ES
快来分享你的内容吧~
- 2025-04-25·Java后端
- 2024-12-24·Java后端查看全文裸辞一周,开始写学习计划 1.每天打卡面试通关训练营 2.跟着皮总完成智能云图库项目 3.系统学习一下VUE 4.补充巩固一下ES和kafka...编程导航_小y:加油,Gro可以看下编程导航的简历优化技巧:https://www.codefather.cn/course/cv,先调整一版简历420分享
Elastic Search 导学
# ES是什么 一个强大的开源**搜索引擎**,可以帮我们从海量数据中快速找到我们想要的内容。 除了搜索外还支持的其他功能: - 高亮显示搜索词。 - 数据分析、可视化。 - 系统监控。 总之,ES很NB就是了。 # 两大索引——倒排索引 和 正排索引 首先这里说的**索引**是**一种键值映射关系**。所谓正排索引 和 倒排索引只是看你如何设计键值对的。 我们用下面的一张普通的MySQL表作为例子: <img src="https://pic.code-nav.cn/post_picture/1644143521826918401/5VdnwgjmcZSzZVGQ.webp" alt="image.png" width="100%" /> > 首先不用我说你应该知道id是这张表的主键。 - **正排索引**的key为id,value为对应行。这体现了 **“由主键找数据”** 的自然逻辑。 - **倒排索引**的key为一个关键词,value为包含这个关键词的id列表。这体现了 **“由内容反查主键”** 的逆向思维。 其中倒排索引就是搜索引擎们能够通过用户输入的关键词快速搜到一大堆相关数据的秘密武器! 为了直观感受倒排索引,这里我们以title字段为例建立一个倒排索引: 首先会对内容进行处理,拆分成一个个关键词(分词),最终得到这样的表。 <img src="https://pic.code-nav.cn/post_picture/1644143521826918401/ZFY0KonULvLKKAdE.webp" alt="image.png" width="100%" /> 基于倒排索引的搜索流程: <img src="https://pic.code-nav.cn/post_picture/1644143521826918401/5hfMN9HRyOn1I7Je.webp" alt="image.png" width="100%" /> # MySQL和ES的关系 ## 核心概念对比 > 这是基于7.17.28这个版本的概念对比。不同的大版本差异有点大。 在ES中有3大核心概念:索引(Index)、映射(Mapping)、文档(Document)。 接下来用一个表格来直观阐述它们在MySQL中的“同义词”。 | Elastic Search | MySQL | | :------------: | :--------: | | 索引 | 表 | | 映射 | 表结构 | | 文档 | 表中的记录 | > 注意⚠: > > 这里的“索引”和前面的“两大索引”里面说的“索引”不是一个东西!**请你不要钻牛角尖!**其实我也不理解为什么ES会搞一个这么容易令人迷惑的概念。 ## 二者的关系 二者是一种**互补**的关系。 **MySQL更擅长事务相关操作**(说人话就是**数据的增删改**),而ES更擅长数据的查询、分析操作。 因此在Java后端开发中,我们一般是**让二者配合使用**的。 用一张图体现二者在Java后端开发的用武之地: <img src="https://pic.code-nav.cn/post_picture/1644143521826918401/562aRmy0fhug6kiE.webp" alt="image.png" width="100%" /> > 注意:一般简单查询(比如根据id查询)还是走MySQL。如果查询比较复杂的话才走ES。 # 安装 ## 1.安装 1Panel 1Panel本身是基于Docker的Linux面板,安装它以后也会**顺带安装Docker + Docker Compose**。 有时候因为网络问题可能会导致Docker无法正常安装。如果你遇到了这种情况,那么你可以参照这个教程下载Docker:【[1Panel三分钟搭建Minio - 编程导航 - 程序员编程学习交流社区](https://www.codefather.cn/post/1912901809601032194)】 我这里就不赘述如何安装1Panel了。 最后和你分享一下我的镜像加速配置: ``` https://docker.m.daocloud.io/ https://docker.1panel.live/ https://dockerproxy.1panel.live/ https://docker.1ms.run/ https://docker.1panel.top/ ``` 反正我用这套镜像加速配置下image嘎嘎快。 ## 2.正式安装 ① 使用git拉取我的一键启动项目。 > Ubuntu好像一开始是自带git的,如果你用的Linux发行版没有git,那你先装一个先。 ``` git clone https://gitee.com/yang-mingzhang/elastic-search-with-ik-and-kibana-starter.git ``` <img src="https://pic.code-nav.cn/post_picture/1644143521826918401/FYrllefz1I35q79c.webp" alt="image.png" width="100%" /> ② 进入这个项目的根目录,然后运行以下命令: 一键启动ES + Kibana服务: ```bash docker-compose up -d ``` 第一次运行会自动下载镜像,因此会比较慢,请耐心等待: <img src="https://pic.code-nav.cn/post_picture/1644143521826918401/Xs8JEbIrkKU3NQw9.webp" alt="image.png" width="100%" /> 成功后: <img src="https://pic.code-nav.cn/post_picture/1644143521826918401/EjLhIWuK0u0sAArx.webp" alt="image.png" width="100%" /> > 一键删除ES + Kibana服务: > > ``` > docker-compose down > ``` > 注意⚠:删除的是容器而不是镜像! 只要镜像还在,之后就真的是一键启动了! <img src="https://pic.code-nav.cn/post_picture/1644143521826918401/1RCqx2ab23VWnW2y.webp" alt="image.png" width="100%" /> ③ 访问Kibana客户端 在浏览器地址栏输入`http://你的虚拟机IP:5601`即可。 > 注意⚠: > > 刚启动ES服务时,如果你立即访问这个网址,可能会出现访问不了的情况。 > > 这是正常的,那是因为Kibana 和 ES服务器还未初始化好。我反正是刷了大概20s左右的样子才刷出来的。 <img src="https://pic.code-nav.cn/post_picture/1644143521826918401/ROsrY2wJ47PcGw00.webp" alt="image.png" width="100%" /> # 认识IK分词器 > 对汉字分词友好。 ## 何为分词 我前面在倒排索引这一部分简单提了一嘴“分词”这个概念。 ES正是对文档内容进行分词,然后建立倒排索引。 之后用户就能通过关键词搜索到相关文档。 ES其实内置了一些分词器,但是对中文支持很差,它是把每一个汉字当作一个词。 ## 测试分词——标准分词器 大战 IK分词器 不信?那我们来对鸽鸽的名言进行分词吧! > 素材: > > **全民制作人们大家好,我是练习时长两年半的个人练习生蔡徐坤。喜欢唱、跳、rap、篮球,music!** 如何测试分词效果? 用Kibana的开发者工具发起如下RESTful请求即可: ```json POST _analyze { "text": "素材内容", "analyzer": "分词器名字" } ``` 我们先用ES默认的分词器【标准分词器,standard】对素材进行分词,结果如下: <img src="https://pic.code-nav.cn/post_picture/1644143521826918401/MEmE3cYjL8k7CNck.webp" alt="image.png" width="100%" /> > 正如你所见,可能老外初次认识中文的时候是一个字一个字的看的doge。 然后看看ik分词器: > ⚠注意: > > ik分词器提供了两种分词模式,它们分别是**智能模式**【ik_smart】和**细粒度模式**【ik_max_word】。这里先以智能模式进行测试,后面再具体介绍二者区别。 <img src="https://pic.code-nav.cn/post_picture/1644143521826918401/F7FVTu5pnryhQUDX.webp" alt="image.png" width="100%" /> > 虽然看起来还是有点怪怪的,但是你至少发现了词语的存在吧。 > > 想要知道为什么会出现这样的现象,我们还得简单了解一下分词的原理。 ## IK分词器两种模式的区别 > 为举例方便,我将以“中华人民共和国”这个词举例。 - 智能模式 - 采用**最粗粒度拆分**,仅输出最可能的词语组合。这个模式下,直接输出“中华人民共和国”这个最大的词,其内部的小词它是不会再继续分的,也就是最粗粒度。 - 细粒度模式 - 将文本进行**最细粒度拆分**,穷尽所有可能的词语组合。这个模式下还可以继续拆分“中华人民共和国”内部的小词,比如:“中华”、“中华人民”、“人民”、“共和国”等等词语。 由此可见: 前者产生的倒排索引占用的空间要小一些,但是会降低搜索到文档的概率;后者则是恰好反过来的。 如何选择呢? **如果是对文档建立倒排索引这个场景,那么用细粒度模式最为合适**,这样文档被搜到的概率也就越大。**如果是用户通过关键词搜索文档这个场景,那么用智能模式最为合适**,可以提高搜索结果的相关性。 ## 分词的原理 标准分词器会把“中国”这个词傻傻地拆分成“中”、“国”这两个词。而IK分词器就比较聪明了,它能正确地将“中国”识别成为一个词。 其实IK分词器**内置了一个汉语词典**(你可以类比为我们小时候用过的《现代汉语词典》),这里面记录了所有可能的词语。 其实一开始IK分词器不知道“中国”是一个词,在它眼里这只是一个普通的由“中”和“国”两个字符组成的字符串而已。然后它会拿着这个字符串去汉语词典里面查,如果查到了,这时IK分词器才会把这个字符串当作一个词语。 怎么样?其实也不是很复杂对吧~ 当然,如果某个字符串要是没被收录到这个词典之中,IK分词器还是会采用和标准分词器一样的策略,即单字分词。例如如“奥力给”这个网络词语,我相信经常在互联网冲浪的大伙肯定不陌生,但是7.17.28这个版本的IK分词器的汉语词典里并没有收录这个词。 <img src="https://pic.code-nav.cn/post_picture/1644143521826918401/ulRoHbOnvJvcAcGQ.webp" alt="image.png" width="100%" /> 现在你能猜到为什么之前用IK分词器来给鸽鸽的名言分词的结果为什么怪怪的了吧?本质是因为里面的好多词并没有收录到IK分词器内置的汉语词典中。 ## 为IK分词器内置的词典添砖加瓦 我们都知道互联网上的信息是爆炸式增长的。当初设计IK分词器内置词典的那批人怎么可能预料到这么多的网络词语的诞生呢? 还好他们给我们留了一手,使得我们可以往这个汉语词典当中添加一些自定义的词。 我们现在移步到IK分词器的配置文件所在目录下: 【PS:我用的WSL】 <img src="https://pic.code-nav.cn/post_picture/1644143521826918401/deDUuseCLmdVkv3v.webp" alt="image.png" width="100%" /> 我们随便点进去一个dic文件看看里面的内容吧。 <img src="https://pic.code-nav.cn/post_picture/1644143521826918401/aT7egNaQmkJ2xVwC.webp" alt="image.png" width="100%" /> > 现在你应该理解分词的本质了吧? 然后再来看看IK分词器的核心配置文件。 <img src="https://pic.code-nav.cn/post_picture/1644143521826918401/USlEPbddomdPEZHS.webp" alt="image.png" width="100%" /> > 其中这个entry标签的内容部分填的就是我们自定义词典文件的路径。 这里需要说明一下**【拓展词典】和【拓展停止字典】**这两个概念: - 拓展词典 - 用于补充内置词典未收录的词(拓展词)。 - 拓展停止字典 - 用于存放需要被过滤掉的词汇(停用词)。 - 它们不会被建立倒排索引,也就是说**不会被搜索到**。 - 这类词汇一般是介词、语气词、敏感词等等。其中常见的介词、语气词等已经被包括了,但是敏感词需要我们自己指定。 为了方便,我就在当前的config目录下新建一个`customer`文件夹用于存放我们自定义的【拓展词典】和【拓展停止字典】。 拓展词典的文件我就命名为`ext_dict.dic`,拓展停止字典的文件我就命名为`ext_stopwords.dic`。 然后在核心配置文件中指明这两个文件的路径: <img src="https://pic.code-nav.cn/post_picture/1644143521826918401/nEDw76wK5QX2JBBU.webp" alt="image.png" width="100%" /> 好啦,那我们随便写几个词测试一下吧。 前面我展示过默认的dic文件里的内容,我们发现**每个词语独占一行**。因此我们自定义的dic文件也要遵循这个规则。 我添加的内容为: 拓展词:“全民制作人”、“两年半” <img src="https://pic.code-nav.cn/post_picture/1644143521826918401/6yogEsvSwlMznhTz.png" alt="image.png" width="100%" /> 停用词:“我”、“是” <img src="https://pic.code-nav.cn/post_picture/1644143521826918401/gVEvrXpbOS0Jajzf.png" alt="image.png" width="100%" /> 最后重启一下ES服务器我们的词库才会生效【拓展点:IK分词器其实支持词典的**热更新**功能,即修改词典后无需重启ES服务器,但是**默认是不开启的**。喜欢折腾的同学可以自行尝试】。 测试结果: <img src="https://pic.code-nav.cn/post_picture/1644143521826918401/3yt5VCu9aT2LB48i.webp" alt="image.png" width="100%" /> > 现在IK分词器认识了“全民制作人”和“两年半”这两个词,并且“我”和“是”这两个词被排除了! # 接下来学什么? 我为什么要写这个总结?只是因为我在找ES教程学习的时候发现很多教程把ES当作一个数据库来教的,然后按照惯例地介绍ES中的CRUD操作。 **可是ES的本质是什么?人家是专门做搜索的啊!!!**所以我感觉介绍ES增删改操作对于我们搞Java后端开发来说似乎**有点多余**。为什么?谁家好人会用不支持事务的ES【不清楚未来会不会支持,反正7.17.28这个版本是没有的】来增删改数据啊? 有的教程开篇就花大量篇幅介绍ES底层原理,如果你是需要面试,学这些内容是理所应当。但是对于大多数同学而言,他们只是想快速知道**如何用ES解决业务问题**。因此对于他们而言,**在连ES能在业务中做什么都还不清楚的前提下过早接触底层原理,这是一种不科学的学习方式!** 这就好比你需要会造车才会开车么? 我认为在你能够熟练应用ES解决常见的业务问题后,你才有动力和兴趣去学习ES的底层原理。 说了这么一大堆,那么对于我们普通Java后端开发者而言我们应该重点学习什么呢? - ES的查询【**超重点!**】 - 使用什么工具实现MySQL和ES的数据实时同步 - 分布式、高可用这些就见仁见智吧,因为我连单体项目都还没玩明白,因此我不会过早接触这些内容。 不只是学ES,你学习其他技术时,就应该思考这些问题!先会用这些技术解决实际业务问题,如果你兴趣来了再去学技术的底层原理也不迟。当然这不适用于着急面试找工作的同学,因为我们无法改变 **“面试造火箭,工作打螺丝”** 这个大环境!
修改 Cannal,解决数据同步过程时间转换异常问题 & 解决 ES 深分页问题
## 概述 这几天复习项目和八股文,然后整理了一下项目的问题,主要是修改 Cannal 源码的,感兴趣的小伙伴可以看看。 ## 需求背景 中级件版本: - MySQL:8.X - Canal:1.1.7 - ES:8.13.X 在项目中,我们通过监听 MySQL BinLog 日志然后通过 Canal 将数据同步到 ElasticSearch 中,项目的流程图如下: <img src="https://pic.code-nav.cn/post_picture/1627889630378479618/OJpmRRnMtYHz8stG.webp" alt="image.png" width="100%" /> 然后如果用户进行搜索时,系统交互如下: <img src="https://pic.code-nav.cn/post_picture/1627889630378479618/IAoFoB3kY5j21TEH.png" alt="image.png" width="100%" /> 可以发现 Cannal 在我们这个项目中主要起到的是一个同步数据的作用。 ## 出现问题 在配置好数据同步之后,通过 spring-data-elasticsearch 查询的时候,会出现时间类型无法转换的问题,也就是其中传输商品数据的 saleTime,会出现字段映射失败的情况,报错内容如下: ``` java.lang.RuntimeException: org.springframework.data.elasticsearch.core.convert.ConversionException: Unable to convert value '2024-05-13T16:04:16+08:00' to java.util.Date for property 'saleTime' org.springframework.data.elasticsearch.core.convert.ConversionException: Unable to convert value '2024-05-13T16:04:16+08:00' to java.util.Date for property 'saleTime' at org.springframework.data.elasticsearch.core.convert.DatePropertyValueConverter.read(DatePropertyValueConverter.java:56) at org.springframework.data.elasticsearch.core.convert.MappingElasticsearchConverter$Reader.convertOnRead(MappingElasticsearchConverter.java:524) at org.springframework.data.elasticsearch.core.convert.MappingElasticsearchConverter$Reader.propertyConverterRead(MappingElasticsearchConverter.java:518) ``` 也就是说,日期类型在 ES 中存储的数据格式是 '2025-02-12T23:04:16+08:00'这种格式,而在代码中是无法转成我们想要的 Date 类型的。 这里首选尝试的方法是设置 saleTime 的 pattern,如: ``` @Field(name = "sale_time",type = FieldType.Date, format = {},pattern = "yyyy-MM-dd'T'HH:mm:ss'+08:00' ||yyyy-MM-dd||strict_date_optional_time||epoch_millis") private Date saleTime; ``` 但是我们会发现问题还是没有解决,因为从 ES 到代码这部分没有方法处理,那就只能让 ES 存储的内容去改变一下,让 ES 可以存储 '2025-02-12 23:04:16' 而不是'2025-02-12T23:04:16+08:00',就可以完美解决问题了。 这里我们尝试修改一下 ES 的索引配置,发现还是不行。 ``` // PUT test_collection { "mappings": { "properties": { "sale_time": { "type": "date", "format": "yyyy-MM-dd HH:mm:ss || yyyy-MM-dd'T'HH:mm:ss'+08:00 || strict_date_optional_time || epoch_millis" } } } } ' ``` ## 问题解决 如果以上问题都没有办法解决,那就只能从 Cannal 上看看为什么 ES 会存储 '2025-02-12T23:04:16+08:00' 这种格式的数据了,然后通过查看 ES 源码,我们发现在 ESSyncUtil中,针对日期类型,他会使用如下方式进行格式化: ``` DateTime dateTime = new DateTime(((java.sql.Timestamp) val).getTime()); if (dateTime.getMillisOfSecond() != 0) { res = dateTime.toString("yyyy-MM-dd'T'HH:mm:ss.SSS" + Util.timeZone); } else { res = dateTime.toString("yyyy-MM-dd'T'HH:mm:ss" + Util.timeZone); } ``` 这也是为什么会出现 '2025-02-12T23:04:16+08:00' 这种格式数据的原因,因为我们的项目并不涉及到多时区,所以就直接修改了一下 Cannal 的源码。 找到ESSyncUtil中的8处日期转换部分内容,做如下修改: <img src="https://pic.code-nav.cn/post_picture/1627889630378479618/AKP7vfruUO8gE883.webp" alt="image.png" width="100%" /> 修改后重新打包,编译成 jar包。因为我们的ES 是8.0的版本,所以最终到`workspace/canal-master/client-adapter/es8x/target`下找到`client-adapter.es8x-1.1.8-SNAPSHOT-jar-with-dependencies.jar`这个编译后的包。 然后上传到 Cannal plugin 的安装目录下面:/root/package/canal-adapter/plugin,然后这样问题重启 Cannal,问题就解决了。 <img src="https://pic.code-nav.cn/post_picture/1627889630378479618/Z0cmeQsM0sIvzDuF.webp" alt="image.png" width="100%" /> ## 补充:基于 ElasticSearch 的search_after解决深分页问题 在 ElasticSearch 中进行分页查询,一般使用到两个参数,即 from 和 size,当我们对 ES 发起一个带有分页参数的查询,如使用from和size参数,ES 需要遍历所有匹配的文档,直到到达指定的起始点 from,然后返回这一点开始的 size 个文档。 ``` GET /your_index/_search{ "from": 50, "size": 10, "query": { "match_all": {} } } ``` - from:表示前面跳过了 50 条记录 - size:表示返回 10 条记录 from + size 的总数不能超过Elasticsearch索引的index.max_result_window设置,默认为10000。这意味着如果你设置from为9900,size为100,查询将会成功。但如果from为9900,size为101,则会失败。 ES 的检索机制就决定了,如果我们需要进行分页查询的时候,ES 要先找到所有位于当前页之前的记录。比如要查询 1000 页的数据,并且每页显示 10 条记录,系统需要先找到前面 9990条记录,然后才能获取到你请求的那10条记录。这意味着,随着页码的增加,数据库需要处理的数据量急剧增加,导致查询效率降低,这就是 **ES 的深分页问题**。 深度分页需要数据库在内存中维护大量的数据,并对这些数据进行排序和处理,这会消耗大量的CPU和内存资源。随着分页深度的增加,查询响应时间会显著增加。在某些情况下,这可能导致查询超时或者系统负载过重。 然后在一般情况下,我们可以通过 scroll 或者 search_after 来解决 ES 深分页问题,区别如下: <img src="https://pic.code-nav.cn/post_picture/1627889630378479618/c9mwG2T780wULrV7.webp" alt="image.png" width="100%" /> 然后在项目中,由于不存在随机页访问的场景,所以我们主要选择性能更加好的 search_after 方案,这里主要是因为我们移动端页面 APP 是通过不断滚屏实现分页,这样只能顺序翻页,就可以完美避开 search_after 不支持随机页访问的问题,实现代码如下: ``` public SAPageInfo<Collection> deepPageQueryByState(String name, String state, int pageSize, Long lastId) { LambdaEsQueryWrapper<Collection> queryWrapper = new LambdaEsQueryWrapper<>(); queryWrapper.match(Collection::getName, name) .and(wrapper -> wrapper .match(Collection::getState, state) .match(Collection::getDeleted, "0") ) .orderByAsc("collection_id"); SAPageInfo<Collection> saPageInfo; if (lastId == null) { saPageInfo = collectionEsMapper.searchAfterPage(queryWrapper, null, pageSize); } else { saPageInfo = collectionEsMapper.searchAfterPage(queryWrapper, ImmutableList.of(lastId), 10); } return saPageInfo; } ```
裸辞一周,开始写学习计划 1.每天打卡面试通关训练营 2.跟着皮总完成智能云图库项目 3.系统学习一下VUE 4.补充巩固一下ES和kafka 大佬们有没有VUE、ES或者kafka的推荐教程?帮帮牢弟🙃 DAY 1
Docker Compose 部署 ElasticSearch
ElasticSearch 是一个分布式、RESTful 风格的搜索和数据分析引擎,可以解决海量数据的实时分析和搜索问题。 ## 服务部署 服务使用 `docker compose` 来管理,这里提供了两种部署方式,一种是单节点部署,一种是多节点集群部署。 ### 环境变量 在 `.env` 文件中,`STACK_VERSION` 为当前 Elastic Stack 的版本。 ```env # .env # Password for the 'elastic' user (at least 6 characters) ELASTIC_PASSWORD=password # Password for the 'kibana_system' user (at least 6 characters) KIBANA_PASSWORD=password # Version of Elastic products STACK_VERSION=8.13.2 # Set the cluster name CLUSTER_NAME=docker-cluster # Port to expose Elasticsearch HTTP API to the host ES_PORT=9200 #ES_PORT=127.0.0.1:9200 # Port to expose Kibana to the host KIBANA_PORT=5601 #KIBANA_PORT=80 # Increase or decrease based on the available host memory (in bytes) MEM_LIMIT=1073741824 # Project namespace (defaults to the current folder name if not set) #COMPOSE_PROJECT_NAME=myproject ``` ### 服务启动 启动服务 `docker compose up -d`,打开 `http://localhost:5601`,使用 `elastic` 用户和环境变量配置的 `ELASTIC_PASSWORD` 登录 kibana。 停止服务 `docker compose down` 删除服务 `docker compose down -v` ### 单节点部署 elasticsearch 单节点 + kibana 中文界面,开箱即用。 单节点 docker compose 自动安装,关闭 tls 安全通信,不生成 SSL 证书,elasticsearch 自动注册到 kibana 上的步骤。 ::: tip elasticsearch 7.X 的版本使用这份单节点部署文件也能做到开箱即用,只需要修改 `.env` 的 STACK_VERSION 为 7.x 的版本就行。 ::: ```yml # docker-compose.yml services: setup: image: elasticsearch:${STACK_VERSION} user: "0" command: > bash -c ' if [ x${ELASTIC_PASSWORD} == x ]; then echo "Set the ELASTIC_PASSWORD environment variable in the .env file"; exit 1; elif [ x${KIBANA_PASSWORD} == x ]; then echo "Set the KIBANA_PASSWORD environment variable in the .env file"; exit 1; fi; echo "Waiting for Elasticsearch availability"; until curl -s http://es01:9200 | grep -q "missing authentication credentials"; do sleep 30; done; echo "Setting kibana_system password"; until curl -s -X POST -u "elastic:${ELASTIC_PASSWORD}" -H "Content-Type: application/json" http://es01:9200/_security/user/kibana_system/_password -d "{\"password\":\"${KIBANA_PASSWORD}\"}" | grep -q "^{}"; do sleep 10; done; echo "All done!"; ' es01: depends_on: - setup image: elasticsearch:${STACK_VERSION} volumes: - esdata01:/usr/share/elasticsearch/data ports: - ${ES_PORT}:9200 environment: - discovery.type=single-node - ELASTIC_PASSWORD=${ELASTIC_PASSWORD} - bootstrap.memory_lock=true - xpack.security.enabled=true - xpack.security.http.ssl.enabled=false - xpack.security.transport.ssl.enabled=false mem_limit: ${MEM_LIMIT} ulimits: memlock: soft: -1 hard: -1 healthcheck: test: ["CMD-SHELL", "curl -s http://localhost:9200 | grep -q 'missing authentication credentials'"] interval: 10s timeout: 10s retries: 120 kibana: depends_on: es01: condition: service_healthy image: kibana:${STACK_VERSION} volumes: - kibanadata:/usr/share/kibana/data ports: - ${KIBANA_PORT}:5601 environment: - SERVERNAME=kibana - ELASTICSEARCH_HOSTS=http://es01:9200 - ELASTICSEARCH_USERNAME=kibana_system - ELASTICSEARCH_PASSWORD=${KIBANA_PASSWORD} - I18N_LOCALE=zh-CN mem_limit: ${MEM_LIMIT} healthcheck: test: ["CMD-SHELL", "curl -s -I http://localhost:5601 | grep -q 'HTTP/1.1 302 Found'"] interval: 10s timeout: 10s retries: 120 volumes: esdata01: driver: local kibanadata: driver: local ``` ### 多节点集群 elasticsearch 三节点 + kibana 中文界面,开箱即用。 多节点 docker compose 自动安装,开启 tls 安全通信,自动生成 SSL 证书、elasticsearch 自动注册到 kibana。 ```yml # docker-compose.yml services: setup: image: docker.elastic.co/elasticsearch/elasticsearch:${STACK_VERSION} volumes: - certs:/usr/share/elasticsearch/config/certs user: "0" command: > bash -c ' if [ x${ELASTIC_PASSWORD} == x ]; then echo "Set the ELASTIC_PASSWORD environment variable in the .env file"; exit 1; elif [ x${KIBANA_PASSWORD} == x ]; then echo "Set the KIBANA_PASSWORD environment variable in the .env file"; exit 1; fi; if [ ! -f config/certs/ca.zip ]; then echo "Creating CA"; bin/elasticsearch-certutil ca --silent --pem -out config/certs/ca.zip; unzip config/certs/ca.zip -d config/certs; fi; if [ ! -f config/certs/certs.zip ]; then echo "Creating certs"; echo -ne \ "instances:\n"\ " - name: es01\n"\ " dns:\n"\ " - es01\n"\ " - localhost\n"\ " ip:\n"\ " - 127.0.0.1\n"\ " - name: es02\n"\ " dns:\n"\ " - es02\n"\ " - localhost\n"\ " ip:\n"\ " - 127.0.0.1\n"\ " - name: es03\n"\ " dns:\n"\ " - es03\n"\ " - localhost\n"\ " ip:\n"\ " - 127.0.0.1\n"\ > config/certs/instances.yml; bin/elasticsearch-certutil cert --silent --pem -out config/certs/certs.zip --in config/certs/instances.yml --ca-cert config/certs/ca/ca.crt --ca-key config/certs/ca/ca.key; unzip config/certs/certs.zip -d config/certs; fi; echo "Setting file permissions" chown -R root:root config/certs; find . -type d -exec chmod 750 \{\} \;; find . -type f -exec chmod 640 \{\} \;; echo "Waiting for Elasticsearch availability"; until curl -s --cacert config/certs/ca/ca.crt https://es01:9200 | grep -q "missing authentication credentials"; do sleep 30; done; echo "Setting kibana_system password"; until curl -s -X POST --cacert config/certs/ca/ca.crt -u "elastic:${ELASTIC_PASSWORD}" -H "Content-Type: application/json" https://es01:9200/_security/user/kibana_system/_password -d "{\"password\":\"${KIBANA_PASSWORD}\"}" | grep -q "^{}"; do sleep 10; done; echo "All done!"; ' healthcheck: test: ["CMD-SHELL", "[ -f config/certs/es01/es01.crt ]"] interval: 1s timeout: 5s retries: 120 es01: depends_on: setup: condition: service_healthy image: docker.elastic.co/elasticsearch/elasticsearch:${STACK_VERSION} volumes: - certs:/usr/share/elasticsearch/config/certs - esdata01:/usr/share/elasticsearch/data ports: - ${ES_PORT}:9200 environment: - node.name=es01 - cluster.name=${CLUSTER_NAME} - cluster.initial_master_nodes=es01,es02,es03 - discovery.seed_hosts=es02,es03 - ELASTIC_PASSWORD=${ELASTIC_PASSWORD} - bootstrap.memory_lock=true - xpack.security.enabled=true - xpack.security.http.ssl.enabled=true - xpack.security.http.ssl.key=certs/es01/es01.key - xpack.security.http.ssl.certificate=certs/es01/es01.crt - xpack.security.http.ssl.certificate_authorities=certs/ca/ca.crt - xpack.security.transport.ssl.enabled=true - xpack.security.transport.ssl.key=certs/es01/es01.key - xpack.security.transport.ssl.certificate=certs/es01/es01.crt - xpack.security.transport.ssl.certificate_authorities=certs/ca/ca.crt - xpack.security.transport.ssl.verification_mode=certificate - xpack.license.self_generated.type=${LICENSE} mem_limit: ${MEM_LIMIT} ulimits: memlock: soft: -1 hard: -1 healthcheck: test: [ "CMD-SHELL", "curl -s --cacert config/certs/ca/ca.crt https://localhost:9200 | grep -q 'missing authentication credentials'", ] interval: 10s timeout: 10s retries: 120 es02: depends_on: - es01 image: docker.elastic.co/elasticsearch/elasticsearch:${STACK_VERSION} volumes: - certs:/usr/share/elasticsearch/config/certs - esdata02:/usr/share/elasticsearch/data environment: - node.name=es02 - cluster.name=${CLUSTER_NAME} - cluster.initial_master_nodes=es01,es02,es03 - discovery.seed_hosts=es01,es03 - bootstrap.memory_lock=true - xpack.security.enabled=true - xpack.security.http.ssl.enabled=true - xpack.security.http.ssl.key=certs/es02/es02.key - xpack.security.http.ssl.certificate=certs/es02/es02.crt - xpack.security.http.ssl.certificate_authorities=certs/ca/ca.crt - xpack.security.transport.ssl.enabled=true - xpack.security.transport.ssl.key=certs/es02/es02.key - xpack.security.transport.ssl.certificate=certs/es02/es02.crt - xpack.security.transport.ssl.certificate_authorities=certs/ca/ca.crt - xpack.security.transport.ssl.verification_mode=certificate - xpack.license.self_generated.type=${LICENSE} mem_limit: ${MEM_LIMIT} ulimits: memlock: soft: -1 hard: -1 healthcheck: test: [ "CMD-SHELL", "curl -s --cacert config/certs/ca/ca.crt https://localhost:9200 | grep -q 'missing authentication credentials'", ] interval: 10s timeout: 10s retries: 120 es03: depends_on: - es02 image: docker.elastic.co/elasticsearch/elasticsearch:${STACK_VERSION} volumes: - certs:/usr/share/elasticsearch/config/certs - esdata03:/usr/share/elasticsearch/data environment: - node.name=es03 - cluster.name=${CLUSTER_NAME} - cluster.initial_master_nodes=es01,es02,es03 - discovery.seed_hosts=es01,es02 - bootstrap.memory_lock=true - xpack.security.enabled=true - xpack.security.http.ssl.enabled=true - xpack.security.http.ssl.key=certs/es03/es03.key - xpack.security.http.ssl.certificate=certs/es03/es03.crt - xpack.security.http.ssl.certificate_authorities=certs/ca/ca.crt - xpack.security.transport.ssl.enabled=true - xpack.security.transport.ssl.key=certs/es03/es03.key - xpack.security.transport.ssl.certificate=certs/es03/es03.crt - xpack.security.transport.ssl.certificate_authorities=certs/ca/ca.crt - xpack.security.transport.ssl.verification_mode=certificate - xpack.license.self_generated.type=${LICENSE} mem_limit: ${MEM_LIMIT} ulimits: memlock: soft: -1 hard: -1 healthcheck: test: [ "CMD-SHELL", "curl -s --cacert config/certs/ca/ca.crt https://localhost:9200 | grep -q 'missing authentication credentials'", ] interval: 10s timeout: 10s retries: 120 kibana: depends_on: es01: condition: service_healthy es02: condition: service_healthy es03: condition: service_healthy image: docker.elastic.co/kibana/kibana:${STACK_VERSION} volumes: - certs:/usr/share/kibana/config/certs - kibanadata:/usr/share/kibana/data ports: - ${KIBANA_PORT}:5601 environment: - SERVERNAME=kibana - ELASTICSEARCH_HOSTS=https://es01:9200 - ELASTICSEARCH_USERNAME=kibana_system - ELASTICSEARCH_PASSWORD=${KIBANA_PASSWORD} - ELASTICSEARCH_SSL_CERTIFICATEAUTHORITIES=config/certs/ca/ca.crt - I18N_LOCALE=zh-CN mem_limit: ${MEM_LIMIT} healthcheck: test: ["CMD-SHELL", "curl -s -I http://localhost:5601 | grep -q 'HTTP/1.1 302 Found'"] interval: 10s timeout: 10s retries: 120 volumes: certs: driver: local esdata01: driver: local esdata02: driver: local esdata03: driver: local kibanadata: driver: local ``` ### 安装 analysis-ik 中文分词器 ```bash docker compose exec es01 bin/elasticsearch-plugin install -b https://get.infini.cloud/elasticsearch/analysis-ik/8.13.2 docker compose exec es02 bin/elasticsearch-plugin install -b https://get.infini.cloud/elasticsearch/analysis-ik/8.13.2 docker compose exec es03 bin/elasticsearch-plugin install -b https://get.infini.cloud/elasticsearch/analysis-ik/8.13.2 ``` 安装成功后需要重启 elasticsearch 服务。 ## 参考 - <https://www.elastic.co/guide/en/elasticsearch/reference/current/docker.html>
ElasticSearch 的基础概念与入门使用
<html> <head></head> <body> <div class="content ql-editor"> <h1>ElasticSearch 的基础概念与入门使用</h1> <p><br></p> <p>如果想要更好的阅读体验请移步 [CSDN](<a href="https://blog.csdn.net/Go_ahead_forever/article/details/135162366" target="_blank">ElasticSearch 的基础概念与入门使用-CSDN博客</a>)</p> <h2>前言</h2> <p><br></p> <p>elasticsearch 是一款非常强大的开源搜索引擎,具备非常多强大的功能,可以帮助我们从海量的数据中快速找到需要的内容。</p> <p><br></p> <p>例如:</p> <p><br></p> <ol> <li data-list="bullet"><span class="ql-ui"></span>在 Github 中搜索代码</li> </ol> <p><br></p> <ol> <li data-list="bullet"><span class="ql-ui"></span>在电商网站搜索商品</li> </ol> <p><br></p> <ol> <li data-list="bullet"><span class="ql-ui"></span>在 Google 搜索答案</li> </ol> <p><br></p> <ol> <li data-list="bullet"><span class="ql-ui"></span>……</li> </ol> <p><br></p> <p><br></p> <p><br></p> <p>elasticsearch结合kibana、Logstash、Beats,也就是elastic stack(ELK)。被广泛应用在日志数据分析、实时监控等领域,而elasticsearch是elastic stack的核心,负责存储、搜索、分析数据。</p> <p><br></p> <p>elasticsearch底层是基于<strong>lucene</strong>来实现的。</p> <p><br></p> <p><strong>Lucene</strong>是一个Java语言的搜索引擎类库,是Apache公司的顶级项目,由DougCutting于1999年研发。官网地址:https://lucene.apache.org/ 。</p> <p><br></p> <p><br></p> <p><br></p> <h2>基础概念</h2> <p><br></p> <h3>文档</h3> <p><br></p> <p>elasticsearch 是面向 <strong>文档</strong> 存储的,可以是数据库中的一条商品数据,一个订单信息。文档数据会被序列化成 json 格式后存储在 elasticsearch 中,而 json 文档中往往包含很多的字段(Field),类似于数据库中的列。</p> <p><br></p> <p><br></p> <p><br></p> <h3>分词器</h3> <p><br></p> <p>作用:</p> <p><br></p> <ol> <li data-list="bullet"><span class="ql-ui"></span>创建倒排索引时对文档分词</li> <li data-list="bullet"><span class="ql-ui"></span>用户搜索时,对输入的内容分词</li> </ol> <p><br></p> <p><br></p> <p><br></p> <h3>索引与映射</h3> <p><br></p> <p><strong>索引(Index)</strong> ,就是相同类型的文档的集合。</p> <p><br></p> <p>例如:</p> <p><br></p> <ol> <li data-list="bullet"><span class="ql-ui"></span>所有用户文档,就可以组织在一起,称为用户的索引;</li> <li data-list="bullet"><span class="ql-ui"></span>所用商品的文档,可以组织在一起,称为商品的索引;</li> <li data-list="bullet"><span class="ql-ui"></span>所有订单的文档,可以组织在一起,称为订单的索引;</li> </ol> <p><br></p> <div class="ql-code-block-container"> <div class="ql-code-block"> son </div> <div class="ql-code-block"> # 商品索引 </div> <div class="ql-code-block"> { </div> <div class="ql-code-block"> "id": 1, </div> <div class="ql-code-block"> "name": "电视机", </div> <div class="ql-code-block"> "category": "家电", </div> <div class="ql-code-block"> "price": 599.99, </div> <div class="ql-code-block"> "stock": 50 </div> <div class="ql-code-block"> }, </div> <div class="ql-code-block"> { </div> <div class="ql-code-block"> "id": 2, </div> <div class="ql-code-block"> "name": "手机", </div> <div class="ql-code-block"> "category": "电子设备", </div> <div class="ql-code-block"> "price": 899.99, </div> <div class="ql-code-block"> "stock": 100 </div> <div class="ql-code-block"> }, </div> <div class="ql-code-block"> { </div> <div class="ql-code-block"> "id": 3, </div> <div class="ql-code-block"> "name": "运动鞋", </div> <div class="ql-code-block"> "category": "服装", </div> <div class="ql-code-block"> "price": 79.99, </div> <div class="ql-code-block"> "stock": 200 </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> # 用户索引 </div> <div class="ql-code-block"> { </div> <div class="ql-code-block"> "id": 1, </div> <div class="ql-code-block"> "name": "电视机", </div> <div class="ql-code-block"> "category": "家电", </div> <div class="ql-code-block"> "price": 599.99, </div> <div class="ql-code-block"> "stock": 50 </div> <div class="ql-code-block"> }, </div> <div class="ql-code-block"> { </div> <div class="ql-code-block"> "id": 2, </div> <div class="ql-code-block"> "name": "手机", </div> <div class="ql-code-block"> "category": "电子设备", </div> <div class="ql-code-block"> "price": 899.99, </div> <div class="ql-code-block"> "stock": 100 </div> <div class="ql-code-block"> }, </div> <div class="ql-code-block"> { </div> <div class="ql-code-block"> "id": 3, </div> <div class="ql-code-block"> "name": "运动鞋", </div> <div class="ql-code-block"> "category": "服装", </div> <div class="ql-code-block"> "price": 79.99, </div> <div class="ql-code-block"> "stock": 200 </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> # 订单索引 </div> <div class="ql-code-block"> { </div> <div class="ql-code-block"> "order_id": 1, </div> <div class="ql-code-block"> "user_id": 1, </div> <div class="ql-code-block"> "products": [ </div> <div class="ql-code-block"> { </div> <div class="ql-code-block"> "product_id": 1, </div> <div class="ql-code-block"> "quantity": 2 </div> <div class="ql-code-block"> }, </div> <div class="ql-code-block"> { </div> <div class="ql-code-block"> "product_id": 2, </div> <div class="ql-code-block"> "quantity": 1 </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> ], </div> <div class="ql-code-block"> "total_price": 2099.97, </div> <div class="ql-code-block"> "order_date": "2023-12-10", </div> <div class="ql-code-block"> "shipping_address": "上海市" </div> <div class="ql-code-block"> }, </div> <div class="ql-code-block"> { </div> <div class="ql-code-block"> "order_id": 2, </div> <div class="ql-code-block"> "user_id": 3, </div> <div class="ql-code-block"> "products": [ </div> <div class="ql-code-block"> { </div> <div class="ql-code-block"> "product_id": 3, </div> <div class="ql-code-block"> "quantity": 3 </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> ], </div> <div class="ql-code-block"> "total_price": 239.97, </div> <div class="ql-code-block"> "order_date": "2023-12-15", </div> <div class="ql-code-block"> "shipping_address": "广州市" </div> <div class="ql-code-block"> }, </div> </div> <p><br></p> <p><br></p> <p>因此,我们可以把索引当作是数据库中的表。</p> <p><br></p> <p><br></p> <p><br></p> <p>数据库的表会有约束信息,用来定义表的结构、字段的名称、类型等信息。因此,索引库中就有<strong>映射(mapping)</strong>,是索引中文档的字段约束信息,类似表的结构约束。</p> <p><br></p> <p><br></p> <p><br></p> <h3>mysql 与 elasticsearch 一些概念的对比</h3> <p><br></p> <p>| <strong>MySQL</strong> | <strong>Elasticsearch</strong> | <strong>说明</strong> |</p> <p>| --------- | ----------------- | ------------------------------------------------------------ |</p> <p>| Table | Index | 索引(index),就是文档的集合,类似数据库的表(table) |</p> <p>| Row | Document | 文档(Document),就是一条条的数据,类似数据库中的行(Row),文档都是JSON格式 |</p> <p>| Column | Field | 字段(Field),就是JSON文档中的字段,类似数据库中的列(Column) |</p> <p>| Schema | Mapping | Mapping(映射)是索引中文档的约束,例如字段类型约束。类似数据库的表结构(Schema) |</p> <p>| SQL | DSL | DSL是elasticsearch提供的JSON风格的请求语句,用来操作elasticsearch,实现CRUD |</p> <p><br></p> <p>是不是说,我们学习了elasticsearch就不再需要mysql了呢?</p> <p><br></p> <p>并不是如此,两者各自有自己的擅长支出:</p> <p><br></p> <ol> <li data-list="bullet"><span class="ql-ui"></span>Mysql:擅长事务类型操作,可以确保数据的安全和一致性</li> </ol> <p><br></p> <ol> <li data-list="bullet"><span class="ql-ui"></span>Elasticsearch:擅长海量数据的搜索、分析、计算</li> </ol> <p><br></p> <p><br></p> <p><br></p> <p>因此在企业中,往往是两者结合使用:</p> <p><br></p> <ol> <li data-list="bullet"><span class="ql-ui"></span>对安全性要求较高的写操作,使用mysql实现</li> <li data-list="bullet"><span class="ql-ui"></span>对查询性能要求较高的搜索需求,使用elasticsearch实现</li> <li data-list="bullet"><span class="ql-ui"></span>两者再基于某种方式,实现数据的同步,保证一致性</li> </ol> <p><br></p> <p>!<a href="https://img-blog.csdnimg.cn/direct/c047fa70f9e743b295dcc5e78acf4420.png" target="_blank">在这里插入图片描述</a></p> <p><br></p> <p><br></p> <h2>安装</h2> <p><br></p> <p>关于安装的话,这里提供 Windows 版本,一个是elasticsearch、一个是 kibana (一个工具)。</p> <p><br></p> <p><a href="https://blog.csdn.net/Go_ahead_forever/article/details/133720298" target="_blank">安装参考</a></p> <p><br></p> <h3>安装中文分词器</h3> <p><br></p> <p>elasticsearch 对中文的分词不是很友好,一般的分词不管你选择的是标准分词器,还是中文分词器,结果都是只能分成一个一个的字,非常不友好。所以我们选择一个开源的分词器。<a href="http://%5Bmedcl/elasticsearch-analysis-ik:%20The%20IK%20Analysis%20plugin%20integrates%20Lucene%20IK%20analyzer%20into%20elasticsearch,%20support%20customized%20dictionary.%20(github.com" target="_blank">下载地址</a>](https://github.com/medcl/elasticsearch-analysis-ik)) 你只需要把内容下载好,把这个目录解压放到安装 elasticsearch 的文件夹中的 plugins 目录中。例如:</p> <p>!<a href="https://img-blog.csdnimg.cn/direct/8287f61465f84c5288b9566d3e3f7af1.png" target="_blank">在这里插入图片描述</a></p> <p><br></p> <p><br></p> <p><br></p> <p>放好以后,直接重启 elasticsearch 就好。</p> <p><br></p> <p>如果出现报错说分词器版本不兼容 elasticsearch 只要进入下载好的分词器目录下面的 <code>plugin-descriptor.properties</code> 文件中八版本改成和你的 elasticsearch 版本一致就好了。</p> <p><br></p> <p>!<a href="https://img-blog.csdnimg.cn/direct/fa80ab4b87f441f7b43623fe4f41cd92.png" target="_blank">在这里插入图片描述</a></p> <p><br></p> <p><br></p> <h2>使用</h2> <p><br></p> <p>了解了上面的一些信息以后,我们接下来就开始使用 elasticsearch 吧。</p> <p><br></p> <h3>索引的增删改查</h3> <p><br></p> <p>索引库就类似数据库表,mapping映射就类似表的结构。</p> <p><br></p> <p>我们要向es中存储数据,必须先创建“库”和“表”。</p> <p><br></p> <h4>mapping 映射属性</h4> <p><br></p> <p>mapping是对索引库中文档的约束,常见的mapping属性包括:</p> <p><br></p> <ol> <li data-list="bullet"><span class="ql-ui"></span>type:字段数据类型,常见的简单类型有:</li> <li data-list="bullet" class="ql-indent-4"><span class="ql-ui"></span>字符串:text(可分词的文本)、keyword(精确值,例如:品牌、国家、ip地址)</li> <li data-list="bullet" class="ql-indent-4"><span class="ql-ui"></span>数值:long、integer、short、byte、double、float、</li> <li data-list="bullet" class="ql-indent-4"><span class="ql-ui"></span>布尔:boolean</li> <li data-list="bullet" class="ql-indent-4"><span class="ql-ui"></span>日期:date</li> <li data-list="bullet" class="ql-indent-4"><span class="ql-ui"></span>对象:object</li> <li data-list="bullet"><span class="ql-ui"></span>index:是否创建索引,默认为true</li> <li data-list="bullet"><span class="ql-ui"></span>analyzer:使用哪种分词器</li> <li data-list="bullet"><span class="ql-ui"></span>properties:该字段的子字段</li> </ol> <p><br></p> <p><br></p> <p><br></p> <p>例如:</p> <p><br></p> <div class="ql-code-block-container"> <div class="ql-code-block"> son </div> <div class="ql-code-block"> { </div> <div class="ql-code-block"> "age": 21, </div> <div class="ql-code-block"> "weight": 52.1, </div> <div class="ql-code-block"> "isMarried": false, </div> <div class="ql-code-block"> "info": "created by xwhking", </div> <div class="ql-code-block"> "email": "2837468248@qq.com", </div> <div class="ql-code-block"> "score": [99.1, 99.5, 98.9], </div> <div class="ql-code-block"> "name": { </div> <div class="ql-code-block"> "firstName": "XWH", </div> <div class="ql-code-block"> "lastName": "Z" </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> </div> <p><br></p> <p><br></p> <p>对应的每个字段映射(mapping):</p> <p><br></p> <ol> <li data-list="bullet"><span class="ql-ui"></span>age:类型为 integer;参与搜索,因此需要index为true;无需分词器</li> <li data-list="bullet"><span class="ql-ui"></span>weight:类型为float;参与搜索,因此需要index为true;无需分词器</li> <li data-list="bullet"><span class="ql-ui"></span>isMarried:类型为boolean;参与搜索,因此需要index为true;无需分词器</li> <li data-list="bullet"><span class="ql-ui"></span>info:类型为字符串,需要分词,因此是text;参与搜索,因此需要index为true;分词器可以用ik_smart</li> <li data-list="bullet"><span class="ql-ui"></span>email:类型为字符串,但是不需要分词,因此是keyword;不参与搜索,因此需要index为false;无需分词器</li> <li data-list="bullet"><span class="ql-ui"></span>score:虽然是数组,但是我们只看元素的类型,类型为float;参与搜索,因此需要index为true;无需分词器</li> <li data-list="bullet"><span class="ql-ui"></span>name:类型为object,需要定义多个子属性</li> <li data-list="bullet" class="ql-indent-4"><span class="ql-ui"></span>name.firstName;类型为字符串,但是不需要分词,因此是keyword;参与搜索,因此需要index为true;无需分词器</li> <li data-list="bullet" class="ql-indent-4"><span class="ql-ui"></span>name.lastName;类型为字符串,但是不需要分词,因此是keyword;参与搜索,因此需要index为true;无需分词器</li> </ol> <p><br></p> <p><br></p> <p><br></p> <h4>创建索引库和映射</h4> <p><br></p> <h5>基本语法:</h5> <p><br></p> <ol> <li data-list="bullet"><span class="ql-ui"></span>请求方式: PUT</li> <li data-list="bullet"><span class="ql-ui"></span>请求路径:/{索引库名}, 可以自定义</li> <li data-list="bullet"><span class="ql-ui"></span>请求参数:mapping 映射</li> </ol> <p><br></p> <p>格式:</p> <p><br></p> <div class="ql-code-block-container"> <div class="ql-code-block"> son </div> <div class="ql-code-block"> PUT /索引库名称 </div> <div class="ql-code-block"> { </div> <div class="ql-code-block"> "mappings": { </div> <div class="ql-code-block"> "properties": { </div> <div class="ql-code-block"> "字段名":{ </div> <div class="ql-code-block"> "type": "text", </div> <div class="ql-code-block"> "analyzer": "ik_smart" </div> <div class="ql-code-block"> }, </div> <div class="ql-code-block"> "字段名2":{ </div> <div class="ql-code-block"> "type": "keyword", </div> <div class="ql-code-block"> "index": "false" </div> <div class="ql-code-block"> }, </div> <div class="ql-code-block"> "字段名3":{ </div> <div class="ql-code-block"> "properties": { </div> <div class="ql-code-block"> "子字段": { </div> <div class="ql-code-block"> "type": "keyword" </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> }, </div> <div class="ql-code-block"> // ...略 </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> </div> <p><br></p> <p><br></p> <p><br></p> <p><br></p> <h5>示例(以上面的数据为例,我们所有的操作都在 Kibana 的中 dev tools 中完成)</h5> <p><br></p> <div class="ql-code-block-container"> <div class="ql-code-block"> son </div> <div class="ql-code-block"> PUT /xwhking </div> <div class="ql-code-block"> { </div> <div class="ql-code-block"> "mappings": { </div> <div class="ql-code-block"> "properties": { </div> <div class="ql-code-block"> "age":{ </div> <div class="ql-code-block"> "type": "integer" </div> <div class="ql-code-block"> }, </div> <div class="ql-code-block"> "weight" :{ </div> <div class="ql-code-block"> "type": "float" </div> <div class="ql-code-block"> }, </div> <div class="ql-code-block"> "isMarrid":{ </div> <div class="ql-code-block"> "type": "boolean" </div> <div class="ql-code-block"> }, </div> <div class="ql-code-block"> "info":{ </div> <div class="ql-code-block"> "type": "text", </div> <div class="ql-code-block"> "analyzer": "ik_smart" </div> <div class="ql-code-block"> }, </div> <div class="ql-code-block"> "email":{ </div> <div class="ql-code-block"> "type": "keyword", </div> <div class="ql-code-block"> "index": false </div> <div class="ql-code-block"> }, </div> <div class="ql-code-block"> "score" :{ </div> <div class="ql-code-block"> "type": "float" </div> <div class="ql-code-block"> }, </div> <div class="ql-code-block"> "name":{ </div> <div class="ql-code-block"> "properties": { </div> <div class="ql-code-block"> "firstname":{ </div> <div class="ql-code-block"> "type":"keyword" </div> <div class="ql-code-block"> }, </div> <div class="ql-code-block"> "lastname" :{ </div> <div class="ql-code-block"> "type":"keyword" </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> </div> <p><br></p> <p><br></p> <p>结果展示:</p> <p><br></p> <p>!<a href="https://img-blog.csdnimg.cn/direct/53822eaa29ec48abbc1e95a38bbe1c5e.png" target="_blank">在这里插入图片描述</a></p> <p><br></p> <p><br></p> <p><br></p> <p><br></p> <h4>查询索引库</h4> <p><br></p> <h5>基本语法:</h5> <p><br></p> <ol> <li data-list="bullet"><span class="ql-ui"></span>请求方式:GET</li> <li data-list="bullet"><span class="ql-ui"></span>请求路径:/索引库名</li> <li data-list="bullet"><span class="ql-ui"></span>请求参数:无</li> </ol> <p><br></p> <p>格式:</p> <p><br></p> <div class="ql-code-block-container"> <div class="ql-code-block"> son </div> <div class="ql-code-block"> GET /索引库名 </div> </div> <p><br></p> <p><br></p> <p>示例(上面创建的索引库):</p> <p><br></p> <div class="ql-code-block-container"> <div class="ql-code-block"> son </div> <div class="ql-code-block"> GET /xwhking </div> </div> <p><br></p> <p><br></p> <p>结果展示:</p> <p><br></p> <p>!<a href="https://img-blog.csdnimg.cn/direct/e80cd7bcd39e4d3786cc8b20f52734e2.png" target="_blank">在这里插入图片描述</a></p> <p><br></p> <p><br></p> <p><br></p> <p><br></p> <h4>修改索引库</h4> <p><br></p> <p>倒排索引结构虽然不复杂,但是一旦数据结构改变(比如改变了分词器),就需要重新创建倒排索引,这简直是灾难。因此索引库<strong>一旦创建,无法修改mapping</strong>。</p> <p><br></p> <p><br></p> <p><br></p> <p>虽然无法修改mapping中已有的字段,但是却允许添加新的字段到mapping中,因为不会对倒排索引产生影响。</p> <p><br></p> <h5>基本语法:</h5> <p><br></p> <p>- 请求方法:PUT</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>请求方法:PUT库名/_mapping</li> <li data-list="bullet"><span class="ql-ui"></span>请求参数:properties (看下面的例子)</li> </ol> <p><br></p> <p>格式:</p> <p><br></p> <div class="ql-code-block-container"> <div class="ql-code-block"> son </div> <div class="ql-code-block"> PUT /索引库名/_mapping </div> <div class="ql-code-block"> { </div> <div class="ql-code-block"> "properties": { </div> <div class="ql-code-block"> "新字段名":{ </div> <div class="ql-code-block"> "type": "integer" </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> </div> <p><br></p> <p><br></p> <p>示例:</p> <p><br></p> <p>为上面创建的索引添加一个 major 字段(Field)</p> <p><br></p> <div class="ql-code-block-container"> <div class="ql-code-block"> son </div> <div class="ql-code-block"> # 为索引添加字段 </div> <div class="ql-code-block"> PUT /xwhking/_mapping </div> <div class="ql-code-block"> { </div> <div class="ql-code-block"> "properties" : { </div> <div class="ql-code-block"> "major" : { </div> <div class="ql-code-block"> "type" : "keyword" </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> </div> <p><br></p> <p><br></p> <p>添加结果:</p> <p><br></p> <p>![在<a href="https://img-blog.csdnimg.cn/direct/f9f548625d754acbba3a5cea974c02c4.png" target="_blank">在这里插入图片描述</a></p> <p>我们重新查看索引看是否有添加字段:</p> <p><br></p> <p>!<a href="https://img-blog.csdnimg.cn/direct/db8418ec4d6c487f9cf8d6d0152362fe.png" target="_blank">在这里插入图片描述</a></p> <p><br></p> <p><br></p> <p>从结果来看我们是成功了的。</p> <p><br></p> <p><br></p> <p><br></p> <h4>删除索引库</h4> <p><br></p> <h5>基本语法:</h5> <p><br></p> <ol> <li data-list="bullet"><span class="ql-ui"></span>请求方式:DELETE</li> <li data-list="bullet"><span class="ql-ui"></span>请求路径:/索引库名</li> <li data-list="bullet"><span class="ql-ui"></span>请求参数: 无</li> </ol> <p><br></p> <p>格式:</p> <p><br></p> <div class="ql-code-block-container"> <div class="ql-code-block"> son </div> <div class="ql-code-block"> DELETE /索引库名 </div> </div> <p><br></p> <p><br></p> <p>例子:</p> <p><br></p> <p>我们删除我们上面传创建的索引库,然后再去查询,看是否能够查询的到</p> <p><br></p> <div class="ql-code-block-container"> <div class="ql-code-block"> DELETE /xwhking </div> </div> <p><br></p> <p><br></p> <p>结果:</p> <p><br></p> <p>!<a href="http://ht%E5%9C%A8%E8%BF%99%E9%87%8C%E6%8F%92%E5%85%A5%E5%9B%BE%E7%89%87%E6%8F%8F%E8%BF%B0%5D(https://img-blog.csdnimg.cn/direct/6da2a7e2933f444897c59220c7570a3d.png" target="_blank">在这里插入图片描述</a></p> <p><br></p> <p><br></p> <p>很明显我们查询不到了。</p> <p><br></p> <p><br></p> <p><br></p> <h3>#档的增删改查</h3> <p><br></p> <h4>新增文档</h4> <p><br></p> <h5>基本语法:</h5> <p><br></p> <ol> <li data-list="bullet"><span class="ql-ui"></span>请求方式:POST</li> </ol> <p>请求路径:/索引库名/_doc/文档id <code>注意这里的文档 id,如果不填,elasticsearch系统会自动帮你添加一个随机的 id 值 - 请求参数:一条以 json</code> 为格式的数据采用键值对方式</p> <p><br></p> <p>格式:</p> <p><br></p> <div class="ql-code-block-container"> <div class="ql-code-block"> son </div> <div class="ql-code-block"> POST /索引库名/_doc/文档id </div> <div class="ql-code-block"> { </div> <div class="ql-code-block"> "字段1": "值1", </div> <div class="ql-code-block"> "字段2": "值2", </div> <div class="ql-code-block"> "字段3": { </div> <div class="ql-code-block"> "子属性1": "值3", </div> <div class="ql-code-block"> "子属性2": "值4" </div> <div class="ql-code-block"> }, </div> <div class="ql-code-block"> // ... </div> <div class="ql-code-block"> } </div> </div> <p><br></p> <p><br></p> <p>例子:</p> <p><br></p> <p>我们再把上面删除的索引重新创建一下,把最开始的那条数据插入试试</p> <p><br></p> <div class="ql-code-block-container"> <div class="ql-code-block"> son </div> <div class="ql-code-block"> # 创建一个文档 </div> <div class="ql-code-block"> POST /xwhking/_doc/1 </div> <div class="ql-code-block"> { </div> <div class="ql-code-block"> "age":21, </div> <div class="ql-code-block"> "weight":52.1, </div> <div class="ql-code-block"> "isMarried":false, </div> <div class="ql-code-block"> "info":"created by xwhking", </div> <div class="ql-code-block"> "email":"2837468248@qq.com", </div> <div class="ql-code-block"> "score":[99.1, 99.5, 98.9], </div> <div class="ql-code-block"> "name":{ </div> <div class="ql-code-block"> "firstName":"XWH", </div> <div class="ql-code-block"> "lastName":"Z" </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> </div> <p><br></p> <p><br></p> <blockquote> <div class="blockquote-item"> 这里可能会有一个疑问:就什么我明明定义 score 的时候只是一个 float 类型,为什么在插入文档的时候就能插入一个数组呢?这是由 elasticsearch 决定的,因为elasticsearch 没有数组这个数据类型,所以就允许接受一个字段的多个值。 </div> </blockquote> <p><br></p> <p>结果:</p> <p><br></p> <p>!<a href="https://img-blog.csdnimg.cn/direct/f1c4857713064d2292d5f7902f1dbff5.png" target="_blank">在这里插入图片描述</a></p> <p><br></p> <p><br></p> <p><br></p> <p><br></p> <h4>查询文档</h4> <p><br></p> <h5>基本语法:</h5> <p><br></p> <ol> <li data-list="bullet"><span class="ql-ui"></span>请求方式:GET</li> <li data-list="bullet"><span class="ql-ui"></span>请求路径:/索引库名/_doc/文档id</li> <li data-list="bullet"><span class="ql-ui"></span>-请求参数:无</li> </ol> <p>格式:</p> <p><br></p> <div class="ql-code-block-container"> <div class="ql-code-block"> son </div> <div class="ql-code-block"> GET /{索引库名称}/_doc/{id} </div> </div> <p><br></p> <p><br></p> <p>示例:</p> <p><br></p> <div class="ql-code-block-container"> <div class="ql-code-block"> son </div> <div class="ql-code-block"> GET /xwhking/_doc/1 </div> </div> <p><br></p> <p><br></p> <p>结果:</p> <p><br></p> <p>!<a href="https://img-blog.csdnimg.cn/direct/ac0d2123621d4a03b10dbf43eb4b61f7.png" target="_blank">在这里插入图片描述</a></p> <p><br></p> <p><br></p> <p><br></p> <p><br></p> <h4>删除文档</h4> <p><br></p> <h5>基本语法:</h5> <p><br></p> <ol> <li data-list="bullet"><span class="ql-ui"></span>请求方式:DELETE</li> <li data-list="bullet"><span class="ql-ui"></span>请求路径:/索引库名/_doc/文档id</li> <li data-list="bullet"><span class="ql-ui"></span>请求参数:无</li> </ol> <p><br></p> <p>格式:</p> <p><br></p> <div class="ql-code-block-container"> <div class="ql-code-block"> son </div> <div class="ql-code-block"> DELETE /{索引库名}/_doc/id值 </div> </div> <p><br></p> <p><br></p> <p>示例</p> <p><br></p> <div class="ql-code-block-container"> <div class="ql-code-block"> son </div> <div class="ql-code-block"> DELETE /xwhking/_doc/1 </div> </div> <p><br></p> <p><br></p> <p>结果参考</p> <p><br></p> <p>!<a href="https://img-blog.csdnimg.cn/direct/81d284a9999c4cc1928717c0830b2182.png" target="_blank">在这里插入图片描述</a></p> <p><br></p> <p><br></p> <p><br></p> <p><br></p> <h4>修改文档</h4> <p><br></p> <p>修改有两种方式:</p> <p><br></p> <ol> <li data-list="bullet"><span class="ql-ui"></span>全量修改:直接覆盖原来的文档</li> <li data-list="bullet"><span class="ql-ui"></span>增量修改:修改文档中的部分字段</li> </ol> <p><br></p> <p><br></p> <p><br></p> <h5>全量修改</h5> <p><br></p> <p>全量修改是覆盖原来的文档,其本质是:</p> <p><br></p> <ol> <li data-list="bullet"><span class="ql-ui"></span>根据指定的id删除文档</li> <li data-list="bullet"><span class="ql-ui"></span>新增一个相同id的文档</li> </ol> <p><br></p> <p><strong>注意</strong>:如果根据id删除时,id不存在,第二步的新增也会执行,也就从修改变成了新增操作了。</p> <p><br></p> <h6>基本语法:</h6> <p><br></p> <ol> <li data-list="bullet"><span class="ql-ui"></span>请求方式:PUT</li> <li data-list="bullet"><span class="ql-ui"></span>请求路径:/索引库名/_doc/文档id</li> <li data-list="bullet"><span class="ql-ui"></span>请求参数:把所有文档信息要修改的进行修改,并且没有修改的也需要</li> </ol> <p><br></p> <p>格式:</p> <p><br></p> <div class="ql-code-block-container"> <div class="ql-code-block"> son </div> <div class="ql-code-block"> PUT /{索引库名}/_doc/文档id </div> <div class="ql-code-block"> { </div> <div class="ql-code-block"> "字段1": "值1", </div> <div class="ql-code-block"> "字段2": "值2", </div> <div class="ql-code-block"> // ... 略 </div> <div class="ql-code-block"> } </div> </div> <p><br></p> <p><br></p> <p>示例:</p> <p><br></p> <p>我们改变一下上面文档的邮箱</p> <p><br></p> <div class="ql-code-block-container"> <div class="ql-code-block"> son </div> <div class="ql-code-block"> PUT /xwhking/_doc/1 </div> <div class="ql-code-block"> { </div> <div class="ql-code-block"> "age": 21, </div> <div class="ql-code-block"> "weight": 52.1, </div> <div class="ql-code-block"> "isMarried": false, </div> <div class="ql-code-block"> "info": "created by xwhking", </div> <div class="ql-code-block"> "email": "https://www.github.com/xwhking", </div> <div class="ql-code-block"> "score": [99.1, 99.5, 98.9], </div> <div class="ql-code-block"> "name": { </div> <div class="ql-code-block"> "firstName": "XWH", </div> <div class="ql-code-block"> "lastName": "Z" </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> </div> <p><br></p> <p><br></p> <p>结果:</p> <p><br></p> <p>!<a href="https://img-blog.csdnimg.cn/direct/e83bd0fa3bfb4ae29666a861c3ba12b0.png" target="_blank">在这里插入图片描述</a></p> <p><br></p> <p><br></p> <p><br></p> <p><br></p> <h5>增量修改</h5> <p><br></p> <p>增量修改是只修改指定id匹配的文档中的部分字段。</p> <p><br></p> <h6>基本语法:</h6> <p><br></p> <ol> <li data-list="bullet"><span class="ql-ui"></span>请求方式:POST</li> <li data-list="bullet"><span class="ql-ui"></span>请求路径:/索引库名/_update/文档id</li> <li data-list="bullet"><span class="ql-ui"></span>请求参数:doc 内容是要修改的字段,以及新的值</li> </ol> <p><br></p> <p>基本格式:</p> <p><br></p> <div class="ql-code-block-container"> <div class="ql-code-block"> son </div> <div class="ql-code-block"> POST /{索引库名}/_update/文档id </div> <div class="ql-code-block"> { </div> <div class="ql-code-block"> "doc": { </div> <div class="ql-code-block"> "字段名": "新的值", </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> </div> <p><br></p> <p><br></p> <p>示例(我们再把邮箱改回来):</p> <p><br></p> <div class="ql-code-block-container"> <div class="ql-code-block"> son </div> <div class="ql-code-block"> # 修改文档,增量修改 </div> <div class="ql-code-block"> POST /xwhking/_doc/1 </div> <div class="ql-code-block"> { </div> <div class="ql-code-block"> "doc": { </div> <div class="ql-code-block"> "email" : "2837468248@qq..com" </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"><br> </div> </div> <p><br></p> <p><br></p> <p>其中的 version 表示修改次数,创建一次,上面一次全量修改一次,增量修改一次,所以是三次。</p> <p><br></p> <p>!<a href="https://img-blog.csdnimg.cn/direct/f95d64e8209d4754a4b338f9398982af.png" target="_blank">在这里插入图片描述</a></p> <p><br></p> <p><br></p> <p><br></p> <p><br></p> <h3>利用 RestAPI 在 Java 中使用 ElasticSearch</h3> <p><br></p> <p>这里暂不介绍如何使用,具体使用其实看一下官方文档,或者直接按照代码提示就可以做了,我们现在只要知道ElasticSearch原生的用法,再去用 Client 就很容易上手、理解。我们这里只稍微带一下在 Java 项目中如何引入。</p> <p><br></p> <p>三步:</p> <p><br></p> <ol> <li data-list="ordered"><span class="ql-ui"></span>引入依赖</li> </ol> <p><br></p> <p>```xml</p> <p><dependency></p> <p><groupId>org.elasticsearch.client</groupId></p> <p><artifactId>elasticsearch-rest-high-level-client</artifactId></p> <p></dependency></p> <p>```</p> <p><br></p> <ol> <li data-list="ordered"><span class="ql-ui"></span>因为 springboot 可能管理了 elasticsearch的版本,所以我们需要把这个版本依赖覆盖掉,并且改成我们自己 elasticsearch的版本</li> </ol> <p><br></p> <p>```xml</p> <p><properties></p> <p><java.version>1.8</java.version></p> <p><elasticsearch.version>7.12.1</elasticsearch.version></p> <p></properties></p> <p>```</p> <p><br></p> <ol> <li data-list="ordered"><span class="ql-ui"></span>初始化 RestHighLevelClient</li> </ol> <p><br></p> <p>```java</p> <p>RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(</p> <p>HttpHost.create("http://192.168.150.101:9200")</p> <p>));</p> <p>```</p> <p><br></p> <p><br></p> <p><br></p> <p><br></p> <h1>TIPS</h1> <blockquote> <div class="blockquote-item"> Elasticsearch 中特殊的数据类型 </div> <div class="blockquote-item"> ES中支持两种地理坐标数据类型: </div> </blockquote> <ol> <li data-list="bullet"><span class="ql-ui"></span>geo_point: 由纬度(latitude)和经度(longitude)确定的一个点。例如:"32.8752345,120.2981576"</li> <li data-list="bullet"><span class="ql-ui"></span>ge0_shape :有多个ge0_point组成的复杂几何图形。例如一条直线,"LINESTRING(-77.0365338.897676,-77.00905138.889939)"</li> </ol> <blockquote> <div class="blockquote-item"> 字段拷贝可以使用copy to属性将当前字段拷贝到指定字段。示例: </div> </blockquote> <div class="ql-code-block-container"> <div class="ql-code-block"> son </div> <div class="ql-code-block"> > { </div> <div class="ql-code-block"> > "a11":{ </div> <div class="ql-code-block"> > "type":"text", </div> <div class="ql-code-block"> > "analyzer":"ik_max_word" </div> <div class="ql-code-block"> > }, </div> <div class="ql-code-block"> > "brand":{ </div> <div class="ql-code-block"> > "type":"keyword", </div> <div class="ql-code-block"> > "copy_to":"all" </div> <div class="ql-code-block"> > } </div> <div class="ql-code-block"> > } </div> <div class="ql-code-block"> > ``` </div> <div class="ql-code-block"> > </div> <div class="ql-code-block"> > </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> ### 如何搜索 ==> DSL查询文档 </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> #### 数组准备 </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> 我这里直接开源一个数据初始化的代码,需要的话直接拉下来,然后通过看项目的 README 文件,配置 elasticsearch, 然后把数据放入elasticsearch 我们就开始进行搜索数据。 </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> [项目数据地址](https://github.com/xwhking/elasticsearch-test-data)) </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> #### DSL 查询分类 </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> Elasticsearch提供了基于JSON的DSL([Domain Specific Language](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl.html))来定义查询。常见的查询类型包括: </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> - **查询所有**:查询出所有数据,一般测试用。例如:match_all </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> - **全文检索(full text)查询**:利用分词器对用户输入内容分词,然后去倒排索引库中匹配。例如: </div> <div class="ql-code-block"> - match_query </div> <div class="ql-code-block"> - multi_match_query </div> <div class="ql-code-block"> - **精确查询**:根据精确词条值查找数据,一般是查找keyword、数值、日期、boolean等类型字段。例如: </div> <div class="ql-code-block"> - ids </div> <div class="ql-code-block"> - range </div> <div class="ql-code-block"> - term </div> <div class="ql-code-block"> - **地理(geo)查询**:根据经纬度查询。例如: </div> <div class="ql-code-block"> - geo_distance </div> <div class="ql-code-block"> - geo_bounding_box </div> <div class="ql-code-block"> - **复合(compound)查询**:复合查询可以将上述各种查询条件组合起来,合并查询条件。例如: </div> <div class="ql-code-block"> - bool </div> <div class="ql-code-block"> - function_score </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> ##### 查询基本语法: </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> - 请求方式: GET </div> <div class="ql-code-block"> - 请求路径:/索引库/_search </div> <div class="ql-code-block"> - 请求参数:根据不同请求方式不同 </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> 基本格式: </div> <div class="ql-code-block"><br> </div> </div> <p><br></p> <p>GET /indexName/_search</p> <p>{</p> <p>"query": {</p> <p>"查询类型": {</p> <p>"查询条件": "条件值"</p> <p>}</p> <p>}</p> <p>}</p> <div class="ql-code-block-container"> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> 示例查询所有: </div> <div class="ql-code-block"><br> </div> </div> <p><br></p> <p>GET /hotel/_search</p> <p>{</p> <p>"query": {</p> <p>"match_all": {</p> <p>}</p> <p>}</p> <p>}</p> <div class="ql-code-block-container"> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> 结果: </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block">  </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> #### 全文检索查询 </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> 全文检索查询的基本流程如下: </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> - 对用户搜索的内容做分词,得到词条 </div> <div class="ql-code-block"> - 根据词条去倒排索引库中匹配,得到文档id </div> <div class="ql-code-block"> - 根据文档id找到文档,返回给用户 </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> 比较常用的场景包括: </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> - 商城的输入框搜索 </div> <div class="ql-code-block"> - 百度输入框搜索 </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> > # TIPS </div> <div class="ql-code-block"> > </div> <div class="ql-code-block"> > 因为是拿着词条去匹配,因此参与搜索的字段也必须是可分词的text类型的字段。 </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> ##### 基本语法 </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> - 请求方式: GET </div> <div class="ql-code-block"> - 请求路径: /索引库/_search </div> <div class="ql-code-block"> - 请求参数: </div> <div class="ql-code-block"> - match查询:单字段查询 </div> <div class="ql-code-block"> - multi_match查询:多字段查询,任意一个字段符合条件就算符合查询条件 </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> ###### 基本格式: </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> **match 查询格式** </div> <div class="ql-code-block"><br> </div> </div> <p><br></p> <p>GET /索引库名/_search</p> <p>{</p> <p>"query": {</p> <p>"match": {</p> <p>"FIELD": "TEXT"</p> <p>}</p> <p>}</p> <p>}</p> <div class="ql-code-block-container"> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> **multi_match** 查询格式: </div> <div class="ql-code-block"><br> </div> </div> <p><br></p> <p>GET /indexName/_search</p> <p>{</p> <p>"query": {</p> <p>"multi_match": {</p> <p>"query": "TEXT",</p> <p>"fields": ["FIELD1", " FIELD12"]</p> <p>}</p> <p>}</p> <p>}</p> <div class="ql-code-block-container"> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> 查询示例: </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> match 示例: </div> <div class="ql-code-block"><br> </div> </div> <p><br></p> <p>GET /hotel/_search</p> <p>{</p> <p>"query": {</p> <p>"match": {</p> <p>"all": "希尔顿"</p> <p>}</p> <p>}</p> <p>}</p> <div class="ql-code-block-container"> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> result : </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block">  </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> multi_match 示例: </div> <div class="ql-code-block"><br> </div> </div> <p><br></p> <p>GET /hotel/_search</p> <p>{</p> <p>"query": {</p> <p>"multi_match": {</p> <p>"query": "如家",</p> <p>"fields": ["brand","name"]</p> <p>}</p> <p>}</p> <p>}</p> <div class="ql-code-block-container"> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> result: </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block">  </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> > # TIPS </div> <div class="ql-code-block"> > </div> <div class="ql-code-block"> > 如果这里我们使用同样的查询词,查询出来的结果会是一样的,为什么呢? </div> <div class="ql-code-block"> > </div> <div class="ql-code-block"> > 因为我们将brand、name、business值都利用copy_to复制到了all字段中。因此你根据三个字段搜索,和根据all字段搜索效果当然一样了。 </div> <div class="ql-code-block"> > </div> <div class="ql-code-block"> > 但是,搜索字段越多,对查询性能影响越大,因此建议采用copy_to,然后单字段查询的方式。 </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> #### 精确查询 </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> 精确查询一般是查找keyword、数值、日期、boolean等类型字段。所以**不会**对搜索条件分词。常见的有: </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> - term:根据词条精确值查询 </div> <div class="ql-code-block"> - range:根据值的范围查询 </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> ##### Term 查询 </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> 因为精确查询的字段搜是不分词的字段,因此查询的条件也必须是**不分词**的词条。查询时,用户输入的内容跟自动值完全匹配时才认为符合条件。如果用户输入的内容过多,反而搜索不到数据。 </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> ###### 基本语法 </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> - 请求方式: GET </div> <div class="ql-code-block"> - 请求路径: /索引库/_search </div> <div class="ql-code-block"> - 请求参数:见示例 </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> 基本格式: </div> <div class="ql-code-block"><br> </div> </div> <p><br></p> <p>// term查询</p> <p>GET /indexName/_search</p> <p>{</p> <p>"query": {</p> <p>"term": {</p> <p>"FIELD": {</p> <p>"value": "VALUE"</p> <p>}</p> <p>}</p> <p>}</p> <p>}</p> <div class="ql-code-block-container"> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> 示例: </div> <div class="ql-code-block"><br> </div> </div> <p><br></p> <p>GET /hotel/_search</p> <p>{</p> <p>"query": {</p> <p>"term": {</p> <p>"city": {</p> <p>"value": "上海"</p> <p>}</p> <p>}</p> <p>}</p> <p>}</p> <div class="ql-code-block-container"> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> result: </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block">  </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> 因为词条是精匹配如果我们输入的是`杭州上海`则不会有结果 </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> <u>但是,当我搜索的内容不是词条,而是多个词语形成的短语时,反而搜索不到:</u> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block">  </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> ##### range 查询 </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> 范围查询,一般应用在对数值类型做范围过滤的时候。比如做价格范围过滤。 </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> ###### 基本语法 </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> - 请求方式: GET </div> <div class="ql-code-block"> - 请求路径: /索引库/_search </div> <div class="ql-code-block"> - 请求参数: 看具体示例 </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> 基本格式: </div> <div class="ql-code-block"><br> </div> </div> <p><br></p> <p>// range查询</p> <p>GET /indexName/_search</p> <p>{</p> <p>"query": {</p> <p>"range": {</p> <p>"FIELD": {</p> <p>"gte": 10, // 这里的gte代表大于等于,gt则代表大于</p> <p>"lte": 20 // lte代表小于等于,lt则代表小于</p> <p>}</p> <p>}</p> <p>}</p> <p>}</p> <div class="ql-code-block-container"> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> 示例: </div> <div class="ql-code-block"><br> </div> </div> <p><br></p> <p>GET /hotel/_search</p> <p>{</p> <p>"query": {</p> <p>"range": {</p> <p>"score": {</p> <p>"gte": 40,</p> <p>"lte": 3000</p> <p>}</p> <p>}</p> <p>}</p> <p>}</p> <div class="ql-code-block-container"> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> result: </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> 出现的都是在 40 以上的! </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block">  </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> #### </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> #### 地理坐标查询 </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> 所谓的地理坐标查询,其实就是根据经纬度查询,官方文档:https://www.elastic.co/guide/en/elasticsearch/reference/current/geo-queries.html </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> 常见的使用场景包括: </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> - 携程:搜索我附近的酒店 </div> <div class="ql-code-block"> - 滴滴:搜索我附近的出租车 </div> <div class="ql-code-block"> - 微信:搜索我附近的人 </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> ##### 矩形范围查询 </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> ###### 基本语法 </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> - 请求方式:GET </div> <div class="ql-code-block"> - 请求路径:/索引库/_search </div> <div class="ql-code-block"> - 请求参数:请看示例 </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block">  </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> 查询时,需要指定矩形的**左上**、**右下**两个点的坐标,然后画出一个矩形,落在该矩形内的都是符合条件的点。 </div> <div class="ql-code-block"><br> </div> </div> <p><br></p> <p>// geo_bounding_box查询</p> <p>GET /indexName/_search</p> <p>{</p> <p>"query": {</p> <p>"geo_bounding_box": {</p> <p>"FIELD": {</p> <p>"top_left": { // 左上点</p> <p>"lat": 31.1,</p> <p>"lon": 121.5</p> <p>},</p> <p>"bottom_right": { // 右下点</p> <p>"lat": 30.9,</p> <p>"lon": 121.7</p> <p>}</p> <p>}</p> <p>}</p> <p>}</p> <p>}</p> <div class="ql-code-block-container"> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> ##### 附近查询 </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> 附近查询,也叫做距离查询(geo_distance):查询到指定中心点小于某个距离值的所有文档。 </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> 换句话来说,在地图上找一个点作为圆心,以指定距离为半径,画一个圆,落在圆内的坐标都算符合条件: </div> <div class="ql-code-block">  </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> ###### 基本语法: </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> - 请求方式:GET </div> <div class="ql-code-block"> - 请求路径:/索引库/_search </div> <div class="ql-code-block"> - 请求参数:请看示例 </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> 格式: </div> <div class="ql-code-block"><br> </div> </div> <p><br></p> <p>// geo_distance 查询</p> <p>GET /indexName/_search</p> <p>{</p> <p>"query": {</p> <p>"geo_distance": {</p> <p>"distance": "15km", // 半径</p> <p>"FIELD": "31.21,121.5" // 圆心</p> <p>}</p> <p>}</p> <p>}</p> <div class="ql-code-block-container"> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> 示例: </div> <div class="ql-code-block"><br> </div> </div> <p><br></p> <p>GET /hotel/_search</p> <p>{</p> <p>"query": {</p> <p>"geo_distance": {</p> <p>"distance" : "15km",</p> <p>"location" : "31.21,121.5"</p> <p>}</p> <p>}</p> <p>}</p> <div class="ql-code-block-container"> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> result: </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block">  </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> 缩小一下半径(就是陆家嘴附近啦): </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block">  </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> #### 复合查询 </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> 当我们利用match查询时,文档结果会根据与搜索词条的关联度打分(_score),返回结果时按照分值降序排列。 </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> 例如,我们搜索 "虹桥如家",结果如下: </div> <div class="ql-code-block"><br> </div> </div> <p><br></p> <p>[</p> <p>{</p> <p>"_score" : 17.850193,</p> <p>"_source" : {</p> <p>"name" : "虹桥如家酒店真不错",</p> <p>}</p> <p>},</p> <p>{</p> <p>"_score" : 12.259849,</p> <p>"_source" : {</p> <p>"name" : "外滩如家酒店真不错",</p> <p>}</p> <p>},</p> <p>{</p> <p>"_score" : 11.91091,</p> <p>"_source" : {</p> <p>"name" : "迪士尼如家酒店真不错",</p> <p>}</p> <p>}</p> <p>]</p> <div class="ql-code-block-container"> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> 在elasticsearch中,早期使用的打分算法是TF-IDF算法,公式如下: </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block">  </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> 在后来的5.1版本升级中,elasticsearch将算法改进为BM25算法,公式如下: </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block">  </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> TF-IDF算法有一各缺陷,就是词条频率越高,文档得分也会越高,单个词条对文档影响较大。而BM25则会让单个词条的算分有一个上限,曲线更加平滑: </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block">  </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> ##### 算分函数查询 </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> 根据相关度打分是比较合理的需求,但**合理的不一定是产品经理需要**的。 </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> 以百度为例,你搜索的结果中,并不是相关度越高排名越靠前,而是谁掏的钱多排名就越靠前。 </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> 要想认为控制相关性算分,就需要利用elasticsearch中的function score 查询了。 </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> ###### 基本语法: </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block">  </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> function score 查询中包含四部分内容: </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> - **原始查询**条件:query部分,基于这个条件搜索文档,并且基于BM25算法给文档打分,**原始算分**(query score) </div> <div class="ql-code-block"> - **过滤条件**:filter部分,符合该条件的文档才会重新算分 </div> <div class="ql-code-block"> - **算分函数**:符合filter条件的文档要根据这个函数做运算,得到的**函数算分**(function score),有四种函数 </div> <div class="ql-code-block"> - weight:函数结果是常量 </div> <div class="ql-code-block"> - field_value_factor:以文档中的某个字段值作为函数结果 </div> <div class="ql-code-block"> - random_score:以随机数作为函数结果 </div> <div class="ql-code-block"> - script_score:自定义算分函数算法 </div> <div class="ql-code-block"> - **运算模式**:算分函数的结果、原始查询的相关性算分,两者之间的运算方式,包括: </div> <div class="ql-code-block"> - multiply:相乘 </div> <div class="ql-code-block"> - replace:用function score替换query score </div> <div class="ql-code-block"> - 其它,例如:sum、avg、max、min </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> function score的运行流程如下: </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> - 1)根据**原始条件**查询搜索文档,并且计算相关性算分,称为**原始算分**(query score) </div> <div class="ql-code-block"> - 2)根据**过滤条件**,过滤文档 </div> <div class="ql-code-block"> - 3)符合**过滤条件**的文档,基于**算分函数**运算,得到**函数算分**(function score) </div> <div class="ql-code-block"> - 4)将**原始算分**(query score)和**函数算分**(function score)基于**运算模式**做运算,得到最终结果,作为相关性算分。 </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> 因此,其中的关键点是: </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> - 过滤条件:决定哪些文档的算分被修改 </div> <div class="ql-code-block"> - 算分函数:决定函数算分的算法 </div> <div class="ql-code-block"> - 运算模式:决定最终算分结果 </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> 示例: </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> 需求:给“如家”这个品牌的酒店排名靠前一些 </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> 翻译一下这个需求,转换为之前说的四个要点: </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> - 原始条件:不确定,可以任意变化 </div> <div class="ql-code-block"> - 过滤条件:brand = "如家" </div> <div class="ql-code-block"> - 算分函数:可以简单粗暴,直接给固定的算分结果,weight </div> <div class="ql-code-block"> - 运算模式:比如求和 </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> 因此最终的DSL语句如下: </div> <div class="ql-code-block"><br> </div> </div> <p><br></p> <p>GET /hotel/_search</p> <p>{</p> <p>"query": {</p> <p>"function_score": {</p> <p>"query": {</p> <p>"match": {</p> <p>"all": "外滩"</p> <p>}</p> <p>},</p> <p>"functions": [</p> <p>{</p> <p>"filter": {</p> <p>"term": {</p> <p>"brand": "如家"</p> <p>}</p> <p>},</p> <p>"weight": 100</p> <p>}</p> <p>],</p> <p>"boost_mode": "sum"</p> <p>}</p> <p>}</p> <p>}</p> <div class="ql-code-block-container"> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> result: </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block">  </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> function score query定义的三要素是什么? </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> - 过滤条件:哪些文档要加分 </div> <div class="ql-code-block"> - 算分函数:如何计算function score </div> <div class="ql-code-block"> - 加权方式:function score 与 query score如何运算 </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> ##### 布尔查询 </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> 布尔查询是一个或多个查询子句的组合,每一个子句就是一个**子查询**。子查询的组合方式有: </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> - must:必须匹配每个子查询,类似“与” </div> <div class="ql-code-block"> - should:选择性匹配子查询,类似“或” </div> <div class="ql-code-block"> - must_not:必须不匹配,**不参与算分**,类似“非” </div> <div class="ql-code-block"> - filter:必须匹配,**不参与算分** </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> 比如在搜索酒店时,除了关键字搜索外,我们还可能根据品牌、价格、城市等字段做过滤: </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> 每一个不同的字段,其查询的条件、方式都不一样,必须是多个不同的查询,而要组合这些查询,就必须用bool查询了。 </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> 需要注意的是,搜索时,参与**打分的字段越多,查询的性能也越差**。因此这种多条件查询时,建议这样做: </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> - 搜索框的关键字搜索,是全文检索查询,使用must查询,参与算分 </div> <div class="ql-code-block"> - 其它过滤条件,采用filter查询。不参与算分 </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> ###### 基本语法 </div> <div class="ql-code-block"><br> </div> </div> <p><br></p> <p>GET /hotel/_search</p> <p>{</p> <p>"query": {</p> <p>"bool": {</p> <p>"must": [</p> <p>{"term": {"city": "上海" }}</p> <p>],</p> <p>"should": [</p> <p>{"term": {"brand": "皇冠假日" }},</p> <p>{"term": {"brand": "华美达" }}</p> <p>],</p> <p>"must_not": [</p> <p>{ "range": { "price": { "lte": 500 } }}</p> <p>],</p> <p>"filter": [</p> <p>{ "range": {"score": { "gte": 45 } }}</p> <p>]</p> <p>}</p> <p>}</p> <p>}</p> <div class="ql-code-block-container"> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> 示例: </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> 需求:搜索名字包含“如家”,价格不高于400,在坐标31.21,121.5周围10km范围内的酒店。 </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> 分析: </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> - 名称搜索,属于全文检索查询,应该参与算分。放到must中 </div> <div class="ql-code-block"> - 价格不高于400,用range查询,属于过滤条件,不参与算分。放到must_not中 </div> <div class="ql-code-block"> - 周围10km范围内,用geo_distance查询,属于过滤条件,不参与算分。放到filter中 </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> result: </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block">  </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> #### 搜索结果处理 </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> ##### 排序 </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> 地理坐标排序略有不同。 </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> **语法说明**: </div> <div class="ql-code-block"><br> </div> </div> <p><br></p> <p>GET /indexName/_search</p> <p>{</p> <p>"query": {</p> <p>"match_all": {}</p> <p>},</p> <p>"sort": [</p> <p>{</p> <p>"_geo_distance" : {</p> <p>"FIELD" : "纬度,经度", // 文档中geo_point类型的字段名、目标坐标点</p> <p>"order" : "asc", // 排序方式</p> <p>"unit" : "km" // 排序的距离单位</p> <p>}</p> <p>}</p> <p>]</p> <p>}</p> <div class="ql-code-block-container"> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> 这个查询的含义是: </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> - 指定一个坐标,作为目标点 </div> <div class="ql-code-block"> - 计算每一个文档中,指定字段(必须是geo_point类型)的坐标 到目标点的距离是多少 </div> <div class="ql-code-block"> - 根据距离排序 </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> ##### 分页 </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> elasticsearch 默认情况下只返回top10的数据。而如果要查询更多数据就需要修改分页参数了。elasticsearch中通过修改from、size参数来控制要返回的分页结果: </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> - from:从第几个文档开始 </div> <div class="ql-code-block"> - size:总共查询几个文档 </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> 类似于mysql中的`limit ?, ?` </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> 分页的基本语法如下: </div> <div class="ql-code-block"><br> </div> </div> <p><br></p> <p>GET /hotel/_search</p> <p>{</p> <p>"query": {</p> <p>"match_all": {}</p> <p>},</p> <p>"from": 0, // 分页开始的位置,默认为0</p> <p>"size": 10, // 期望获取的文档总数</p> <p>"sort": [</p> <p>{"price": "asc"}</p> <p>]</p> <p>}</p> <div class="ql-code-block-container"> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> 现在,我要查询990~1000的数据,查询逻辑要这么写: </div> <div class="ql-code-block"><br> </div> </div> <p><br></p> <p>GET /hotel/_search</p> <p>{</p> <p>"query": {</p> <p>"match_all": {}</p> <p>},</p> <p>"from": 990, // 分页开始的位置,默认为0</p> <p>"size": 10, // 期望获取的文档总数</p> <p>"sort": [</p> <p>{"price": "asc"}</p> <p>]</p> <p>}</p> <div class="ql-code-block-container"> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> 这里是查询990开始的数据,也就是 第990~第1000条 数据。 </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> 不过,elasticsearch内部分页时,必须先查询 0~1000条,然后截取其中的990 ~ 1000的这10条: </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> 查询TOP1000,如果es是单点模式,这并无太大影响。 </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> 但是elasticsearch将来一定是集群,例如我集群有5个节点,我要查询TOP1000的数据,并不是每个节点查询200条就可以了。 </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> 因为节点A的TOP200,在另一个节点可能排到10000名以外了。 </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> 因此要想获取整个集群的TOP1000,必须先查询出每个节点的TOP1000,汇总结果后,重新排名,重新截取TOP1000。 </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> 那如果我要查询9900~10000的数据呢?是不是要先查询TOP10000呢?那每个节点都要查询10000条?汇总到内存中? </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> 当查询分页深度较大时,汇总数据过多,对内存和CPU会产生非常大的压力,因此elasticsearch会禁止from+ size 超过10000的请求。 </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> 针对深度分页,ES提供了两种解决方案,[官方文档](https://www.elastic.co/guide/en/elasticsearch/reference/current/paginate-search-results.html): </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> - search after:分页时需要排序,原理是从上一次的排序值开始,查询下一页数据。官方推荐使用的方式。 </div> <div class="ql-code-block"> - scroll:原理将排序后的文档id形成快照,保存在内存。官方已经不推荐使用。 </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> ##### 高亮 </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> 高亮显示的实现分为两步: </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> - 1)给文档中的所有关键字都添加一个标签,例如`<em>`标签 </div> <div class="ql-code-block"> - 2)页面给`<em>`标签编写CSS样式 </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> **高亮的语法**: </div> <div class="ql-code-block"><br> </div> </div> <p><br></p> <p>GET /hotel/_search</p> <p>{</p> <p>"query": {</p> <p>"match": {</p> <p>"FIELD": "TEXT" // 查询条件,高亮一定要使用全文检索查询</p> <p>}</p> <p>},</p> <p>"highlight": {</p> <p>"fields": { // 指定要高亮的字段</p> <p>"FIELD": {</p> <p>"pre_tags": "<em>", // 用来标记高亮字段的前置标签</p> <p>"post_tags": "</em>" // 用来标记高亮字段的后置标签</p> <p>}</p> <p>}</p> <p>}</p> <p>}</p> <p>```</p> <p><br></p> <p><br></p> <p><br></p> <p>**注意:**</p> <p><br></p> <p>- 高亮是对关键字高亮,因此**搜索条件必须带有关键字**,而不能是范围这样的查询。</p> <p>- 默认情况下,**高亮的字段,必须与搜索指定的字段一致**,否则无法高亮</p> <p>- 如果要对非搜索字段高亮,则需要添加一个属性:required_field_match=false</p> <p><br></p> </div> </body> </html>
ES6今日学习笔记
<html> <head></head> <body> <div class="content ql-editor"> <p><br></p> <p><br></p> <p><br></p> <h1>作用域</h1> <h2>1.4垃圾回收机制</h2> <h3>内存的生命周期</h3> <p><br></p> <p>JS环境中分配的内存,一般有如下<span style="color: rgb(223, 42, 63);">生命周期</span></p> <ol> <li data-list="bullet"><span class="ql-ui"></span>内存分配:当我们声明变量、函数、对象的时候,系统会自动为他们分配内存1.2.</li> <li data-list="bullet"><span class="ql-ui"></span>内存使用:即读写内存,也就是使用变量、函数等</li> <li data-list="bullet"><span class="ql-ui"></span>内存回收: 使用完毕,由垃圾回收器自动回收不再使用的内存</li> </ol> <p><br></p> <h3>说明:</h3> <p><br></p> <ol> <li data-list="bullet"><span class="ql-ui"></span>全局变量一般不会回收(关闭页面回收)</li> <li data-list="bullet"><span class="ql-ui"></span>一般情况下局部变量的值,不用了,会被自动回收掉</li> </ol> <p><strong>内存泄漏</strong>:程序中分配的内存由于某种原因程序未释放或无法释放叫做内存泄漏</p> <p><br></p> <p><br></p> <h2>拓展-JS垃圾回收机制-算法说明</h2> <p><br></p> <p>下面介绍两种常见的浏览器垃圾回收算法:引用计数去和 标记清除法</p> <p><br></p> <h3>标记清除法</h3> <p><br></p> <p>现代的浏览器已经不再使用引用计数算法了</p> <p>现代浏览器通用的大多是基于标记清除算法的某些改进算法,总体思想都是一致的。</p> <p><br></p> <h4>核心</h4> <p><br></p> <ol> <li data-list="bullet"><span class="ql-ui"></span>标记清除算法将“不再使用的对象”定义为“无法达到的对象".</li> <li data-list="bullet"><span class="ql-ui"></span>就是从根部(在]S中就是全局对象)出发定时扫2.内存中的对象。凡是能从根部到达的对象,都是还需要使用的。</li> <li data-list="bullet"><span class="ql-ui"></span>那些无法由根部出发触及到的对象被标记为不再使用,稍后进行回收</li> </ol> <p><br></p> <h2>1.5 闭包</h2> <p><br></p> <ol> <li data-list="bullet"><span class="ql-ui"></span>目标: 能说出什么是闭包,闭包的作用以及注意事项</li> <li data-list="bullet"><span class="ql-ui"></span>概念:一个函数对周围状态的引用捆绑在一起,内层函数中访问到其外层函数的作用:</li> <li data-list="bullet"><span class="ql-ui"></span>简单理解:<strong>闭包=内层函数 + 外层函数的变量</strong></li> <li data-list="bullet"><span class="ql-ui"></span>作用:封闭数据,提供操作,外部也可以访问函数内部的变量</li> <li data-list="bullet"><span class="ql-ui"></span>闭包应用:实现数据的私有</li> </ol> <p>先看个简单的代码:</p> <div class="ql-code-block-container"> <div class="ql-code-block"> function fn() { </div> <div class="ql-code-block"> var num = 10; </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> function fun() { </div> <div class="ql-code-block"> console.log(num); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> fun(); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> fn(); </div> </div> <p><br></p> <h3>总结</h3> <h4>1.怎么理解闭包?</h4> <p><br></p> <p>> 闭包 = 内层函数 +外层函数的变量</p> <p><br></p> <h3>2.闭包的作用?</h3> <p><br></p> <p>封闭数据,实现数据私有,外部也可以访问函数内部的变量闭包很有用,因为它允许将函数与其所操作的某些数据 (环境)关联起来</p> <p><br></p> <h3>3.闭包可能引起的问题?</h3> <p><br></p> <p>内存泄漏</p> <p><br></p> <p><br></p> <h2>1.6变量提升</h2> <p><br></p> <p>目标:了解什么是变量提升</p> <p>变量提升是javaScript 中比较“奇怪”的现象它允许在变量声明之前即被访问 (仅存在于var声明变量)注意:</p> <p>1.变量在未声明即被访问时会报语法错误</p> <p>2.变量在var声明之前即被访问,变量的值为 undefined</p> <p>3let/const声明的变量不存在变量提升</p> <p>4.变量提升出现在相同作用域当中</p> <p>5.实际开发中推荐先声明再访问变量</p> <p><br></p> <h3>总结</h3> <p><br></p> <p>1.用哪个关键字声明变量会有变量提升?</p> <p>var</p> <p>2.变量提升是什么流程?</p> <p>先把var变量提升到当前作用域于最前面只提升变量声明,不提升变量赋值然后依次执行代码我们不建议使用var声明变量</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>1.把所有var声明的变量提升到 当前作用城的最前面</li> <li data-list="bullet"><span class="ql-ui"></span>2.只提升声明, 不提升赋值</li> </ol> <p><br></p> <h1>函数进阶</h1> <h2>2.1函数提升</h2> <p><br></p> <p>目标:能说出函数提升的过程</p> <p>函数提升与变量提升比较类似,是指函数在声明之前即可被调用</p> <p>知道函数参数默认值、动态参数、剩余参数的使用细节,提升函数应用的灵活度,知道箭头函数的语法及与普通函数的差异</p> <ol> <li data-list="ordered"><span class="ql-ui"></span>1。会把所有函数声明提升到当前作用域的最前面</li> <li data-list="ordered"><span class="ql-ui"></span>2。只提升函数声明,不提升函数调用</li> <li data-list="ordered"><span class="ql-ui"></span>函数表达式 必须先声明和赋值,后调用 否则 报错</li> </ol> <p><br></p> <h3>总结</h3> <p><br></p> <ol> <li data-list="bullet"><span class="ql-ui"></span>1.函数提升能够使函数的声明调用更灵活</li> <li data-list="bullet"><span class="ql-ui"></span>2函数表达式不存在提升的现象</li> <li data-list="bullet"><span class="ql-ui"></span>3.函数提升出现在相同作用域当中</li> </ol> <p><br></p> <p><br></p> <h2>2.2 函数参数</h2> <p><br></p> <p>函数参数的使用细节,能够提升函数应用的灵活度</p> <p>学习路径</p> <p>1.动态参数</p> <p>arguments 是函数内部内置的伪数组变量,它包含了调用函数时传入的所有实参</p> <p><br></p> <h3>总结:</h3> <p><br></p> <ol> <li data-list="bullet" class="ql-indent-1"><span class="ql-ui"></span>1arguments 是一个伪数组,只存在于函数中</li> <li data-list="bullet" class="ql-indent-1"><span class="ql-ui"></span>2.arguments的作用是动态获取函数的实参</li> <li data-list="bullet" class="ql-indent-1"><span class="ql-ui"></span>3.可以通过for循环依次得到传递过来的实参</li> </ol> <p>1.当不确定传递多少个实参的时候,我们怎么办?</p> <p>arguments 动态参数</p> <p>2.arguments是什么?</p> <ol> <li data-list="bullet" class="ql-indent-1"><span class="ql-ui"></span>伪数组</li> <li data-list="bullet" class="ql-indent-1"><span class="ql-ui"></span>它只存在函数中</li> </ol> <p>2. 剩余参数</p> <p>1....是语法符号,置于最末函数形参之前,用于获取多余的实参</p> <p>2.借助 ...获取的剩余实参,是个真数组</p> <p><strong>开发中,提倡使用多使用:剩余参数</strong></p> <p><br></p> <h3>展开运算符</h3> <p><br></p> <p>目标:能够使用展开运算符并说出常用的使用场景展开运算符(...),将一个数组进行展开</p> <p><br></p> <p><br></p> <h2>2.3 箭头函数 (重要)</h2> <p><br></p> <p><strong>目标</strong>:能够熟悉箭头函数不同写法</p> <p><strong>目的</strong>:引入箭头函数的目的是更简短的函数写法并且不绑定this,箭头函数的语法比函数表达式更简洁</p> <p><strong>使用场景</strong>:箭头函数更适用于那些本来需要匿名函数的地方</p> <p>学习路径:</p> <ol> <li data-list="ordered"><span class="ql-ui"></span>基本语法</li> </ol> <p><br></p> <h3>总结</h3> <p><br></p> <p>1.箭头函数属于表达式函数,因此不存在函数提升</p> <p>2.箭头函数只有一个参数时可以省略圆括号 ()</p> <p>3.箭头函数函数体只有一行代码时可以省略花括号#,并自动做为返回值被返回</p> <p>4加括号的函数体返回对象字面量表达式</p> <ol> <li data-list="ordered"><span class="ql-ui"></span>箭头函数参数</li> </ol> <p>普通函数有arguments 动态参数</p> <p>箭头函数没有 arguments 动态参数,但是有 剩余参数 ..args</p> <ol> <li data-list="ordered"><span class="ql-ui"></span>箭头函数this</li> </ol> <p>在箭头函数出现之前,每一个新函数根据它是被如何调用的来定义这个函数的this值,非常令人讨厌箭头函数不会创建自己的this,它只会从自己的作用域链的上一层沿用this。</p> <p>箭头函数不会创建自己的this,它只会从自己的作用域链的上一层沿用this.</p> <p><br></p> <h3>总结</h3> <p><br></p> <p>1.箭头函数里面有this吗?</p> <p>箭头函数不会创建自己的this,它只会从自己的作用域链的上一层沿用this</p> <p>2.DOM事件回调函数推荐使用箭头函数吗?</p> <p>不太推荐,特别是需要用到this的时候</p> <p>事件回调函数使用箭头函数时,this 为全局的 window</p> <p><img src="https://pic.code-nav.cn/planet_post_image/1609216259264610306/sa4jd5sz.jpeg"></p> <p><br></p> <h1>解构赋值</h1> <p><br></p> <ol> <li data-list="bullet"><span class="ql-ui"></span>数组解构</li> </ol> <p>数组解构是将数组的单元值快速批量赋值给一系列变量的简洁语法</p> <p><strong>基本语法:</strong></p> <p>1.赋值运算符 =左侧的用于批量声明变量,右侧数组的单元值将被赋值给左侧的变量2.变量的顺序对应数组单元值的位置依次进行赋值操作</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>对象解构</li> </ol> <p>目标:知道解构的语法及分类,使用解构简洁语法快速为变量赋值</p> <p><br></p> <p>数组解构是将数组的单元值快速批量赋值给一系列变量的简洁语法</p> <p>基本语法:典型应用交互2个变量</p> <p>注意: js 前面必须加分号情况</p> <p>1.立即执行函数</p> <p><img src="https://pic.code-nav.cn/planet_post_image/1609216259264610306/uqw3gj1o.jpeg"></p> <p>2.数组解构</p> <p><img src="https://pic.code-nav.cn/planet_post_image/1609216259264610306/jar21csd.jpeg"></p> <p><br></p> <p><br></p> <p><br></p> <p><br></p> </div> </body> </html>
