https://www.elastic.co/guide/en/elasticsearch/reference/7.4/index.html
我们主要对照上面的官方文档:
我们测试需要的测试数据,可以使用官方为我们提供的test数据:
https://github.com/elastic/elasticsearch/blob/master/docs/src/test/resources/accounts.json
对照Mysql数据库,类比回忆一下 ES 的几个基础概念:
ES | MYSQL |
索引 Index | 数据库 Database |
类型 Type | 表 Table |
文档 Document | 行数据 rowdata |
一、ElasticSearch的2中查询方式
1、ES支持2中基本方式检索:
-
通过使用 Rest Request URI 发送搜索参数(URI + 检索参数);
-
通过 Rest Request Body 来发送它们(URI + 请求体);
首先批量保存数据:
POST /bank/account/_bulk //向索引为bank类型为account批量插入多条document
查看当前索引情况:
GET _cat/indices 结果: yellow open bank s826I5U5T6awpTb123L__w 1 1 1000 0 414.2kb 414.2kb //1000条数据,共占用414.2kb空间 green open .kibana_task_manager_1 NplcfbL4T2qAsrPOp6ktjA 1 0 2 0 32kb 32kb green open kibana_sample_data_ecommerce FtLjxnndRfe5Uq8vLvZLtQ 1 0 4675 0 4.6mb 4.6mb green open .apm-agent-configuration wPQduhx9THydx8WWKYwYnQ 1 0 0 0 283b 283b green open .kibana_1 NPZGhzh-SqyV0vTtJoiF9g 1 0 55 0 933.7kb 933.7kb
2、一切检索从 _search 开始:
URI + 检索参数方式检索:
GET bank/_search?q=*&sort=account_number:asc
URI + 请求体方式检索:QueryDSL方式:查询领域对象语言;——重点使用方式
GET /bank/_search { "query": { "match_all": {} }, "sort": [ { "account_number": "asc" } ] }
两种方式的查询结果相同:
具体的对应关键字的定义:
took – how long it took Elasticsearch to run the query, in milliseconds
timed_out – whether or not the search request timed out
_shards – how many shards were searched and a breakdown of how many shards succeeded, failed, or were skipped.
max_score – the score of the most relevant document found
hits.total.value – how many matching documents were found
hits.sort – the document’s sort position (when not sorting by relevance score)
hits._score – the document’s relevance score (not applicable when using match_all)
3、分页查询下一页的10条数据:
GET /bank/_search { "query": { "match_all": {} }, "sort": [ { "account_number": "asc" } ], "from": 10, "size": 10 }
4、QueryDSL领域对象语言查询的基本语法:
二、ES的常用查询方式和聚合
1、match_all:上面已经使用过了——查询所有;
2、match:
当match的查询字段为非String类型的数据时,为精确查询:
当match的查询字段为String类型的数据时,为模糊匹配——全文检索(得益于 ES的“倒排索引”技术)
全文检索的最终结果,默认会按照评分“_score”进行倒序——也是得益于“倒排索引”技术;——会对检索条件进行“分词匹配”
3、match_phrase【短语匹配】——match中对 字段.keyword 也能实现类似的短语匹配效果
所谓短语匹配:就是将查询的多个单词当做一个短语进行匹配,不要分割;
4、multi_match 【多字段匹配】
5、bool【复合查询】
复合查询:可以帮助我们合并多个查询条件:真正常见的使用场景,多个条件都必须同时满足:
-
must:必须满足
-
must_not:必须不满足
-
should:应该满足,满足最好,不满足也行
should的影响点主要在得分 _score 上:
6、filter【过滤】——也属于 bool 复合查询下的选项
filter过滤:看起来和must、must_not很像,但是区别在于:
-
filter:不贡献相关性 得分;
-
must、must_not:贡献相关性 得分;
7、term【精确查询】
和 match 一样,匹配某个属性的值,与 match 的用法区别:
-
match:String类型,需要全文检索的字段,使用match;
-
term:其它非String类型的字段,精确匹配使用term;
以上作为规范和约定,开发中必须遵从;
使用term去查询String(text)字段时,存在analysis数据分析的问题(进行了分词操作),使用term去检索完整的字段是非常困难的;
8、aggregations【执行聚合】
聚合:提供了从数据中分组和提取数据的能力。
最简单的聚合方法大致等于 SQL GROUP BY 和 SQL 聚合函数功能;
您有执行搜索返回hits(命中结果),并且同时返回聚合结果,把一个响应中的所有hits(命中结果)分隔开的能力;
这是非常强大且有效的,你可以执行查询和多个聚合,并且在一次使用中得到各自的(任何一个的)返回结果,使用一次简洁和简化的API来避免网络往返;
示例:
-
基础:搜索address中包含mill的所有人的年龄分布以及平均年龄,但不显示这些人的详情;
-
复杂:按照年龄聚合,并且请求这些年龄段的这些人的平均薪资;
此功能用于做报表、曲线非常适合
-
复杂:查出所有年龄分布,并且这些年龄段中M的平均薪资和F的平均薪资,以及这个年龄段的总的平均薪资
(用来做对比报表)
ElasticSearch中的聚合分析方式有很多,上面只列举了几个常用的,详细使用文档:
https://www.elastic.co/guide/en/elasticsearch/reference/7.4/search-aggregations.html
三、ES中的映射——mapping
https://www.elastic.co/guide/en/elasticsearch/reference/7.4/mapping.html
1、Mapping映射是用来定义一个文档Document是将如何被处理的;即,属性字段是如何被存储、被索引的;
举例,我们可以使用映射定义:
-
哪个String类型的字段应该被当做全文检索full text字段;
-
哪个属性是包含数字、日期或地理位置坐标的;
-
日期的格式化信息等;
类似于在关系型数据库中,我们要定义每一列的数据类型(Type);——在ES中就是Mapping Type映射类型;
但是必须要注意的是:Mapping Type在ES6.0之后的版本中,就已经被Deprecated了;将会在ES8中彻底被放弃;
2、为什么要移除Type类型的概念?
-
关系型数据库中,两个数据表是独立的(数据表相当于ES中的类型Type),即使他们里面有相同名称的列,也不影响使用;
-
但是ES不一样,ES是基于Lucene开发的搜索引擎,不同的type下名称相同的field最终在Lucene中的处理方式是一样的;
两个不同Type下的两个user_name字段,在ES同一个索引下其实是被认为是同一个field的,你必须在两个不同的type中定义相同的field映射。否则,不同的type中的相同字段名称就会在处理中出现冲突的情况,导致Lucene的处理效率下降。
去掉Type是为了提高ES处理数据的效率的;
ES7.x中:
URL中的type参数为可选。比如:索引一个文档,可以提供文档类型,也可以不提供;
ES8.x中:
不再支持URL中的tyoe参数;
解决:
将索引从多类型迁移到单类型,每种类型的文档创建一个独立索引!!!
四、Mapping的使用
1、映射的查看:
GET bank/_mapping
2、映射的创建:
虽然有了ES的自动类型猜测机制,但是有时候猜测的结果并不是我们所期望的,所以我们就可以手动自己创建映射Mapping:
支持的属性类型:
https://www.elastic.co/guide/en/elasticsearch/reference/7.4/mapping-types.html
创建了一个新的索引my_index,并且指定了映射;
3、向已存在的索引中添加映射:
4、映射的更新:
对于已经存在的映射字段:我们是不能更新的,想更新的话,必须创建新的索引进行数据迁移;
因为映射中可能已经有数据了,这时候更新了映射(相当于mysql数据表中字段的数据类型),那么肯定是会有问题的;
5、创建新索引+数据迁移:
需求: 之前的bank索引下的数据,都有一个类型_type为account:
而在新版本的ES中不建议使用_type类型,所以我们正好进行一次,数据迁移,同时将age的映射从“long”修改为“integer”等:
PUT /newbank { "mappings": { "properties": { "account_number": { "type": "long" }, "address": { "type": "text" }, "age": { "type": "integer" }, "balance": { "type": "long" }, "city": { "type": "keyword" }, "email": { "type": "keyword" }, "employer": { "type": "keyword" }, "firstname": { "type": "text" }, "gender": { "type": "keyword" }, "lastname": { "type": "text", "fields": { "keyword": { "type": "keyword", "ignore_above": 256 } } }, "state": { "type": "keyword" } } } }
执行成功:
执行数据迁移:(将老索引中的数据,迁移到新的索引中)
POST _reindex { "source": { "index": "bank", "type": "account" }, "dest": { "index": "newbank" } }
之后我们再查看新的索引中的数据时候,就会发现,每一条文档的_type属性全部变为了默认的_doc
五、ES分词-安装ik分词
1、分词——是ElasticSearch中实现全文检索的核心
分词——将一段完整的话,拆分为一个一个的单词;
分词操作 在ES中是使用 tokenizer(分词器)来进行的,tokenizer接受一个字节流,将之分割为独立的tokens(词元,通常是独立的单词),然后输出tokens流;
使用默认的分词器whitespace tokenizer可以将文本“Quick brown fox!”分割为 [Quick, brown, fox!] 数组;
该tokenizer(分词器)还负责记录各个 term 词条的顺序或 position 位置(用于 phrase短语和 word proximity 近邻词查询),以及term(词条)所代表的原始 word(单词)的start(起始)和 end(结束)的character offsets(字符偏移量)(用于高亮显示搜索的内容)。
ElasticSearch提供了很多内置的分词器,可以用来构建 custom analyzers(自定义分词器);
2、分词器的使用:
默认的分词器(Standard Analyzer)
https://www.elastic.co/guide/en/elasticsearch/reference/7.4/analysis-standard-analyzer.html
POST _analyze { "analyzer": "standard", "text": "my name is jiguiquan, i'm 25 years old" }
但是这种分词规则其实并不好用:比如,不支持中文分词(ES官方默认带的分词器都不能很好地支持中文分词)
这时候我们就需要安装一些额外的好用的、支持中文分词的分词器“ik分词器”;
3、ik分词器 安装
ik分词器的下载地址:
https://github.com/medcl/elasticsearch-analysis-ik/releases
注意下载的分词器版本必须严格和我们安装的ElasticSearch保持一致:7.4.2
这里我们跳转链接到使用Docker安装ElseticSearch的章节:
http://www.jiguiquan.com/archives/1103
4、ik分词器支持2中分词模式:
-
ik-smart:智能分词
-
ik-max-word:分得尽可能多的词组;
但是,显然,这些还是不能完成“吉桂权”这样的固定名字的词组识别,这时候我们就需要为ik创建自定义的词库,使其能够满足我们得需求,完成分词功能;
六、自定义ES-ik分词器的扩展词库
1、修改 /mydata/elasticsearch/plugins/ik/config/ 目录下的 IKAnalyzer.cfg.xml 配置文件:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd"> <properties> <comment>IK Analyzer 扩展配置</comment> <!–用户可以在这里配置自己的扩展字典 –> <entry key="ext_dict"></entry> <!–用户可以在这里配置自己的扩展停止词字典–> <entry key="ext_stopwords"></entry> <!–用户可以在这里配置远程扩展字典 –> <!– <entry key="remote_ext_dict">words_location</entry> –> <!–用户可以在这里配置远程扩展停止词字典–> <!– <entry key="remote_ext_stopwords">words_location</entry> –> </properties> |
这里,我们为了提供远程词库服务,有2种方式:
-
写一个服务项目,专门用来处理获取服务的请求;
-
使用nginx服务进行暴露(显然这种方式最为简单)
2、使用docker安装nginx容器的方法详见:
http://www.jiguiquan.com/archives/1084
3、在宿主机的 /mydata/nginx/html/ 目录下创建 /es/fenci/txt 文件:
吉桂权 乔碧萝 寒月
测试访问: http://192.168.174.141/es/fenci.txt
4、修改步骤 1 中,将标红配置修改为上面的“分词文件可访问地址”
5、重启 elasticsearch 容器:
docker restart elasticsearch
重新测试分词效果:
显然自定义扩展词库生效了!