asp.net做网站系统,wordpress多类型会员,90后做网站赚了,筑龙网登录一、概述
1.1介绍
The Elastic Stack, 包括 Elasticsearch、Kibana、Beats 和 Logstash#xff08;也称为 ELK Stack#xff09;。 能够安全可靠地获取任何来源、任何格式的数据#xff0c;然后实时地对数据进行搜索、分析和可视 化。Elaticsearch#xff0c;简称为 ES也称为 ELK Stack。 能够安全可靠地获取任何来源、任何格式的数据然后实时地对数据进行搜索、分析和可视 化。Elaticsearch简称为 ESES 是一个开源的高扩展的分布式全文搜索引擎是整个 Elastic Stack 技术栈的核心。它可以近乎实时的存储、检索数据本身扩展性很好可以扩展到上 百台服务器处理 PB 级别的数据。
Elasticsearch是由elastic公司开发的一套搜索引擎技术它是elastic技术栈中的一部分。完整的技术栈包括 Elasticsearch用于数据存储、计算和搜索 Logstash/Beats用于数据收集 Kibana用于数据可视化
整套技术栈被称为ELK经常用来做日志收集、系统监控和状态分析等等 1.2全文搜索引擎
Google百度类的网站搜索它们都是根据网页中的关键字生成索引我们在搜索的时 候输入关键字它们会将该关键字即索引匹配到的所有网页返回还有常见的项目中应用日 志的搜索等等。对于这些非结构化的数据文本关系型数据库搜索不是能很好的支持。全文搜索引擎
全⽂检索 是计算机程序通过扫描⽂章中的 每⼀个词对每⼀个词建⽴⼀个索 引指明该词在⽂章中出现的次数和位置 。当⽤户 查询时根据建⽴的索引查找 类似于通过字典的检索字表查字的过程
索: 建⽴索引 ⽂本----切分 --- 词 ⽂章出现过 出现多少次
检索: 查询 关键词--- 索引中-- 符合条件⽂章 相关度排序
全⽂检索(Full-Text Retrieval)以⽂本作为检索对象找出含有指定词汇的⽂ 本。全⾯、准确和快速是衡量全⽂检索系统的关键指标
只处理⽂本、不处理语义搜索时英⽂不区分⼤⼩写结果列表有相关度排序
1.3搜索引擎软件
Lucene 是 Apache 软件基金会 Jakarta 项目组的一个子项目提供了一个简单却强大的 应用程式接口能够做全文索引和搜寻。在 Java 开发环境里 Lucene 是一个成熟的免费开源 工具。就其本身而言Lucene 是当前以及最近几年最受欢迎的免费 Java 信息检索程序库。 但 Lucene 只是一个提供全文搜索功能类库的核心工具包而真正使用它还需要一个完善的 服务框架搭建起来进行应用。
目前市面上流行的搜索引擎软件主流的就两款Elasticsearch 和 Solr,这两款都是基 于 Lucene 搭建的可以独立部署启动的搜索引擎服务软件。由于内核相同所以两者除了 服务器安装、部署、管理、集群以外对于数据的操作 修改、添加、保存、查询等等都十 分类似。
在使用过程中一般都会将 Elasticsearch 和 Solr 这两个软件对比然后进行选型。这两 个搜索引擎都是流行的先进的的开源搜索引擎。它们都是围绕核心底层搜索库 - Lucene 构建的 - 但它们又是不同的。像所有东西一样每个都有其优点和缺点 1.4Elasticsearch 应用案例 1.5安装部署
ElasticSearch
ElasticSearch 简称 ES 是基于 Apache Lucene 构建的 开源搜索引 擎 是当前最流⾏的 企业级搜索引擎 。 Lucene本身就可以被认为迄今为⽌性 能最好的⼀款开源搜索引擎⼯具包 但是lucene的API相对复杂需要深厚的 搜索理论。很难集成到实际的应⽤中去。 ES是采⽤java语⾔编写提供了简 单易⽤的RestFul API开发者可以使⽤其简单的RestFul API开发相关的搜 索功能从⽽避免lucene的复杂性
docker run -d \--name es \-e ES_JAVA_OPTS-Xms512m -Xmx512m \-e discovery.typesingle-node \-v es-data:/usr/share/elasticsearch/data \-v es-plugins:/usr/share/elasticsearch/plugins \--privileged \--network hm-net \-p 8200:9200 \-p 8300:9300 \elasticsearch:7.12.1
注意这里我们采用的是elasticsearch的7.12.1版本由于8以上版本的JavaAPI变化很大在企业中应用并不广泛企业中应用较多的还是8以下的版本。
进入容器内部
sudo docker exec -it c4af11a6c1a5 /bin/bashKibana
Kibana Navicat 是⼀个针对 Elasticsearch mysql 的 开源分析及可视化 平台 使⽤Kibana可以 查询、查看并与存储在ES索引的数据进⾏交互操作 使 ⽤Kibana能执⾏⾼级的 数据分析并能以图表、表格和地图的形式查看数据
docker run -d \
--name kibana \
-e ELASTICSEARCH_HOSTShttp://es:8200 \
-p 8601:5601 \
kibana:7.12.1
安装完成后直接访问5601端口即可看到控制台页面 选择Explore on my own之后进入主页面 然后选中Dev tools进入开发工具页面 1.6核心概念
索引
⼀个索引就是⼀个拥有⼏分相似特征的⽂档的集合 。⽐如说你可以有⼀个 商品数据的索引⼀个订单数据的索引还有⼀个⽤户数据的索引。 ⼀个 索引由⼀个名字来标识 (必须全部是⼩写字⺟的) 并且当我们要对这个索引 中的⽂档进⾏索引、搜索、更新和删除的时候都要使⽤到这个名字
随着业务发展需要在es中存储的文档也会越来越多比如有商品的文档、用户的文档、订单文档等等 所有文档都散乱存放显然非常混乱也不方便管理。
因此我们要将类型相同的文档集中在一起管理称为索引Index。例如 映射
映射是定义⼀个⽂档和它所包含的字段如何被存储和索引的过程 。在默认配 置下ES可以根据插⼊的数据 ⾃动地创建mapping也可以⼿动创建 mapping 。 mapping中主要包括字段名、字段类型等
数据库的表会有约束信息用来定义表的结构、字段的名称、类型等信息。因此索引库中就有映射mapping是索引中文档的字段约束信息类似表的结构约束。
⽂档
⽂档是索引中存储的⼀条条数据。⼀条⽂档是⼀个可被索引的最⼩单元 。ES 中的⽂档采⽤了轻量级的JSON格式数据来表示。
elasticsearch是面向文档Document存储的可以是数据库中的一条商品数据一个订单信息。文档数据会被序列化为json格式后存储在elasticsearch中 因此原本数据库中的一行数据就是ES中的一个JSON文档而数据库中每行数据都包含很多列这些列就转换为JSON文档中的字段Field。
mysql与elasticsearch MySQL Elasticsearch 说明 Table Index 索引(index)就是文档的集合类似数据库的表(table) Row Document 文档Document就是一条条的数据类似数据库中的行Row文档都是JSON格式 Column Field 字段Field就是JSON文档中的字段类似数据库中的列Column Schema Mapping Mapping映射是索引中文档的约束例如字段类型约束。类似数据库的表结构Schema SQL DSL DSL是elasticsearch提供的JSON风格的请求语句用来操作elasticsearch实现CRUD 那是不是说我们学习了elasticsearch就不再需要mysql了呢
并不是如此两者各自有自己的擅长之处 Mysql擅长事务类型操作可以确保数据的安全和一致性 Elasticsearch擅长海量数据的搜索、分析、计算
因此在企业中往往是两者结合使用 对安全性要求较高的写操作使用mysql实现 对查询性能要求较高的搜索需求使用elasticsearch实现 两者再基于某种方式实现数据的同步保证一致性
1.7倒排索引
elasticsearch之所以有如此高性能的搜索表现正是得益于底层的倒排索引技术。那么什么是倒排索引呢
倒排索引的概念是基于MySQL这样的正向索引而言的。
正向索引
我们先来回顾一下正向索引。
例如有一张名为tb_goods的表 id title price 1 小米手机 3499 2 华为手机 4999 3 华为小米充电器 49 4 小米手环 49 ... ... ...
其中的id字段已经创建了索引由于索引底层采用了B树结构因此我们根据id搜索的速度会非常快。但是其他字段例如title只在叶子节点上存在。
因此要根据title搜索的时候只能遍历树中的每一个叶子节点判断title数据是否符合要求。
比如用户的SQL语句为
select * from tb_goods where title like %手机%; 综上根据id精确匹配时可以走索引查询效率较高。而当搜索条件为模糊匹配时由于索引无法生效导致从索引查询退化为全表扫描效率很差。
因此正向索引适合于根据索引字段的精确搜索不适合基于部分词条的模糊匹配。
而倒排索引恰好解决的就是根据部分词条模糊匹配的问题。
倒排索引
倒排索引中有两个非常重要的概念 文档Document用来搜索的数据其中的每一条数据就是一个文档。例如一个网页、一个商品信息 词条Term对文档数据或用户搜索数据利用某种算法分词得到的具备含义的词语就是词条。例如我是中国人就可以分为我、是、中国人、中国、国人这样的几个词条
创建倒排索引是对正向索引的一种特殊处理和应用流程如下 将每一个文档的数据利用分词算法根据语义拆分得到一个个词条 创建表每行数据包括词条、词条所在文档id、位置等信息 因为词条唯一性可以给词条创建正向索引
此时形成的这张以词条为索引的表就是倒排索引表两者对比如下 倒排索引的搜索流程如下以搜索华为手机为例如图 总结
正向索引是最传统的根据id索引的方式。但根据词条查询时必须先逐条获取每个文档然后判断文档中是否包含所需要的词条是根据文档找词条的过程。
而倒排索引则相反是先找到用户要搜索的词条根据词条得到保护词条的文档的id然后根据id获取文档。是根据词条找文档的过程。 正向索引 优点 可以给多个字段创建索引 根据索引字段搜索、排序速度非常快 缺点 根据非索引字段或者索引字段中的部分词条查找时只能全表扫描。 倒排索引 优点 根据词条搜索、模糊搜索时速度非常快 缺点 只能给词条创建索引而不是字段 无法根据字段做排序 1.8分词器
Analysis 和 Analyzer
Analysis ⽂本分析是把全⽂本转换⼀系列单词(term/token)的过程也 叫分词(Analyzer)。Analysis是通过Analyzer来实现的。 分词就是将⽂档通过 Analyzer分成⼀个⼀个的Term(关键词查询),每⼀个Term都指向包含这个Term 的⽂档
Analyzer 组成 注意: 在ES中默认使⽤标准分词器: StandardAnalyzer 特点: 中⽂单字分 词 单词分词 我是中国⼈ this is good man---- analyzer---- 我 是 中 国 ⼈ this is good man
Analyzer 组成
注意: 在ES中默认使⽤标准分词器: StandardAnalyzer 特点: 中⽂单字分 词 单词分词
我是中国⼈ this is good man---- analyzer---- 我 是 中 国 ⼈ this is good man 分析器analyzer都由三种构件组成的 character filters tokenizers token filters 。 注意:
三者顺序: Character Filters---Tokenizer---Token Filter三者个数Character Filters0个或多个 Tokenizer Token Filters(0 个或多个
内置分词器
Standard Analyzer - 默认分词器英⽂按单词词切分并⼩写处理Simple Analyzer - 按照单词切分(符号被过滤), ⼩写处理Stop Analyzer - ⼩写处理停⽤词过滤(the,a,is)Whitespace Analyzer - 按照空格切分不转⼩写Keyword Analyzer - 不分词直接将输⼊当作输出
内置分词器测试
标准分词器
特点: 按照单词分词 英⽂统⼀转为⼩写 过滤标点符号 中⽂单字分词
POST /_analyze
{analyzer: standard,text: this is a , good Man 中华⼈⺠共和国
}
Simple 分词器
特点: 英⽂按照单词分词 英⽂统⼀转为⼩写 去掉符号 中⽂按照空格 进⾏分词
POST /_analyze
{analyzer: simple,text: this is a , good Man 中华⼈⺠共和国
}Whitespace 分词器
特点: 中⽂ 英⽂ 按照空格分词 英⽂不会转为⼩写 不去掉标点符号
POST /_analyze
{analyzer: whitespace,text: this is a , good Man
}
创建索引设置分词
PUT /索引名
{settings: {},mappings: {properties: {title:{type: text,analyzer: standard //显示指定分词器}}}
}中⽂分词器
在ES中⽀持中⽂分词器⾮常多 如 smartCN、IK 等推荐的就是 IK分词 器 1.9IK分词器
docker exec -it es ./bin/elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.12.1/elasticsearch-analysis-ik-7.12.1.zip
然后重启es容器
docker restart es
IK分词器包含两种模式 ik_smart智能语义切分 ik_max_word最细粒度切分
POST /_analyze
{analyzer: ik_smart,text: 程序员学习java太棒了
}
拓展词典
随着互联网的发展“造词运动”也越发的频繁。出现了很多新的词语在原有的词汇列表中并不存在。比如“泰裤辣”“传智播客” 等。
IK分词器如何拓展词条如何停用词条 利用config目录的IkAnalyzer.cfg.xml文件添加拓展词典和停用词典 在词典中添加拓展词条或者停用词条 1. 修改vim IKAnalyzer.cfg.xml?xml version1.0 encodingUTF-8?!DOCTYPE properties SYSTEM
http://java.sun.com/dtd/properties.dtdpropertiescommentIK Analyzer 扩展配置/comment!--⽤户可以在这⾥配置⾃⼰的扩展字典 --entry keyext_dictext_dict.dic/entry!--⽤户可以在这⾥配置⾃⼰的扩展停⽌词字典--entry keyext_stopwordsext_stopword.dic/entry/properties
2. 在ik分词器⽬录下config⽬录中创建ext_dict.dic⽂件 编码⼀定要为
UTF-8才能⽣效vim ext_dict.dic 加⼊扩展词即可
3. 在ik分词器⽬录下config⽬录中创建ext_stopword.dic⽂件vim ext_stopword.dic 加⼊停⽤词即可
4.重启es⽣效二、快速入门
2.1索引库操作
Index就类似数据库表Mapping映射就类似表的结构。我们要向es中存储数据必须先创建Index和Mapping
Mapping是对索引库中文档的约束常见的Mapping属性包括 type字段数据类型常见的简单类型有 字符串text可分词的文本、keyword精确值例如品牌、国家、ip地址 数值long、integer、short、byte、double、float、 布尔boolean 日期date 对象object index是否创建索引默认为true analyzer使用哪种分词器 properties该字段的子字段 store是否将数据进行独立存储默认为 false 原始的文本会存储在_source 里面默认情况下其他提取出来的字段都不是独立存储 的是从_source 里面提取出来的。当然你也可以独立的存储某个字段只要设置 store: true 即可获取独立存储的字段要比从_source 中解析快得多但是也会占用 更多的空间所以要根据实际业务需求来设置。 说明: ES中⽀持字段类型⾮常丰富如text、keyword、integer、 long、ip 等。更多参⻅https://www.elastic.co/guide/en/elasticsearch/re ference/7.15/mapping-types.html 2.2索引库的CRUD
创建索引库和映射
基本语法 请求方式PUT 请求路径/索引库名可以自定义 请求参数mapping映射
格式
PUT /索引库名称
{mappings: {properties: {字段名:{type: text,analyzer: ik_smart},字段名2:{type: keyword,index: false},字段名3:{properties: {子字段: {type: keyword}}},// ...略}}
}
示例
PUT /yanyu
{mappings: {properties: {info:{type: text,analyzer: ik_smart},email:{type: keyword,index: false},name:{properties: {firstName: {type: keyword}}}}}
}
# 1.创建索引
- PUT /索引名 PUT /products
- 注意:1.ES中索引健康转态 red(索引不可⽤) 、yellwo(索引可⽤,存在⻛险)、
green(健康)2.默认ES在创建索引时回为索引创建1个备份索引和⼀个primary索引# 2.创建索引 进⾏索引分⽚配置
- PUT /products
{settings: {number_of_shards: 1, #指定主分⽚的数量number_of_replicas: 0 #指定副本分⽚的数量}
}查询索引库
基本语法 请求方式GET 请求路径/索引库名 请求参数无
格式
GET /索引库名
示例
GET /yanyu
查看所有索引
GET /_cat/indices?v 修改索引库
倒排索引结构虽然不复杂但是一旦数据结构改变比如改变了分词器就需要重新创建倒排索引这简直是灾难。因此索引库一旦创建无法修改mapping。
虽然无法修改mapping中已有的字段但是却允许添加新的字段到mapping中因为不会对倒排索引产生影响。因此修改索引库能做的就是向索引库中添加新字段或者更新索引库的基础属性。
语法说明
PUT /索引库名/_mapping
{properties: {新字段名:{type: integer}}
}
示例
PUT /yanyu/_mapping
{properties: {age:{type: integer}}
}
删除索引库
语法 请求方式DELETE 请求路径/索引库名 请求参数无
格式
DELETE /索引库名
示例
DELETE /heima
2.3映射
查询
# 1.查看某个索引的映射
- GET /索引名/_mapping GET /products/_mapping2.4文档操作
有了索引库接下来就可以向索引库中添加数据了。
Elasticsearch中的数据其实就是JSON风格的文档。操作文档自然保护增、删、改、查等几种常见操作我们分别来学习。
新增文档
语法
POST /索引库名/_doc/文档id
{字段1: 值1,字段2: 值2,字段3: {子属性1: 值3,子属性2: 值4},
}
示例
POST /yanyu/_doc/1
{info: 讲师,email: zyitcast.cn,name: {firstName: 云,lastName: 赵}
}
注意
POST /products/_doc/ #⾃动⽣成⽂档id
{title:iphone14,price:8999.99,created_at:2021-09-15,description:iPhone 13屏幕采⽤6.8英⼨OLED屏幕
}查询文档
根据rest风格新增是post查询应该是get不过查询一般都需要条件这里我们把文档id带上。
语法
GET /{索引库名称}/_doc/{id}
示例
GET /yanyu/_doc/1
查询所有文档
在 Postman 中向 ES 服务器发 GET 请求 http://127.0.0.1:9200/student/_search
{query: {match_all: {}}
}
# query这里的 query 代表一个查询对象里面可以有不同的查询属性
# match_all查询类型例如match_all(代表查询所有) matchterm range 等等
# {查询条件}查询条件会根据类型的不同写法也有差异
GET /yanyu/_search
删除文档
删除使用DELETE请求同样需要根据id进行删除
删除一个文档不会立即从磁盘上移除它只是被标记成已删除逻辑删除。
语法
DELETE /{索引库名}/_doc/id值
示例
DELETE /yanyu/_doc/1
条件删除文档
一般删除数据都是根据文档的唯一性标识进行删除实际操作时也可以根据条件对多条数 据进行删除
向 ES 服务器发 POST 请求
POST /yanyu/_delete_by_query
{query: {match: {name.firstName: 云222}}
}
修改文档
修改有两种方式 全量修改直接覆盖原来的文档 局部修改修改文档中的部分字段
全量修改
全量修改是覆盖原来的文档其本质是两步操作 根据指定的id删除文档 新增一个相同id的文档
注意如果根据id删除时id不存在第二步的新增也会执行也就从修改变成了新增操作了。
语法
PUT /{索引库名}/_doc/文档id
{字段1: 值1,字段2: 值2,// ... 略
}
示例
PUT /heima/_doc/1
{info: Java讲师,email: zyitcast.cn,name: {firstName: 云,lastName: 赵}
}
局部修改
局部修改是只修改指定id匹配的文档中的部分字段。
语法
POST /{索引库名}/_update/文档id
{doc: {字段名: 新的值,}
}
示例
POST /yanyu/_update/1
{doc: {email: ZhaoYunitcast.cn}
}
批处理
批处理采用POST请求基本语法如下
POST _bulk
{ index : { _index : test, _id : 1 } }
{ field1 : value1 }
{ delete : { _index : test, _id : 2 } }
{ create : { _index : test, _id : 3 } }
{ field1 : value3 }
{ update : {_id : 1, _index : test} }
{ doc : {field2 : value2} }
其中 index代表新增操作 _index指定索引库名 _id指定要操作的文档id { field1 : value1 }则是要新增的文档内容 delete代表删除操作 _index指定索引库名 _id指定要操作的文档id update代表更新操作 _index指定索引库名 _id指定要操作的文档id { doc : {field2 : value2} }要更新的文档字段
示例批量新增
POST /_bulk
{index: {_index:heima, _id: 3}}
{info: 黑马程序员C讲师, email: wwitcast.cn, name:{firstName: 五, lastName:王}}
{index: {_index:heima, _id: 4}}
{info: 黑马程序员前端讲师, email: zhangsanitcast.cn, name:{firstName: 三, lastName:张}}
批量删除
POST /_bulk
{delete:{_index:heima, _id: 3}}
{delete:{_index:heima, _id: 4}} 说明:批量时不会因为⼀个失败⽽全部失败,⽽是继续执⾏后续操作,在返 回时按照执⾏的状态返回 2.5⾼级查询DSL
ES中提供了⼀种强⼤的检索数据⽅式,这种检索⽅式称之为 Query DSL , Query DSL 是利⽤ Rest API传递JSON格式的请求体(Request Body)数据 与ES进⾏交互这种⽅式的 丰富查询语法 让ES检索变得 更强⼤更简洁
Elasticsearch的查询可以分为两大类 叶子查询Leaf query clauses一般是在特定的字段里查询特定值属于简单查询很少单独使用。 复合查询Compound query clauses以逻辑方式组合多个叶子查询或者更改叶子查询的行为方式。
快速入门
我们依然在Kibana的DevTools中学习查询的DSL语法。首先来看查询的语法结构
GET /{索引库名}/_search
{query: {查询类型: {// .. 查询条件}}
}
说明 GET /{索引库名}/_search其中的_search是固定路径不能修改
GET /items/_search
{query: {match_all: {}}
} 你会发现虽然是match_all但是响应结果中并不会包含索引库中的所有文档而是仅有10条。这是因为处于安全考虑elasticsearch设置了默认的查询页数。
叶子查询
叶子查询的类型也可以做进一步细分详情大家可以查看官方文档
https://www.elastic.co/guide/en/elasticsearch/reference/7.12/query-dsl.html
如图 这里列举一些常见的例如 全文检索查询Full Text Queries利用分词器对用户输入搜索条件先分词得到词条然后再利用倒排索引搜索词条。例如 match multi_match 精确查询Term-level queries不对用户输入搜索条件分词根据字段内容精确值匹配。但只能查找keyword、数值、日期、boolean类型的字段。例如 ids term range 地理坐标查询用于搜索地理位置搜索方式很多例如 geo_bounding_box按矩形搜索 geo_distance按点和半径搜索
--------------------------------------
全文检索查询
match查询
GET /{索引库名}/_search
{query: {match: {字段名: 搜索条件}}
}
GET /yanyu/_search
{query: {match: {name.firstName: 云2}}
}多字段查询[multi_match]
与match类似的还有multi_match区别在于可以同时对多个字段搜索而且多个字段都要满足语法示例
GET /{索引库名}/_search
{query: {multi_match: {query: 搜索条件,fields: [字段1, 字段2]}}
} 查询所有[match_all]
match_all关键字: 返回索引中的全部⽂档
GET /products/_search
{query: {match_all: {}}
}
-------------------------------------
精确查询
精确查询英文是Term-level query顾名思义词条级别的查询。也就是说不会对用户输入的搜索条件再分词而是作为一个词条与搜索的字段内容精确值匹配。因此推荐查找keyword、数值、日期、boolean类型的字段。例如 id price 城市
详情可以查看官方文档
https://www.elastic.co/guide/en/elasticsearch/reference/7.12/term-level-queries.html
term查询
以term查询为例其语法如下
GET /{索引库名}/_search
{query: {term: {字段名: {value: 搜索条件}}}
} NOTE1: 通过使⽤term查询得知ES中默认使⽤分词器为 标准分词器 (StandardAnalyzer),标准分词器对于英⽂单词分词,对于中⽂单字分词 NOTE2: 通过使⽤term查询得知, 在ES的Mapping Type 中 keyword , date ,integer, long , double , boolean or ip 这些类型不分 词只有text类型分词 当你输入的搜索条件不是词条而是短语时由于不做分词你反而搜索不到 范围查询[range]
GET /{索引库名}/_search
{query: {range: {字段名: {gte: {最小值},lte: {最大值}}}}
}
range是范围查询对于范围筛选的关键字有 gte大于等于 gt大于 lte小于等于 lt小于
前缀查询[prefix]
prefix 关键字: ⽤来检索含有指定前缀的关键词的相关⽂档
GET /products/_search
{query: {prefix: {title: {value: ipho}}}
}通配符查询[wildcard]
wildcard 关键字: 通配符查询 ? ⽤来匹配⼀个任意字符 * ⽤来匹配多个任意字符
GET /products/_search
{query: {wildcard: {description: {value: iphon*}}}
}
多id查询[ids]
ids 关键字 : 值为数组类型,⽤来根据⼀组id获取多个对应的⽂档
GET /products/_search
{query: {ids: {values:
[verUq3wBOTjuBizqAegi,vurUq3wBOTjuBizqAegk]}}
}
模糊查询[fuzzy]
fuzzy 关键字: ⽤来模糊查询含有指定关键字的⽂档 返回包含与搜索字词相似的字词的文档。 编辑距离是将一个术语转换为另一个术语所需的一个字符更改的次数。这些更改可以包括 更改字符box → fox 删除字符black → lack 插入字符sic → sick 转置两个相邻字符act → cat 为了找到相似的术语fuzzy 查询会在指定的编辑距离内创建一组搜索词的所有可能的变体 或扩展。然后查询返回每个扩展的完全匹配。 通过 fuzziness 修改编辑距离。一般使用默认值 AUTO根据术语的长度生成编辑距离。 {query: {fuzzy: {title: {value: zhangsan,
fuzziness: 2}}}
}GET /products/_search
{query: {fuzzy: {description: iphooone}}
} 因此该查询的含义是在products索引中搜索描述字段description包与iphooone相似的文档。 { id: 1, name: iPhone X, description: The latest iPhone model with Face ID } 默认字段分词查询[query_string]
GET /products/_search
{query: {query_string: {default_field: description,query: 屏幕真的⾮常不错}}
}
注意: 查询字段分词就将查询条件分词查询 查询字段不分词将查询条件不分词查询 假设在products索引中有以下文档 { description: 这款手机的屏幕真的非常出色色彩鲜艳且清晰。, price: 5000 }{ description: 这台电脑的屏幕质量非常好分辨率高色彩还原度高。, price: 8000 }{ description: 这台电视的屏幕效果非常棒画质细腻色彩饱满。, price: 12000 }{ description: 这款相机的屏幕显示效果不错可以实时预览拍摄画面。, price: 3000 } ---------------------------------------
.复合查询
复合查询大致可以分为两类 第一类基于逻辑运算组合叶子查询实现组合条件例如 bool 第二类基于某种算法修改查询时的文档相关性算分从而改变文档排名。例如 function_score dis_max
其它复合查询及相关语法可以参考官方文档
https://www.elastic.co/guide/en/elasticsearch/reference/7.12/compound-queries.html
---------------------------------------
布尔查询[bool]
bool查询即布尔查询。就是利用逻辑运算来组合一个或多个查询子句的组合。bool查询支持的逻辑运算有 must必须匹配每个子查询类似“与” should选择性匹配子查询类似“或” must_not必须不匹配不参与算分类似“非” filter必须匹配不参与算分
bool查询的语法如下
GET /items/_search
{query: {bool: {must: [{match: {name: 手机}}],should: [{term: {brand: { value: vivo }}},{term: {brand: { value: 小米 }}}],must_not: [{range: {price: {gte: 2500}}}],filter: [{range: {price: {lte: 1000}}}]}}
}
过滤查询
过滤查询其实准确来说ES中的查询操作分为2种: 查询(query) 和 过滤 (filter) 。查询即是之前提到的 query查询 它 (查询)默认会计算每个返 回⽂档的得分然后根据得分排序。⽽ 过滤(filter) 只会筛选出符合的⽂ 档并不计算 得分⽽且它可以缓存⽂档 。所以单从性能考虑过滤⽐ 查询更快。 换句话说 过滤适合在⼤范围筛选数据⽽查询则适合精确匹配数 据。⼀般应⽤时 应先使⽤过滤操作过滤数据 然后使⽤查询匹配数据。 GET /ems/emp/_search
{query: {bool: {must: [{match_all: {}} //查询条件],filter: {....} //过滤条件}
}注意:
在执⾏ filter 和 query 时,先执⾏ filter 在执⾏query Elasticsearch会⾃动缓存经常使⽤的过滤器以加快性能。
常⻅过滤类型有: term 、 terms 、ranage、exists、ids等filter。
GET /ems/emp/_search # 使用term过滤
{query: {bool: {must: [{term: {name: {value: ⼩⿊}}}],filter: {term: {content: 框架}}}}
}GET /dangdang/book/_search #使用terms过滤
{query: {bool: {must: [{term: {name: {value: 中国}}}],filter: {terms: {content: [科技,声⾳]}}}}
} 算分函数查询
当我们利用match查询时文档结果会根据与搜索词条的关联度打分_score返回结果时按照分值降序排列。
例如我们搜索 手机结果如下 从elasticsearch5.1开始采用的相关性打分算法是BM25算法公式如下 基于这套公式就可以判断出某个文档与用户搜索的关键字之间的关联度还是比较准确的。但是在实际业务需求中常常会有竞价排名的功能。不是相关度越高排名越靠前而是掏的钱多的排名靠前。 示例给IPhone这个品牌的手机算分提高十倍分析如下 过滤条件品牌必须为IPhone 算分函数常量weight值为10 算分模式相乘multiply
对应代码如下
GET /hotel/_search
{query: {function_score: {query: { .... }, // 原始查询可以是任意条件functions: [ // 算分函数{filter: { // 满足的条件品牌必须是Iphoneterm: {brand: Iphone}},weight: 10 // 算分权重为2}],boost_mode: multipy // 加权模式求乘积}}
} 2.6排序和分页
排序
elasticsearch默认是根据相关度算分_score来排序但是也支持自定义方式对搜索结果排序。不过分词字段无法排序能参与排序字段类型有keyword类型、数值类型、地理坐标类型、日期类型等。
详细说明可以参考官方文档
https://www.elastic.co/guide/en/elasticsearch/reference/7.12/sort-search-results.html
语法说明
GET /indexName/_search
{query: {match_all: {}},sort: [{排序字段: {order: 排序方式asc和desc}}]
}
GET /items/_search
{query: {match_all: {}},sort: [{price: {order: desc}}]
}
返回指定字段[_source]
GET /products/_search
{query: {match_all: {}},_source: [title,description]
}过滤字段
我们也可以通过
includes来指定想要显示的字段
excludes来指定不想要显示的字段
{_source: {includes: [name,nickname]}, query: {terms: {nickname: [zhangsan]}}
}多字段排序
{query: {match_all: {}},sort: [{age: {order: desc}},{_score:{order: desc}}]
}分页
返回指定条数[size】
GET /products/_search
{query: {match_all: {}},size: 5
} 深度分页
elasticsearch的数据一般会采用分片存储也就是把一个索引中的数据分成N份存储到不同节点上。这种存储方式比较有利于数据扩展但给分页带来了一些麻烦。
比如一个索引库中有100000条数据分别存储到4个分片每个分片25000条数据。现在每页查询10条查询第99页。那么分页查询的条件如下
GET /items/_search
{from: 990, // 从第990条开始查询size: 10, // 每页查询10条sort: [{price: asc}]
}
从语句来分析要查询第990~1000名的数据。
从实现思路来分析肯定是将所有数据排序找出前1000名截取其中的990~1000的部分。但问题来了我们如何才能找到所有数据中的前1000名呢
要知道每一片的数据都不一样第1片上的第900~1000在另1个节点上并不一定依然是900~1000名。所以我们只能在每一个分片上都找出排名前1000的数据然后汇总到一起重新排序才能找出整个索引库中真正的前1000名此时截取990~1000的数据即可。
如图 试想一下假如我们现在要查询的是第999页数据呢是不是要找第9990~10000的数据那岂不是需要把每个分片中的前10000名数据都查询出来汇总在一起在内存中排序如果查询的分页深度更深呢需要一次检索的数据岂不是更多
由此可知当查询分页深度较大时汇总数据过多对内存和CPU会产生非常大的压力。
因此elasticsearch会禁止from size 超过10000的请求。
针对深度分页elasticsearch提供了两种解决方案 search after分页时需要排序原理是从上一次的排序值开始查询下一页数据。官方推荐使用的方式。 scroll原理将排序后的文档id形成快照保存下来基于快照做分页。官方已经不推荐使用。 详情见文档
https://www.elastic.co/guide/en/elasticsearch/reference/7.12/paginate-search-results.html 总结 大多数情况下我们采用普通分页就可以了。查看百度、京东等网站会发现其分页都有限制。例如百度最多支持77页每页不足20条。京东最多100页每页最多60条。 因此一般我们采用限制分页深度的方式即可无需实现深度分页。 2.7.高亮
在进行关键字搜索时搜索出的内容中的关键字会显示不同的颜色称之为高亮。
css样式肯定是前端实现页面的时候写好的但是前端编写页面的时候是不知道页面要展示什么数据的不可能给数据加标签。而服务端实现搜索功能要是有elasticsearch做分词搜索是知道哪些词条需要高亮的。
因此词条的高亮标签肯定是由服务端提供数据的时候已经加上的。 因此实现高亮的思路就是 用户输入搜索关键字搜索数据 服务端根据搜索关键字到elasticsearch搜索并给搜索结果中的关键字词条添加html标签 前端提前给约定好的html标签添加CSS样式
GET /{索引库名}/_search
{query: {match: {搜索字段: 搜索关键字}},highlight: {fields: {高亮字段名称: {pre_tags: em,post_tags: /em}}}
} 注意 搜索必须有查询条件而且是全文检索类型的查询条件例如match 参与高亮的字段必须是text类型的字段 默认情况下参与高亮的字段要与搜索字段一致除非添加required_field_matchfalse GET /products/_search
{query: {term: {description: {value: iphone}}},highlight: {fields: {*:{}}}
}表示对所有字段进行高亮显示。 GET /products/_search
{query: {term: {description: {value: iphone}}},highlight: {post_tags: [/span],pre_tags: [span stylecolor:red],fields: {*:{}}}
} post_tags: []表示在高亮显示的文本后面添加一个结束标签即。 pre_tags: []表示在高亮显示的文本前面添加一个开始标签即。 这两个标签用于将高亮显示的文本包裹起来以便在显示时能够以红色字体突出显示。 多字段⾼亮 使⽤ require_field_match 开启多个字段⾼亮
GET /products/_search
{query: {term: {description: {value: iphone}}},highlight: {require_field_match: false,post_tags: [/span],pre_tags: [span stylecolor:red],
返回指定条数[size]
size 关键字: 指定查询结果中返回指定条数。 默认返回值10条
分⻚查询[form]
from 关键字: ⽤来指定起始返回位置和size关键字连⽤可实现分⻚效
果fields: {*:{}}}
} 2.8聚合aggregations
聚合英⽂为Aggregation是es除搜索功能外提供的针对es数据做统计分 析的功能。聚合有助于根据搜索查询提供聚合数据。聚合查询是数据库中 重要的功能特性ES作为搜索引擎兼数据库同样提供了强⼤的聚合分析 能⼒。它基于查询条件来对数据进⾏分桶、计算的⽅法。有点类似于 SQL 中的 group by 再加⼀些函数⽅法的操作
聚合aggregations可以让我们极其方便的实现对数据的统计、分析、运算。例如 什么品牌的手机最受欢迎 这些手机的平均价格、最高价格、最低价格 这些手机每月的销售情况如何
实现这些统计功能的比数据库的sql要方便的多而且查询速度非常快可以实现近实时搜索效果。
官方文档
https://www.elastic.co/guide/en/elasticsearch/reference/7.12/search-aggregations.html 注意参加聚合的字段必须是keyword、日期、数值、布尔类型 注意事项text类型是不⽀持聚合的 测试数据
# 创建索引 index 和映射 mapping
PUT /fruit
{mappings: {properties: {title:{type: keyword},price:{type:double},description:{type: text,analyzer: ik_max_word}}}
}
# 放⼊测试数据
PUT /fruit/_bulk
{index:{}}{title : ⾯包,price : 19.9,description : ⼩⾯包⾮常好
吃}
{index:{}}{title : 旺仔⽜奶,price : 29.9,description : ⾮常好
喝}
{index:{}}{title : ⽇本⾖,price : 19.9,description : ⽇本⾖⾮常
好吃}
{index:{}}
使⽤
根据某个字段分组{title : ⼩馒头,price : 19.9,description : ⼩馒头⾮常
好吃}
{index:{}}{title : ⼤辣⽚,price : 39.9,description : ⼤辣⽚⾮常
好吃}
{index:{}}{title : 透⼼凉,price : 9.9,description : 透⼼凉⾮常好
喝}
{index:{}}{title : ⼩浣熊,price : 19.9,description : 童年的味
道}
{index:{}}{title : 海苔,price : 19.9,description : 海的味道}
Bucket聚合
例如我们要统计所有商品中共有哪些商品分类其实就是以分类category字段对数据分组。category值一样的放在同一组属于Bucket聚合中的Term聚合。
基本语法如下
GET /items/_search
{size: 0, aggs: {category_agg: {terms: {field: category,size: 20}}}
}
# 根据某个字段进⾏分组 统计数量
GET /fruit/_search
{query: {term: {description: {value: 好吃}}},aggs: {price_group: {terms: {field: price}
求最⼤值
求最⼩值}}
}
带条件聚合
默认情况下Bucket聚合是对索引库的所有文档做聚合例如我们统计商品中所有的品牌结果如下
但真实场景下用户会输入搜索条件因此聚合必须是对搜索结果聚合。那么聚合必须添加限定条件。
例如我想知道价格高于3000元的手机品牌有哪些该怎么统计呢
我们需要从需求中分析出搜索查询的条件和聚合的目标 搜索查询条件 价格高于3000 必须是手机 聚合目标统计的是品牌肯定是对brand字段做term聚合
语法如下
GET /items/_search
{query: {bool: {filter: [{term: {category: 手机}},{range: {price: {gte: 300000}}}]}}, size: 0, aggs: {brand_agg: {terms: {field: brand,size: 20}}}
}
Metric聚合
我们统计了价格高于3000的手机品牌形成了一个个桶。现在我们需要对桶内的商品做运算获取每个品牌价格的最小值、最大值、平均值。
这就要用到Metric聚合了例如stat聚合就可以同时获取min、max、avg等结果。 求最⼤值
# 求最⼤值
GET /fruit/_search
{aggs: {price_max: {max: {field: price}}}
}
# 求平均值
GET /fruit/_search
{aggs: {price_agv: {avg: {field: price}}}
}
# 求和
GET /fruit/_search
{aggs: {price_sum: {sum: {field: price}}}
}
三、Java实战
3.1RestAPI
初始化RestClient
ES官方提供了各种不同语言的客户端用来操作ES。这些客户端的本质就是组装DSL语句通过http请求发送给ES。
官方文档地址
Elasticsearch Clients | Elastic
由于ES目前最新版本是8.8提供了全新版本的客户端老版本的客户端已经被标记为过时。而我们采用的是7.12版本因此只能使用老版本客户端RestAPI propertiesmaven.compiler.source11/maven.compiler.sourcemaven.compiler.target11/maven.compiler.targetelasticsearch.version7.12.1/elasticsearch.version/properties
dependencygroupIdorg.elasticsearch.client/groupIdartifactIdelasticsearch-rest-high-level-client/artifactId
/dependency
测试
public class IndexTest {private RestHighLevelClient client;BeforeEachvoid setUp() {this.client new RestHighLevelClient(RestClient.builder(HttpHost.create(http://192.168.150.101:9200)));}Testvoid testConnect() {System.out.println(client);}AfterEachvoid tearDown() throws IOException {this.client.close();}
}
创建索引库
由于要实现对商品搜索所以我们需要将商品添加到Elasticsearch中不过需要根据搜索业务的需求来设定索引库结构而不是一股脑的把MySQL数据写入Elasticsearch. 创建索引
import org.elasticsearch.client.indices.CreateIndexRequest;
Test
void testCreateIndex() throws IOException {// 1.创建Request对象CreateIndexRequest request new CreateIndexRequest(items);// 2.准备请求参数request.source(MAPPING_TEMPLATE, XContentType.JSON);// 3.发送请求client.indices().create(request, RequestOptions.DEFAULT);
}static final String MAPPING_TEMPLATE {\n \mappings\: {\n \properties\: {\n \id\: {\n \type\: \keyword\\n },\n \name\:{\n \type\: \text\,\n \analyzer\: \ik_max_word\\n },\n \price\:{\n \type\: \integer\\n },\n \stock\:{\n \type\: \integer\\n },\n \image\:{\n \type\: \keyword\,\n \index\: false\n },\n \category\:{\n \type\: \keyword\\n },\n \brand\:{\n \type\: \keyword\\n },\n \sold\:{\n \type\: \integer\\n },\n \commentCount\:{\n \type\: \integer\\n },\n \isAD\:{\n \type\: \boolean\\n },\n \updateTime\:{\n \type\: \date\\n }\n }\n }\n };
判断索引库是否存在
Test
void testExistsIndex() throws IOException {// 1.创建Request对象GetIndexRequest request new GetIndexRequest(items);// 2.发送请求boolean exists client.indices().exists(request, RequestOptions.DEFAULT);// 3.输出System.err.println(exists ? 索引库已经存在 : 索引库不存在);
}
删除索引库
Test
void testDeleteIndex() throws IOException {// 1.创建Request对象DeleteIndexRequest request new DeleteIndexRequest(items);// 2.发送请求client.indices().delete(request, RequestOptions.DEFAULT);
}
总结 JavaRestClient操作elasticsearch的流程基本类似。核心是client.indices()方法来获取索引库的操作对象。
索引库操作的基本步骤 初始化RestHighLevelClient 创建XxxIndexRequest。XXX是Create、Get、Delete 准备请求参数 Create时需要其它是无参可以省略 发送请求。调用RestHighLevelClient#indices().xxx()方法xxx是create、exists、delete
3.2RestClient操作文档
索引库准备好以后就可以操作文档了。为了与索引库操作分离我们再次创建一个测试类做两件事情 初始化RestHighLevelClient 我们的商品数据在数据库需要利用IHotelService去查询所以注入这个接口
.新增文档
实体类
Data
ApiModel(description 索引库实体)
public class ItemDoc{ApiModelProperty(商品id)private String id;ApiModelProperty(商品名称)private String name;ApiModelProperty(价格分)private Integer price;ApiModelProperty(商品图片)private String image;ApiModelProperty(类目名称)private String category;ApiModelProperty(品牌名称)private String brand;ApiModelProperty(销量)private Integer sold;ApiModelProperty(评论数)private Integer commentCount;ApiModelProperty(是否是推广广告true/false)private Boolean isAD;ApiModelProperty(更新时间)private LocalDateTime updateTime;
} 查询文档 Test
void testGetDocumentById() throws IOException {// 1.准备Request对象GetRequest request new GetRequest(items).id(100002644680);// 2.发送请求GetResponse response client.get(request, RequestOptions.DEFAULT);// 3.获取响应结果中的sourceString json response.getSourceAsString();ItemDoc itemDoc JSONUtil.toBean(json, ItemDoc.class);System.out.println(itemDoc ItemDoc);
}
删除文档
Test
void testDeleteDocument() throws IOException {// 1.准备Request两个参数第一个是索引库名第二个是文档idDeleteRequest request new DeleteRequest(item, 100002644680);// 2.发送请求client.delete(request, RequestOptions.DEFAULT);
}
修改文档
修改我们讲过两种方式 全量修改本质是先根据id删除再新增 局部修改修改文档中的指定字段值
在RestClient的API中全量修改与新增的API完全一致判断依据是ID 如果新增时ID已经存在则修改 如果新增时ID不存在则新增
这里不再赘述我们主要关注局部修改的API即可 Test
void testUpdateDocument() throws IOException {// 1.准备RequestUpdateRequest request new UpdateRequest(items, 100002644680);// 2.准备请求参数request.doc(price, 58800,commentCount, 1);// 3.发送请求client.update(request, RequestOptions.DEFAULT);
}
总结
新增文档 查询文档 修改文档 批量导入文档
在之前的案例中我们都是操作单个文档。而数据库中的商品数据实际会达到数十万条某些项目中可能达到数百万条。
我们如果要将这些数据导入索引库肯定不能逐条导入而是采用批处理方案。常见的方案有 利用Logstash批量导入 需要安装Logstash 对数据的再加工能力较弱 无需编码但要学习编写Logstash导入配置 利用JavaAPI批量导入 需要编码但基于JavaAPI学习成本低 更加灵活可以任意对数据做再加工处理后写入索引库
语法说明
BulkRequest本身其实并没有请求参数其本质就是将多个普通的CRUD请求组合在一起发送。例如 批量新增文档就是给每个文档创建一个IndexRequest请求然后封装到BulkRequest中一起发出。 批量删除就是创建N个DeleteRequest请求然后封装到BulkRequest一起发出 可以看到能添加的请求有 IndexRequest也就是新增 UpdateRequest也就是修改 DeleteRequest也就是删除
Test
void testBulk() throws IOException {// 1.创建RequestBulkRequest request new BulkRequest();// 2.准备请求参数request.add(new IndexRequest(items).id(1).source(json doc1, XContentType.JSON));request.add(new IndexRequest(items).id(2).source(json doc2, XContentType.JSON));// 3.发送请求client.bulk(request, RequestOptions.DEFAULT);
} 3.3RestClient查询 这里关键的API有两个一个是request.source()它构建的就是DSL中的完整JSON参数。其中包含了query、sort、from、size、highlight等所有功能 另一个是QueryBuilders其中包含了我们学习过的各种叶子查询、复合查询等 快速入门
Test
void testMatchAll() throws IOException {// 1.创建RequestSearchRequest request new SearchRequest(items);// 2.组织请求参数request.source().query(QueryBuilders.matchAllQuery());// 3.发送请求SearchResponse response client.search(request, RequestOptions.DEFAULT);// 4.解析响应handleResponse(response);
}private void handleResponse(SearchResponse response) {SearchHits searchHits response.getHits();// 1.获取总条数long total searchHits.getTotalHits().value;System.out.println(共搜索到 total 条数据);// 2.遍历结果数组SearchHit[] hits searchHits.getHits();for (SearchHit hit : hits) {// 3.得到_source也就是原始json文档String source hit.getSourceAsString();// 4.反序列化并打印ItemDoc item JSONUtil.toBean(source, ItemDoc.class);System.out.println(item);}
} 叶子查询
所有的查询条件都是由QueryBuilders来构建的叶子查询也不例外。因此整套代码中变化的部分仅仅是query条件构造的方式其它不动。
例如match查询
Test
void testMatch() throws IOException {// 1.创建RequestSearchRequest request new SearchRequest(items);// 2.组织请求参数request.source().query(QueryBuilders.matchQuery(name, 脱脂牛奶));// 3.发送请求SearchResponse response client.search(request, RequestOptions.DEFAULT);// 4.解析响应handleResponse(response);
}
再比如multi_match查询
Test
void testMultiMatch() throws IOException {// 1.创建RequestSearchRequest request new SearchRequest(items);// 2.组织请求参数request.source().query(QueryBuilders.multiMatchQuery(脱脂牛奶, name, category));// 3.发送请求SearchResponse response client.search(request, RequestOptions.DEFAULT);// 4.解析响应handleResponse(response);
}
还有range查询
Test
void testRange() throws IOException {// 1.创建RequestSearchRequest request new SearchRequest(items);// 2.组织请求参数request.source().query(QueryBuilders.rangeQuery(price).gte(10000).lte(30000));// 3.发送请求SearchResponse response client.search(request, RequestOptions.DEFAULT);// 4.解析响应handleResponse(response);
}
还有term查询
Test
void testTerm() throws IOException {// 1.创建RequestSearchRequest request new SearchRequest(items);// 2.组织请求参数request.source().query(QueryBuilders.termQuery(brand, 华为));// 3.发送请求SearchResponse response client.search(request, RequestOptions.DEFAULT);// 4.解析响应handleResponse(response);
}
复合查询
复合查询也是由QueryBuilders来构建我们以bool查询为例DSL和JavaAPI的对比如图 Test
void testBool() throws IOException {// 1.创建RequestSearchRequest request new SearchRequest(items);// 2.组织请求参数// 2.1.准备bool查询BoolQueryBuilder bool QueryBuilders.boolQuery();// 2.2.关键字搜索bool.must(QueryBuilders.matchQuery(name, 脱脂牛奶));// 2.3.品牌过滤bool.filter(QueryBuilders.termQuery(brand, 德亚));// 2.4.价格过滤bool.filter(QueryBuilders.rangeQuery(price).lte(30000));request.source().query(bool);// 3.发送请求SearchResponse response client.search(request, RequestOptions.DEFAULT);// 4.解析响应handleResponse(response);
}
排序和分页 Test
void testPageAndSort() throws IOException {int pageNo 1, pageSize 5;// 1.创建RequestSearchRequest request new SearchRequest(items);// 2.组织请求参数// 2.1.搜索条件参数request.source().query(QueryBuilders.matchQuery(name, 脱脂牛奶));// 2.2.排序参数request.source().sort(price, SortOrder.ASC);// 2.3.分页参数request.source().from((pageNo - 1) * pageSize).size(pageSize);// 3.发送请求SearchResponse response client.search(request, RequestOptions.DEFAULT);// 4.解析响应handleResponse(response);
}
高亮
高亮查询与前面的查询有两点不同 条件同样是在request.source()中指定只不过高亮条件要基于HighlightBuilder来构造 高亮响应结果与搜索的文档结果不在一起需要单独解析 private void handleResponse(SearchResponse response) {SearchHits searchHits response.getHits();// 1.获取总条数long total searchHits.getTotalHits().value;System.out.println(共搜索到 total 条数据);// 2.遍历结果数组SearchHit[] hits searchHits.getHits();for (SearchHit hit : hits) {// 3.得到_source也就是原始json文档String source hit.getSourceAsString();// 4.反序列化ItemDoc item JSONUtil.toBean(source, ItemDoc.class);// 5.获取高亮结果MapString, HighlightField hfs hit.getHighlightFields();if (CollUtils.isNotEmpty(hfs)) {// 5.1.有高亮结果获取name的高亮结果HighlightField hf hfs.get(name);if (hf ! null) {// 5.2.获取第一个高亮结果片段就是商品名称的高亮值String hfName hf.getFragments()[0].string();item.setName(hfName);}}System.out.println(item);}
} 3.4RestClient实现聚合
可以看到在DSL中aggs聚合条件与query条件是同一级别都属于查询JSON参数。因此依然是利用request.source()方法来设置。
聚合必须的三要素 聚合名称 聚合类型 聚合字段
聚合可配置属性有 size指定聚合结果数量 order指定聚合结果排序方式 field指定聚合字段 不过聚合条件的要利用AggregationBuilders这个工具类来构造。DSL与JavaAPI的语法对比如下 聚合结果与搜索文档同一级别因此需要单独获取和解析。具体解析语法如下 Test
void testAgg() throws IOException {// 1.创建RequestSearchRequest request new SearchRequest(items);// 2.准备请求参数BoolQueryBuilder bool QueryBuilders.boolQuery().filter(QueryBuilders.termQuery(category, 手机)).filter(QueryBuilders.rangeQuery(price).gte(300000));request.source().query(bool).size(0);// 3.聚合参数request.source().aggregation(AggregationBuilders.terms(brand_agg).field(brand).size(5));// 4.发送请求SearchResponse response client.search(request, RequestOptions.DEFAULT);// 5.解析聚合结果Aggregations aggregations response.getAggregations();// 5.1.获取品牌聚合Terms brandTerms aggregations.get(brand_agg);// 5.2.获取聚合中的桶List? extends Terms.Bucket buckets brandTerms.getBuckets();// 5.3.遍历桶内数据for (Terms.Bucket bucket : buckets) {// 5.4.获取桶内keyString brand bucket.getKeyAsString();System.out.print(brand brand);long count bucket.getDocCount();System.out.println(; count count);}
}
四、spring整合
4.1初始化
dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-data-elasticsearch/artifactId
/dependency
配置客户端
Configuration
public class RestClientConfig extends
AbstractElasticsearchConfiguration {OverrideBeanpublic RestHighLevelClient elasticsearchClient() {final ClientConfiguration clientConfiguration
ClientConfiguration.builder().connectedTo(172.16.91.10:9200).build();return RestClients.create(clientConfiguration).rest();}
}spring:elasticsearch:rest:uris: 114.116.205.180:8200
客户端对象
ElasticsearchOperationsRestHighLevelClient 推荐
4.2创建实体类
Data
Document(indexName products, createIndex true)
public class Product {Idprivate Integer id;Field(type FieldType.Keyword)private String title;Field(type FieldType.Float)private Double price;Field(type FieldType.Text)private String description;
}
4.3ElasticsearchOperations文档操作
问题 创建索引⽂档 SpringBootTestpublic class estest {private final ElasticsearchOperations elasticsearchOperations;Autowiredpublic estest(ElasticsearchOperations elasticsearchOperations) {this.elasticsearchOperations elasticsearchOperations;}/**save索引一条文档更新一条文档save方法当文档id不存在时添加文档当文档id存在时候更新文档*/Testpublic void testCreate() {Product product new Product();product.setId(3); //存在id指定id 不存在id⾃动⽣成idproduct.setTitle(怡宝矿泉⽔3);product.setPrice(129.11);product.setDescription(我们喜欢喝矿泉⽔3....);elasticsearchOperations.save(product);}}查询⽂档
Test
public void testGet() {Product product elasticsearchOperations.get(1,
Product.class);System.out.println(product);
}查询所有
Test
public void testFindAll() {SearchHitsProduct productSearchHits
elasticsearchOperations.search(Query.findAll(),
Product.class);productSearchHits.forEach(productSearchHit - {System.out.println(id: productSearchHit.getId());System.out.println(score:
productSearchHit.getScore());Product product productSearchHit.getContent();System.out.println(product: product);});
}删除⽂档
Test
public void testDelete() {Product product new Product();product.setId(1);String delete elasticsearchOperations.delete(product);System.out.println(delete);
}
删除所有
Test
public void testDeleteAll() {elasticsearchOperations.delete(Query.findAll(),
Product.class);4.4RestHighLevelClient
创建索引映射
SpringBootTest
public class estest2 {private final RestHighLevelClient restHighLevelClient;Autowiredpublic estest2(RestHighLevelClient restHighLevelClient) {this.restHighLevelClient restHighLevelClient;}Testpublic void testCreateIndex() throws IOException {CreateIndexRequest createIndexRequest newCreateIndexRequest(fruit);createIndexRequest.mapping({\n \properties\: {\n \title\:{\n \type\: \keyword\\n },\n \price\:{\n \type\: \double\\n },\n \created_at\:{\n \type\: \date\\n },\n \description\:{\n \type\: \text\\n }\n }\n }\n , XContentType.JSON);CreateIndexResponse createIndexResponse restHighLevelClient.indices().create(createIndexRequest,RequestOptions.DEFAULT);System.out.println(createIndexResponse.isAcknowledged());restHighLevelClient.close();}}
其他同RestClient操作文档