Elasticsearch全文检索
1.全文检索概念
我们生活中的数据分为结构化数据和非结构化数据:
- 结构化数据:具有固定格式或有限长度的数据,可以用二维表结构来逻辑表达实现的,如数据库,元数据等。
- 非结构化数据:指不定长或无固定格式的数据,如办公文档、文本、图片、XML、HTML、各类报表、图像和音频/视频信息等等。也叫全文数据。
- 对于结构化数据的搜索:如对数据库的搜索,用 SQL 语句。再如对元数据的搜索,如利用windows 搜索对文件名,类型,修改时间进行搜索等。
- 对非结构化数据的搜索:如利用 windows 的搜索也可以搜索文件内容,Linux 下的 grep命令,再如用 Google 和百度可以搜索大量内容数据。
对非结构化数据也即对全文数据的搜索主要有两种方法:
- 一种是顺序扫描法:比如在一个项目中找一个接口名为 queryTest 的接口,就是在项目里一个文件一个文件的找,对于每个文档从头到尾的去找,直到扫描项目里面的所有文件。window 的搜索文件内容,linux 的 grep 命令就是如此的。小数据量的文件还可以接受,如果对于大量的文件,方法就很慢了。
- 另一种方法就是通过索引:把非结构化数据重新设计成有一定的结构,利用结构化的数据采取一定的搜索算法加快速度。把非结构化数据中提取出的然后重新组织的信息,称之为索引。比如字典,字典的拼音表和部首检字表就是相当于字典的索引,对每一个字的解释就是非结构化的,如果字典没有音节表和部首检字表,在茫茫辞海中找一个字只能顺序扫描。
然而字的某些信息可以提取出来进行结构化处理,比如读音,就比较结构化,分声母和韵母,分别只有几种可以一一列举,于是将读音拿出来按一定的顺序排列,每一项读音都指向此字的详细解释的页数。我们搜索时按结构化的拼音搜到读音,然后按其指向的页数,便可找到我们的非结构化数据——也即对字的解释。
这种先建立索引,再对索引进行搜索的过程叫全文检索。
通俗解释,全文检索是指计算机索引程序通过扫描文章中的每一个词,对每一个词建立一个索引,指明该词在文章中出现的次数和位置,当用户查询时,检索程序就根据事先建立的索引进行查找,并将查找的结果反馈给用户的检索方式。
全文检索大体分两个过程,索引创建(Indexing)和搜索索引(Search)。
索引创建:将现实世界中所有的结构化和非结构化数据提取信息,创建索引的过程。
搜索索引:通过用户的查询请求搜索创建的索引,然后返回查询结果的过程。
2.背景与发展
搜索引擎的发生背景在因特网发展初期,网站相对较少,新闻查找比较容易。然而随着新闻技术的飞速发展,特别是因特网应用的迅速普及,网站越来越多,并且每天全球互联网网页数目以千万级的数量增加。要在浩瀚的网络新闻中寻找所需要的材料无异于大海捞针。这时为满足人人新闻检索需求的搜索网站应运而生。
技术发展:lucene—>solr—>elasticsearch
2.1. lucene
2.1.1 介绍
官网地址:
https://lucene.apache.org/
2.1.2 lucene起源
- 1997 年年末,Doug Cutting (lucene 的原作者) 因为工作不稳定,产生了把自己的一些软件商业化的想法。再加上当时 Java 已经是一门炙手可热的编程语言 , 作者也正好需要一个学习它的理由。因为作者以前有编写搜索软件的经验,于是觉得可以用 Java 写一个搜索软件以维持生计。于是 lucene 就此诞生。
- 2000 年 ,作者由于对商业运作并没有什么兴趣 ,就把 lucene 放到了 SourceForge 上 , 希望通过开源的方式来让自己保持程序创作的激情。
- 2001 年 , 此时的 lucene 已经吸引了一些人使用 , Apache 希望能够接收 lucene 项目 。从此 lucene 变得真正活跃起来 , 有一些志愿者加入到 lucene 的开发中。不过此时原作者 Doug Cutting 任然是唯一的核心开发者。
- 2010 年 ,原作者已经不再参与 lucene 的日常开发和维护了 , 已经有很多对 lucene 核心有着深入了解的开发者参与到项目中。
- lucene 发展出了C++ , C# , Prel , Python 等其他语言的版本。
2.1.3 lucene优点和局限
优点:
1.稳定,索引性能高
现代磁盘上每小时能够索引150GB以上的数据;
对内存的要求小——只需要1MB的堆内存;
增量索引和批量索引一样快;
索引的大小约为索引文本大小的20%~30%;
2.高效、准确、高性能的搜索算法
搜索排名——最好的结果显示在最前面;
许多强大的查询类型:短语查询、通配符查询、近似查询、范围查询等;
对字段级别搜索(如标题,作者,内容);
可以对任意字段排序;
支持搜索多个索引并合并搜索结果;
支持更新操作和查询操作同时进行;
灵活的切面、高亮、join和group by功能;
速度快,内存效率高,容错性好;
可选排序模型,包括向量空间模型和BM25模型;
可配置存储引擎;
3.跨平台解决方案
作为 Apache 开源许可,在商业软件和开放程序中都可以使用 Lucene;
100%纯Java编写;
对多种语言提供接口;
局限:
只能基于Java语言开发
类库的接口学习曲线陡峭
原生并不支持水平扩展
2.2. solr
2.2.1 介绍
Solr是一个采用Java开发,基于Lucene的全文搜索服务器,同时对其进行了扩展,提供了比Lucene更为丰富的查询语言,同时实现了可配置、可扩展并对查询性能进行了优化,并且提供了一个完善的功能管理界面,是一款非常优秀的全文搜索引擎。它对外提供类似于Web-service的API接口。用户可以通过http请求,向搜索引擎服务器提交一定格式的XML文件,生成索引;也可以通过Http Get操作提出查找请求,并得到XML格式的返回结果。
官网地址:
https://lucene.apache.org/solr/
2.2.2 solr优点
①高级的全文搜索功能;
②专为高通量的网络流量进行的优化;
③基于开放接口(XML和HTTP)的标准;
④综合的HTML管理界面;
⑤可伸缩性-能够有效地复制到另外一个Solr搜索服务器;
⑥使用XML配置达到灵活性和适配性;
⑦可扩展的插件体系。
2.3. Elasticsearch
2.3.1介绍
官方网址:https://www.elastic.co/cn/products/elasticsearch
Github :https://github.com/elastic/elasticsearch
总结:
1、elasticsearch是一个基于Lucene的高扩展的分布式搜索服务器,支持开箱即用。
2、elasticsearch隐藏了Lucene的复杂性,对外提供Restful 接口来操作索引、搜索。
突出优点:
- 支持分布式,扩展性好,可部署上百台服务器集群,处理PB级数据。
- 近实时的去索引数据、搜索数据。
- 降低全文检索的学习曲线,可以被任何编程语言调用
2.3.2 ElasticSearch VS Solr
- 近几年的流行趋势
我们查看一下这两种产品的Google搜索趋势。谷歌趋势表明,与 Solr 相比,Elasticsearch具有很大的吸引力,但这并不意味着Apache Solr已经死亡。虽然有些人可能不这么认为,但Solr仍然是最受欢迎的搜索引擎之一,拥有强大的社区和开源支持。
总结:
(1)es基本是开箱即用,非常简单。Solr安装略微复杂一丢丢,可关注
(2)Solr 利用 Zookeeper 进行分布式管理,而 Elasticsearch 自身带有分布式协调管理功能。
(3)Solr 支持更多格式的数据,比如JSON、XML、CSV,而 Elasticsearch 仅支持json文件格式。
(4)Solr 官方提供的功能更多,而 Elasticsearch 本身更注重于核心功能,高级功能多有第三方插件提供,例如图形化界面需要kibana友好支撑
(5)Solr 查询快,但更新索引时慢(即插入删除慢),用于电商等查询多的应用;ES建立索引快,即实时性查询快,用于facebook新浪等搜索。Solr 是传统搜索应用的有力解决方案,但 Elasticsearch 更适用于新兴的实时搜索应用。
3.功能和使用场景
3.1功能
第一、分布式的搜索引擎和数据分析引擎
搜索:百度,网站的站内搜索,IT系统的检索
数据分析:
电商网站,最近一周手机商品销量排名前10的商家有哪些;
新闻网站,最近1个月访问量排名前3的新闻版块是哪些
第二、 全文检索,结构化检索,数据分析
全文检索:我想搜索商品名称包含手机的商品,select * from products where product_name like “%手机%”
结构化检索:我想搜索商品分类为电子数码的商品都有哪些,select * from products where category_id=‘电子数码’
部分匹配、自动完成、搜索纠错、搜索推荐
数据分析:我们分析每一个商品分类下有多少个商品,select category_id,count(*) from products group by category_id
第三、对海量数据进行近实时的处理
分布式:ES自动可以将海量数据分散到多台服务器上去存储和检索
海量数据的处理:分布式以后,就可以采用大量的服务器去存储和检索数据,自然而然就可以实现海量数据的处理了
近实时:检索个数据要花费1小时(这就不要近实时,离线批处理,batch-processing);在秒级别对数据进行搜索和分析
跟分布式/海量数据相反的:lucene,单机应用,只能在单台服务器上使用,最多只能处理单台服务器可以处理的数据量
3.2使用场景
- 非结构化数据查询(结构化也行)
- 适用于海量数据的快速检索
- 日志数据分析,logstash采集日志,ES进行复杂的数据分析
4.工作原理
4.1 基本概念
-
倒排索引(Inverted Index):
该索引表中的每一项都包括一个属性值和具有该属性值的各记录的地址。由于不是由记录来确定属性值,而是由属性值来确定记录的位置,因而称为倒排索引(inverted index)。Elasticsearch能够实现快速、高效的搜索功能,正是基于倒排索引原理。 -
索引(Index):
Elasticsearch 数据管理的顶层单位就叫做 Index(索引),相当于关系型数据库里的数据库的概念。另外,每个Index的名字必须是小写。 -
类型(Type):
Document 可以分组,比如employee这个 Index 里面,可以按部门分组,也可以按职级分组。这种分组就叫做 Type,它是虚拟的逻辑分组,用来过滤 Document,类似关系型数据库中的数据表。 -
文档(Document):
index里面单条的记录称为 Document(文档)。多条 Document 构成了一个 Index。Document 使用 JSON 格式表示。同一个 Index 里面的 Document,不要求有相同的结构(scheme),但是最好保持相同,这样有利于提高搜索效率。 -
文档元数据(Document metadata):
文档元数据为_index, _type, _id, 这三者可以唯一表示一个文档,_index表示文档在哪存放,_type表示文档的对象类别,_id为文档的唯一标识。 -
字段(Fields):
每个Document都类似一个JSON结构,它包含了许多字段,每个字段都有其对应的值,多个字段组成了一个 Document,可以类比关系型数据库数据表中的字段。
在 Elasticsearch 中,文档(Document)归属于一种类型(Type),而这些类型存在于索引(Index)中,下图展示了Elasticsearch与传统关系型数据库的类比:
4.2索引结构
下图是ElasticSearch的索引结构,下边黑色部分是物理结构,上边黄色部分是逻辑结构,逻辑结构也是为了更好的去描述ElasticSearch的工作原理及去使用物理结构中的索引文件。
逻辑结构部分是一个倒排索引表:
1、将要搜索的文档内容分词,所有不重复的词组成分词列表。
2、将搜索的文档最终以Document方式存储起来。
3、每个词和docment都有关联。
例如:
现在,如果我们想搜索 quick brown ,我们只需要查找包含每个词条的文档:
两个文档都匹配,但是第一个文档比第二个匹配度更高。如果我们使用仅计算匹配词条数量的简单 相似性算法 ,那么,我们可以说,对于我们查询的相关性来讲,第一个文档比第二个文档更佳。
4.3RESTful应用方法
Elasticsearch提供 RESTful Api接口进行索引、搜索,并且支持多种客户端。
下图是es在项目中的应用方式:
1)用户在前端搜索关键字
2)项目前端通过http方式请求项目服务端
3)项目服务端通过Http RESTful方式请求ES集群进行搜索
4)ES集群从索引库检索数据。
5. 使用
5.1 ElasticaSearch 安装
安装配置:
1、新版本要求至少jdk1.8以上。
2、支持tar、zip、rpm等多种安装方式。
在windows下开发建议使用ZIP安装方式。
3、支持docker方式安装
详细参见:https://www.elastic.co/guide/en/elasticsearch/reference/current/install-elasticsearch.html
下载 ES: Elasticsearch 7.8.0
https://www.elastic.co/downloads/past-releases
解压 elasticsearch-7.8.0.zip
bin:脚本目录,包括:启动、停止等可执行脚本
config:配置文件目录
data:索引目录,存放索引文件的地方
logs:日志目录
modules:模块目录,包括了es的功能模块
plugins :插件目录,es支持插件机制
5.2配置文件
使用zip、tar安装,配置文件的地址在安装目录的config下,配置文件如下:
- elasticsearch.yml : 用于配置Elasticsearch运行参数
- jvm.options : 用于配置Elasticsearch JVM设置
- log4j2.properties: 用于配置Elasticsearch日志
elasticsearch.yml:
cluster.name: demo
node.name: demo_node_1
network.host: 0.0.0.0
http.port: 9200
transport.tcp.port: 9300
node.master: true
node.data: true
#discovery.zen.ping.unicast.hosts: ["0.0.0.0:9300", "0.0.0.0:9301", "0.0.0.0:9302"]
discovery.zen.minimum_master_nodes: 1
bootstrap.memory_lock: false
node.max_local_storage_nodes: 1
path.data: D:\ElasticSearch\elasticsearch‐7.8.0\data
path.logs: D:\ElasticSearch\elasticsearch‐7.8.0\logs
http.cors.enabled: true //允许跨域
http.cors.allow‐origin: /.*/ //允许所有路径跨域
注意path.data和path.logs路径配置正确!!!
参数解释:
cluster.name:
配置elasticsearch的集群名称,默认是elasticsearch。建议修改成一个有意义的名称。
node.name:
节点名,通常一台物理服务器就是一个节点,es会默认随机指定一个名字,建议指定一个有意义的名称,方便管理一个或多个节点组成一个cluster集群,集群是一个逻辑的概念,节点是物理概念
network.host:
设置绑定主机的ip地址,设置为0.0.0.0表示绑定任何ip,允许外网访问,生产环境建议设置为具体的ip。
http.port: 9200
设置对外服务的http端口,默认为9200。
transport.tcp.port: 9300
集群结点之间通信端口
node.master:
指定该节点是否有资格被选举成为master结点,默认是true,如果原来的master宕机会重新选举新的master。
node.data:
指定该节点是否存储索引数据,默认为true。
discovery.zen.ping.unicast.hosts: [“host1:port”, “host2:port”, “…”]
设置集群中master节点的初始列表。
discovery.zen.minimum_master_nodes:
主结点数量的最少值 ,此值的公式为:(master_eligible_nodes / 2) + 1 ,比如:有3个符合要求的主结点,那么这里要设置为2。
bootstrap.memory_lock:
true 设置为true可以锁住ES使用的内存,避免内存与swap分区交换数据。
node.max_local_storage_nodes:
单机允许的最大存储结点数,通常单机启动一个结点建议设置为1,开发环境如果单机启动多个节点可设置大于1.
path.conf:
设置配置文件的存储路径,tar或zip包安装默认在es根目录下的config文件夹,rpm安装默认在/etc/
elasticsearch path.data:
设置索引数据的存储路径,默认是es根目录下的data文件夹,可以设置多个存储路径,
用逗号隔开。
path.logs:
设置日志文件的存储路径,默认是es根目录下的logs文件夹 path.plugins: 设置插件的存
放路径,默认是es根目录下的plugins文件夹
discovery.zen.ping.timeout: 3s
设置ES自动发现节点连接超时的时间,默认为3秒,如果网络延迟高可设置大些。
集群搭建:copy一份,修改elasticsearch.yml中:node.name,network.host,http.port,transport。tcp.port,path.data,path.logs即可。(注意节点数量设置)
5.3 启动
进入bin目录,在cmd下运行:elasticsearch.bat
浏览器输入:http://localhost:9200
显示结果如下(配置不同内容则不同)说明 ES启动成功:
{
"name" : "demo_node_1",
"cluster_name" : "demo",
"cluster_uuid" : "L0DJsrmCSN-lhQiky7rybw",
"version" : {
"number" : "7.8.0",
"build_flavor" : "default",
"build_type" : "zip",
"build_hash" : "757314695644ea9a1dc2fecd26d1a43856725e65",
"build_date" : "2020-06-14T19:35:50.234439Z",
"build_snapshot" : false,
"lucene_version" : "8.5.1",
"minimum_wire_compatibility_version" : "6.8.0",
"minimum_index_compatibility_version" : "6.0.0-beta1"
},
"tagline" : "You Know, for Search"
}
5.4创建索引库
ES的索引库是一个逻辑概念,它包括了分词列表及文档列表,同一个索引库中存储了相同类型的文档。它就相当于MySQL中的表,或相当于Mongodb中的集合。
关于索引这个语:
索引(名词):ES是基于Lucene构建的一个搜索服务,它要从索引库搜索符合条件索引数据。
索引(动词):索引库刚创建起来是空的,将数据添加到索引库的过程称为索引。
下边介绍两种创建索引库的方法,它们的工作原理是相同的,都是客户端向ES服务发送命令。
1)使用postman或curl这样的工具创建:
put http://localhost:9200/索引库名称
{
"settings":{
"index":{
"number_of_shards":1,
"number_of_replicas":0
}
}
}
number_of_shards:设置分片的数量,在集群中通常设置多个分片,表示一个索引库将拆分成多片分别存储不同的结点,提高了ES的处理能力和高可用性,这里设置为1。
number_of_replicas:设置副本的数量,设置副本是为了提高ES的高可靠性,单机环境设置为0。
2)使用head图形化插件创建
5.5创建映射
- 概念说明:
在索引中每个文档都包括了一个或多个field,创建映射就是向索引库中创建field的过程,下边是document和field与关系数据库的概念的类比:
文档(Document)----------------Row记录
字段(Field)-------------------Columns 列
注意:6.0之前的版本有type(类型)概念,type相当于关系数据库的表,ES官方将在ES9.0版本中彻底删除type。
上边讲的创建索引库相当于关系数据库中的数据库还是表?
1、如果相当于数据库就表示一个索引库可以创建很多不同类型的文档,这在ES中也是允许的。
2、如果相当于表就表示一个索引库只能存储相同类型的文档,ES官方建议 在一个索引库中只存储相同类型的文档。
5.5.1 ik分词器
使用IK分词器可以实现对中文分词的效果。
下载IK分词器:(Github地址:https://github.com/medcl/elasticsearch-analysis-ik)
下载zip:
解压,并将解压的文件拷贝到ES安装目录的plugins下的ik目录下
注意:分词器版本和elasticsearch版本一致
ik分词器有两种分词模式:ik_max_word和ik_smart模式。
ik_max_word
会将文本做最细粒度的拆分
比如会将“中华人民共和国人民大会堂”拆分为“中华人民共和国、中华人民、中华、
华人、人民共和国、人民、共和国、大会堂、大会、会堂等词语。
ik_smart
会做最粗粒度的拆分
比如会将“中华人民共和国人民大会堂”拆分为中华人民共和国、人民大会堂。
- 自定义词库
如果要让分词器支持一些专有词语,可以自定义词库。
iK分词器自带一个main.dic的文件,此文件为词库文件。
在上边的目录中新建一个my.dic文件(注意文件格式为utf-8(不要选择utf-8 BOM))
可以在其中自定义词汇:
比如定义:
在配置文件IKAnalyzer.cfg.xml中配置自定义my.dic
5.5.2ElasticSearch 常用字段类型
- 核⼼数据类型
- 复杂数据类
- 专用数据类型
核心的字段类型:
(1)字符串
text ⽤于全⽂索引,搜索时会自动使用分词器进⾏分词再匹配
keyword 不分词,搜索时需要匹配完整的值,如:邮政编码、手机号码、身份证等。keyword字段通常用于过虑、排序、聚合等。
{
"name": {
"type": "text",
"analyzer": "ik_max_word",
"search_analyzer": "ik_smart",
"index": true, //默认true
"store": false, //默认false
}
}
- type:类型,这里为字符串,其它类型见上图;
- analyzer:通过analyzer属性指定分词器。使用ik分词器的ik_max_word分词模式,上边指定了analyzer是指在索引和搜索都使用ik_max_word,如果单独想定义搜索时使用的分词器则可以通过search_analyzer属性。分词颗粒度ik_max_word大于ik_smart。对于ik分词器建议是索引时使用ik_max_word将搜索内容进行细粒度分词,搜索时使用ik_smart提高搜索精确性。
- index:通过index属性指定是否索引。默认为index=true,即要进行索引,只有进行索引才可以从索引库搜索到。也有一些内容不需要索引,比如:商品图片地址只被用来展示图片,不进行搜索图片,此时可以将index设置为false。
- store:是否在source之外存储,每个文档索引后会在 ES中保存一份原始文档,存放在"_source"中,一般情况下不需要设置store为true,因为在_source中已经有一份原始文档了。
(2)数值型
整型: byte,short,integer,long
浮点型: float, half_float, scaled_float,double
(3)日期类型
date
日期类型不用设置分词器。通常日期类型的字段用于排序。通过format设置日期格式
例子:
下边的设置允许date字段存储年月日时分秒、年月日及毫秒三种格式。
"timestamp": {
"type": "date",
"format": "yyyy‐MM‐dd HH:mm:ss||yyyy‐MM‐dd"
}
(4)范围型
integer_range, long_range, float_range,double_range,date_range
比如招聘要求年龄在[20, 40]上,mapping:
"age_limit" :{
"type" : "integer_range"
}
"age_limit" : {
"gte" : 20,
"lte" : 40
}
(5)布尔
boolean #true、false
(6)⼆进制
binary 会把值当做经过 base64 编码的字符串
复杂数据类型:
(1)对象object
定义mapping:
"user" : {
"type":"object"
}
插入数据:
"user" : {
"name":"chy",
"age":12
}
(2)数组
定义mapping:
"arr" : {
"type":"integer"
}
插入数据:
"arr" : [1,3,4]
专用数据类型:
ip类型
ip类型的字段用于存储IPV4或者IPV6的地址
定义mapping:
"ip_address" : {
"type":"ip"
}
插入数据:
"ip_address" : "192.168.1.1"
6.5.3 创建映射
例如:把课程信息存储到ES中,这里我们创建课程信息的映射:
发送:post http://localhost:9200/索引库名称 /类型名称/_mapping
创建类型为demo_course的映射
post 请求:http://localhost:9200/demo_course/doc/_mapping
{
"properties":{
"description":{
"analyzer":"ik_max_word",
"search_analyzer":"ik_smart",
"type":"text"
},
"grade":{
"type":"keyword"
},
"id":{
"type":"keyword"
},
"mt":{
"type":"keyword"
},
"name":{
"analyzer":"ik_max_word",
"search_analyzer":"ik_smart",
"type":"text"
},
"users":{
"index":false,
"type":"text"
},
"charge":{
"type":"keyword"
},
"valid":{
"type":"keyword"
},
"pic":{
"index":false,
"type":"keyword"
},
"qq":{
"index":false,
"type":"keyword"
},
"price":{
"type":"float"
},
"price_old":{
"type":"float"
},
"st":{
"type":"keyword"
},
"status":{
"type":"keyword"
},
"studymodel":{
"type":"keyword"
},
"teachmode":{
"type":"keyword"
},
"teachplan":{
"analyzer":"ik_max_word",
"search_analyzer":"ik_smart",
"type":"text"
},
"expires":{
"type":"date",
"format":"yyyy‐MM‐ddHH:mm:ss"
},
"pub_time":{
"type":"date",
"format":"yyyy‐MM‐ddHH:mm:ss"
},
"start_time":{
"type":"date",
"format":"yyyy‐MM‐ddHH:mm:ss"
},
"end_time":{
"type":"date",
"format":"yyyy‐MM‐ddHH:mm:ss"
},
"update_time":{
"type":"date",
"format":"yyyy‐MM‐ddHH:mm:ss"
}
}
}
5.6新增文档
ES中的文档相当于MySQL数据库表中的记录。
发送:put 或Post http://localhost:9200/demo_course/doc/id值
(如果不指定id值ES会自动生成ID)
{
"qq":"78698789",
"price_old":168,
"st":"1-3-4",
"charge":"203003",
"mt":"1-3",
"pubTime":"2020-07-13 09:50:00",
"studymodel":"201202",
"description":"水情水调中心",
"pic":"template_overwrite",
"teachplan":"{}",
"users":"asus",
"valid":"204002",
"price":68,
"grade":"200004",
"name":"智慧管理平台",
"id":"wrweyttwetwrer33343",
"timestamp":1594604940000
}
5.7搜索文档
1、根据课程id查询文档
发送:get http://localhost:9200/demo_course/doc/4028e58161bcf7f40161bcf8b77c0000
2、查询所有记录
发送 get http://localhost:9200/demo_course/doc/_search
3、查询名称中包括spring 关键字的的记录
发送:get http://localhost:9200/ demo_course/doc/_search?q=name:spring
4、查询学习模式为201001的记录
发送 get http://localhost:9200/ demo_course/doc/_search?q=studymodel:201001
分析搜索结果:
{
"took": 4,
"timed_out": false,
"_shards": {
"total": 1,
"successful": 1,
"skipped": 0,
"failed": 0
},
"hits": {
"total": {
"value": 1,
"relation": "eq"
},
"max_score": 1,
"hits": [
{
"_index": "demo_course",
"_type": "doc",
"_id": "5UJI3HMBW7M_WXPelPnL",
"_score": 1,
"_source": {
"qq": "78698789",
"price_old": 168,
"st": "1-3-4",
"charge": "203003",
"mt": "1-3",
"pubTime": "2020-07-13 09:50:00",
"studymodel": "201202",
"description": "水情水调中心",
"pic": "template_overwrite",
"teachplan": "{}",
"users": "asus",
"valid": "204002",
"price": 68,
"grade": "200004",
"name": "智慧管理平台",
"id": "wrweyttwetwrer33343",
"timestamp": 1594604940000
}
}
]
}
}
took:本次操作花费的时间,单位为毫秒。
timed_out:请求是否超时
_shards:说明本次操作共搜索了哪些分片
hits:搜索命中的记录
hits.total : 符合条件的文档总数 hits.hits :匹配度较高的前N个文档
hits.max_score:文档匹配得分,这里为最高分
_score:每个文档都有一个匹配度得分,按照降序排列。
_source:显示了文档的原始内容。
5.7.1查询语法
叶子查询
叶子查询子句在特定字段中查找特定值,例如:
- match query 模糊匹配——会将关键词分词再匹配
- term query 精确匹配——不会将关键词分词直接匹配
- range query 范围查询
复合查询
复合查询子句包装其他叶查询或复合查询,并用于以逻辑方式组合多个查询,例如:
- bool query 布尔查询
- boosting query 助推查询(常用于降低文档得分,比如售罄或暂不销售商品沉底)
- constant_score query 恒定分数查询,,目的就是返回指定的score,一般都结合filter使用,因为filter context忽略score。
- dis_max query 分离最大化查询: 将任何与任一查询匹配的文档作为结果返回,但只将最佳匹配的评分作为查询的评分结果返回
- function_score query 功能评分查询:它会在查询结束后对每一个匹配的文档进行一系列的重打分操作,最后以生成的最终分数进行排序。
查询方式参考官网:https://www.elastic.co/guide/cn/elasticsearch/guide/current/query-dsl-intro.html
5.7.1.1 match query
- match是标准的全文检索
- 在匹配之前会先对查询关键字进行分词
- 可以指定分词器来覆盖mapping中设置的搜索分词器
{
"query": {
"match": {
"name": {
"query": "es是一个优秀的搜索引擎",
"analyzer": "ik_smart"
}
}
}
}
5.7.1.2 term query
针对某个属性的查询,这里注意 term 不会进行分词,比如 在 es 中 存了 “集团” 会被分成 “集/团” 当你用 term 去查询 “集”时能查到,但是查询 “集团” 时,就什么都没有,而 match 就会将词语分成 “集/团”去查
{
"query": {
"term": {
"name": {
"query": "集团"
}
}
}
}
5.7.1.3 bool query
可以组合多种叶子查询,包含如下:
-
must:出现于匹配查询当中,有助于匹配度(_score)的计算
-
filter:必须满足才能出现,属于过滤,不会影响分值的计算,但是会过滤掉不符合的数据
-
should:该条件下的内容是应该满足的内容,如果符合会增加分值,不符合降低分值,不会不显示
-
must_not:满足的内容不会出现,与filter功能相反,属于过滤,不会影响分值的计算,但是会过滤掉不符合的数据
{
"query": {
"bool": {
"must": [{
"match": {
"name": "花花公子羽绒服"
}
}],
"must_not": [{
"term": {
"proId": 6
}
}],
"should": [{
"terms": {
"name.keyword": ["花花公子暖心羽绒服", "花花公子外套"]
}
}],
"filter": {
"range": {
"createTime": {
"gte": "2019-12-12 17:56:56",
"lte": "2019-12-19 17:56:56",
"format": "yyyy-MM-dd HH:mm:ss"
}
}
}
}
}
}
Filter
其实准确来说,ES中的查询操作分为2种:查询(query)和过滤(filter)。查询即是之前提到的query查询,它(查询)默认会计算每个返回文档的得分,然后根据得分排序。而过滤(filter)只会筛选出符合的文档,并不计算得分,且它可以缓存文档。所以,单从性能考虑,过滤比查询更快。
换句话说,过滤适合在大范围筛选数据,而查询则适合精确匹配数据。一般应用时,应先使用过滤操作过滤数据,然后使用查询匹配数据。
在Filter context中,查询子句回答问题“此文档是否与此查询子句匹配?”答案是简单的“是”或“否”,即不计算分数。过滤器上下文主要用于过滤结构化数据,例如:
- 该食品的生产日期是否在2018-2019之间
- 该商品的状态是否为"已上架"
Ps:常用过滤器将由Elasticsearch自动缓存,以提高性能。
5.8修改文档
ES更新文档的顺序是:先检索到文档、将原来的文档标记为删除、创建新文档、删除旧文档,创建新文档就会重建索引。
1、完全替换
Post:http://localhost:9200/demo_course/doc/3
{
"name": "spring cloud实战",
"description": "本课程主要从四个章节进行讲解: 1.微服务架构入门 2.spring cloud 基础入门 3.实战SpringBoot 4. 注册中心eureka。 ",
"studymodel": "201001" ,
"price": 5.6
}
2 、局部更新
下边的例子是只更新price字段。
post: http://localhost:9200/demo_course/doc/3/_update
{
"doc": {
"price": 66.6
}
}
5.9删除文档
根据id删除,格式如下:
DELETE /{index}/{type}/{id}
搜索匹配删除,将搜索出来的记录删除,格式如下:
POST /{index}/{type}/_delete_by_query
条件删除:
{
"query": {
"term": {
"studymodel": "201001"
}
}
}
6.javaApi进行增删改查
6.1 搭建工程
ES提供多种不同的客户端:
1、TransportClient
ES提供的传统客户端,官方计划8.0版本删除此客户端。
2、RestClient
RestClient是官方推荐使用的,它包括两种:Java Low Level REST Client和 Java High Level REST Client。
ES在6.0之后提供 Java High Level REST Client, 两种客户端官方更推荐使用 Java High Level REST Client,不过当前它还处于完善中,有些功能还没有。
这里准备采用 Java High Level REST Client,如果它有不支持的功能,则使用Java Low Level REST Client。
6.1.1添加依赖:
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch‐rest‐high‐level‐client</artifactId>
<version>7.8.0</version>
</dependency>
<dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
<version>7.8.0</version>
</dependency>
6.1.2application.yml配置
server.port=40100
spring.application.name=search_service
spring.main.allow-bean-definition-overriding=true
# ----------------------------------------
# elasticsearch config
##------------RestClient配置-----------
demo.hostlist=127.0.0.1:9200
demo.course.index=demo_course
demo.course.type=doc
demo.course.source_field=id,name,grade,mt,st,charge,valid,pic,qq,price,price_old,status,studymodel,teachmode,expires,pub_time,start_time,end_time,update_time
##------------RestClient配置配置-----------
# ----------------------------------------
6.1.3配置类
创建ElasticsearchConfig:
package com.nr.esdemo.config;
import org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 功能描述:
*
* @author chenhao
* @since 2020/7/12
*/
@Configuration
public class ElasticsearchConfig {
@Value("${demo.hostlist}")
private String hostlist;
@Bean
public RestHighLevelClient restHighLevelClient(){
//解析hostlist配置信息
String[] split = hostlist.split(",");
//创建HttpHost数组,其中存放es主机和端口的配置信息
HttpHost[] httpHostArray = new HttpHost[split.length];
for(int i=0;i<split.length;i++){
String item = split[i];
httpHostArray[i] = new HttpHost(item.split(":")[0], Integer.parseInt(item.split(":")[1]), "http");
}
//创建RestHighLevelClient客户端
return new RestHighLevelClient(RestClient.builder(httpHostArray));
}
//项目主要使用RestHighLevelClient,对于低级的客户端暂时不用
@Bean
public RestClient restClient(){
//解析hostlist配置信息
String[] split = hostlist.split(",");
//创建HttpHost数组,其中存放es主机和端口的配置信息
HttpHost[] httpHostArray = new HttpHost[split.length];
for(int i=0;i<split.length;i++){
String item = split[i];
httpHostArray[i] = new HttpHost(item.split(":")[0], Integer.parseInt(item.split(":")[1]), "http");
}
return RestClient.builder(httpHostArray).build();
}
}
6.1.4启动类
package com.nr.esdemo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.data.elasticsearch.repository.config.EnableElasticsearchRepositories;
@SpringBootApplication
public class EsdemoApplication {
public static void main(String[] args) {
SpringApplication.run(EsdemoApplication.class, args);
}
}
6.2增删改查
6.2.1添加文档
//添加文档
@Test
public void testAddDoc() throws IOException {
//准备json数据
Map<String, Object> jsonMap = new HashMap<>();
jsonMap.put("name", "spring cloud实战");
jsonMap.put("description", "主要演示增删改查");
jsonMap.put("studymodel", "201001");
SimpleDateFormat dateFormat =new SimpleDateFormat("yyyy‐MM‐dd HH:mm:ss");
jsonMap.put("timestamp", dateFormat.format(new Date()));
jsonMap.put("price", 5.6f);
//索引请求对象
IndexRequest indexRequest = new IndexRequest("demo_course","doc");
//指定索引文档内容
indexRequest.source(jsonMap);
//索引响应对象
IndexResponse indexResponse = restHighLevelClient.index(indexRequest);
//获取响应结果
DocWriteResponse.Result result = indexResponse.getResult();
System.out.println(result);
}
6.2.2查询文档
//根据id查询文档
@Test
public void getDoc() throws IOException {
GetRequest getRequest = new GetRequest(
"demo_course",
"doc",
"40JF3HMBW7M_WXPesfnU");
GetResponse getResponse = restHighLevelClient.get(getRequest);
boolean exists = getResponse.isExists();
Map<String, Object> sourceAsMap = getResponse.getSourceAsMap();
System.out.println(sourceAsMap);
}
6.2.3更新文档
//更新文档
@Test
public void updateDoc() throws IOException {
UpdateRequest updateRequest = new UpdateRequest(index, type, "4028e581617f945f01617f9dabc40000");
Map<String, String> map = new HashMap<>();
map.put("name", "spring cloud实战");
updateRequest.doc(map);
UpdateResponse update = restHighLevelClient.update(updateRequest);
RestStatus status = update.status();
System.out.println(status);
}
6.2.4删除文档
//根据id删除文档
@Test
public void testDelDoc() throws IOException {
//删除文档id
String id = "40JF3HMBW7M_WXPesfnU";
//删除索引请求对象
DeleteRequest deleteRequest = new DeleteRequest(index, type, id);
//响应对象
DeleteResponse deleteResponse = restHighLevelClient.delete(deleteRequest);
//获取响应结果
DocWriteResponse.Result result = deleteResponse.getResult();
System.out.println(result);
}
6.2.5分页查询
//分页查询
@Test
public void testPageQuery() throws IOException {
SearchRequest searchRequest = new SearchRequest(index);
searchRequest.types(type);
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.query(QueryBuilders.matchAllQuery());
//分页查询,设置起始下标,从0开始
searchSourceBuilder.from(0);
//每页显示个数
searchSourceBuilder.size(10);
//source源字段过虑
searchSourceBuilder.fetchSource(new String[]{"name", "studymodel"}, new String[]{});
searchRequest.source(searchSourceBuilder);
SearchResponse searchResponse = client.search(searchRequest);
}
6.2.6term Query
Term Query为精确查询,在搜索时会整体匹配关键字,不再将关键字分词。
//TermQuery
@Test
public void testTermQuery() throws IOException {
SearchRequest searchRequest = new SearchRequest(index);
searchRequest.types(type);
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.query(QueryBuilders.termQuery("name", "spring"));
//source源字段过虑
searchSourceBuilder.fetchSource(new String[]{"name", "studymodel"}, new String[]{});
searchRequest.source(searchSourceBuilder);
SearchResponse searchResponse = client.search(searchRequest);
System.out.println(searchResponse);
}
6.2.7match Query
1、基本使用
match Query即全文检索,它的搜索方式是先将搜索字符串分词,再使用各各词条从索引中搜索。
match query与Term query区别是match query在搜索前先将搜索关键字分词,再拿各各词语去索引中搜索。
1 、将“spring开发”分词,分为spring、开发两个词
2、再使用spring和开发两个词去匹配索引中搜索。
3、由于设置了operator为or,只要有一个词匹配成功则就返回该文档,operator设置为and表示每个词都在文档中出现则才符合条件。
//根据MatchQuery
@Test
public void testMatchQuery() throws IOException {
SearchRequest searchRequest = new SearchRequest(index);
searchRequest.types(type);
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
//source源字段过虑
searchSourceBuilder.fetchSource(new String[]{"name", "studymodel"}, new String[]{});
//匹配关键字
searchSourceBuilder.query(QueryBuilders.matchQuery("description", "spring开发").operator(Operator.OR));
searchRequest.source(searchSourceBuilder);
SearchResponse searchResponse = client.search(searchRequest);
SearchHits hits = searchResponse.getHits();
SearchHit[] searchHits = hits.getHits();
for (SearchHit hit : searchHits) {
String index = hit.getIndex();
String type = hit.getType();
String id = hit.getId();
float score = hit.getScore();
String sourceAsString = hit.getSourceAsString();
Map<String, Object> sourceAsMap = hit.getSourceAsMap();
String name = (String) sourceAsMap.get("name");
String studymodel = (String) sourceAsMap.get("studymodel");
String description = (String) sourceAsMap.get("description");
System.out.println(name);
System.out.println(studymodel);
System.out.println(description);
}
}
2、minimum_should_match
上边使用的operator = or表示只要有一个词匹配上就得分,如果实现三个词至少有两个词匹配如何实现?
使用minimum_should_match可以指定文档匹配词的占比:
比如搜索语句如下:
//匹配关键字
MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery("description", "spring开发框架")
.minimumShouldMatch("80%");//设置匹配占比
searchSourceBuilder.query(matchQueryBuilder);
“spring开发框架”会被分为三个词:spring、开发、框架
设置"minimum_should_match": "80%"表示,三个词在文档的匹配占比为80%,即3*0.8=2.4,向上取整得2,表示至少有两个词在文档中要匹配成功。
6.2.7multi Query
上边的termQuery和matchQuery一次只能匹配一个Field,multiQuery一次可以匹配多个字段。
1、基本使用
单项匹配是在一个field中去匹配,多项匹配是拿关键字去多个Field中匹配。
MultiMatchQueryBuilder multiMatchQueryBuilder = QueryBuilders.multiMatchQuery("spring 框架","name", "description")
.minimumShouldMatch("50%");
2、提升boost
匹配多个字段时可以提升字段的 boost(权重)来提高得分
MultiMatchQueryBuilder multiMatchQueryBuilder = QueryBuilders.multiMatchQuery("spring 框架",
"name", "description")
.minimumShouldMatch("50%");
multiMatchQueryBuilder.field("name",10);//提升权重10倍
6.2.7过滤器
过虑是针对搜索的结果进行过虑,过虑器主要判断的是文档是否匹配,不去计算和判断文档的匹配度得分,所以过虑器性能比查询要高,且方便缓存,推荐尽量使用过虑器去实现查询或者过虑器和查询共同使用。
过虑器在布尔查询中使用,下边是在搜索结果的基础上进行过虑:
{
"_source": ["name", "studymodel", "description", "price"],
"query": {
"bool": {
"must": [{
"multi_match": {
"query": "spring框架",
"minimum_should_match": "50%",
"fields": ["name^10", "description"]
}
}],
"filter": [{
"term": {
"studymodel": "201001"
}
},
{
"range": {
"price": {
"gte": 60,
"lte": 100
}
}
}
]
}
}
}
range:范围过虑,保留大于等于60 并且小于等于100的记录。
term :项匹配过虑,保留studymodel等于"201001"的记录。
注意:range和term一次只能对一个Field设置范围过虑。
// 布尔查询使用过虑器
@Test
public void testFilter() throws IOException {
SearchRequest searchRequest = new SearchRequest("demo_course");
searchRequest.types("doc");
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
//source源字段过虑
searchSourceBuilder.fetchSource(new String[]{"name","studymodel","price","description"},
new String[]{});
searchRequest.source(searchSourceBuilder);
//匹配关键字
MultiMatchQueryBuilder multiMatchQueryBuilder = QueryBuilders.multiMatchQuery("spring框架", "name", "description");
//设置匹配占比
multiMatchQueryBuilder.minimumShouldMatch("50%");
//提升另个字段的Boost值
multiMatchQueryBuilder.field("name",10);
searchSourceBuilder.query(multiMatchQueryBuilder);
//布尔查询
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
boolQueryBuilder.must(searchSourceBuilder.query());
//过虑
boolQueryBuilder.filter(QueryBuilders.termQuery("studymodel", "201001"));
boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").gte(60).lte(100));
SearchResponse searchResponse = client.search(searchRequest);
SearchHits hits = searchResponse.getHits();
SearchHit[] searchHits = hits.getHits();
for (SearchHit hit : searchHits) {
String index = hit.getIndex();
String type = hit.getType();
String id = hit.getId();
float score = hit.getScore();
String sourceAsString = hit.getSourceAsString();
Map<String, Object> sourceAsMap = hit.getSourceAsMap();
String name = (String) sourceAsMap.get("name");
String studymodel = (String) sourceAsMap.get("studymodel");
String description = (String) sourceAsMap.get("description");
System.out.println(name);
System.out.println(studymodel);
System.out.println(description);
}
}
6.2.7布尔查询
布尔查询对应于Lucene的BooleanQuery查询,实现将多个查询组合起来。
三个参数:
must:文档必须匹配must所包括的查询条件,相当于 “AND”
should:文档应该匹配should所包括的查询条件其中的一个或多个,相当于 “OR”
must_not:文档不能匹配must_not所包括的该查询条件,相当于“NOT”
分别使用 must、should、must_not测试下边的查询:
发送:POST http://localhost:9200/demo_course/doc/_search
{
"_source": ["name", "studymodel", "description"],
"from": 0,
"size": 1,
"query": {
"bool": {
"must": [{
"multi_match": {
"query": "spring框架",
"minimum_should_match": "50%",
"fields": ["name^10", "description"]
}
},
{
"term": {
"studymodel": "201001"
}
}
]
}
}
}
must:表示必须,多个查询条件必须都满足。(通常使用must)
should:表示或者,多个查询条件只要有一个满足即可。
must_not:表示非。
//BoolQuery ,将搜索关键字分词,拿分词去索引库搜索
@Test
public void testBoolQuery() throws IOException {
//创建搜索请求对象
SearchRequest searchRequest = new SearchRequest(index);
searchRequest.types(type);
//创建搜索源配置对象
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.fetchSource(new String[]{"name", "pic", "studymodel"}, new String[]{});
//multiQuery
String keyword = "spring开发框架";
MultiMatchQueryBuilder multiMatchQueryBuilder = QueryBuilders.multiMatchQuery("spring框架",
"name", "description")
.minimumShouldMatch("50%");
multiMatchQueryBuilder.field("name", 10);
//TermQuery
TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("studymodel", "201001");
// 布尔查询
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
boolQueryBuilder.must(multiMatchQueryBuilder);
boolQueryBuilder.must(termQueryBuilder);
//设置布尔查询对象
searchSourceBuilder.query(boolQueryBuilder);
searchRequest.source(searchSourceBuilder);//设置搜索源配置
SearchResponse searchResponse = client.search(searchRequest);
SearchHits hits = searchResponse.getHits();
SearchHit[] searchHits = hits.getHits();
for (SearchHit hit : searchHits) {
Map<String, Object> sourceAsMap = hit.getSourceAsMap();
System.out.println(sourceAsMap);
}
}
6.2.7排序查询
可以在字段上添加一个或多个排序,支持在keyword、date、float等类型上添加,text类型的字段上不允许添加排序。
发送 POST http://localhost:9200/demo_course/doc/_search
过虑0–10元价格范围的文档,并且对结果进行排序,先按studymodel降序,再按价格升序
{
"_source": ["name", "studymodel", "description", "price"],
"query": {
"bool": {
"filter": [{
"range": {
"price": {
"gte": 0,
"lte": 100
}
}
}]
}
},
"sort": [{
"studymodel": "desc"
},
{
"price": "asc"
}
]
}
//排序
@Test
public void testSort() throws IOException {
SearchRequest searchRequest = new SearchRequest(index);
searchRequest.types(type);
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
//source源字段过虑
searchSourceBuilder.fetchSource(new String[]{"name", "studymodel", "price", "description"},
new String[]{});
searchRequest.source(searchSourceBuilder);
//布尔查询
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
//过虑
boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").gte(0).lte(100));
// 排序
searchSourceBuilder.sort(new FieldSortBuilder("studymodel").order(SortOrder.DESC));
searchSourceBuilder.sort(new FieldSortBuilder("price").order(SortOrder.ASC));
SearchResponse searchResponse = client.search(searchRequest);
SearchHits hits = searchResponse.getHits();
SearchHit[] searchHits = hits.getHits();
for (SearchHit hit : searchHits) {
String index = hit.getIndex();
String type = hit.getType();
String id = hit.getId();
float score = hit.getScore();
String sourceAsString = hit.getSourceAsString();
Map<String, Object> sourceAsMap = hit.getSourceAsMap();
String name = (String) sourceAsMap.get("name");
String studymodel = (String) sourceAsMap.get("studymodel");
String description = (String) sourceAsMap.get("description");
System.out.println(name);
System.out.println(studymodel);
System.out.println(description);
}
}
6.2.7高亮查询
高亮显示可以将搜索结果一个或多个字突出显示,以便向用户展示匹配关键字的位置。
在搜索语句中添加highlight即可实现,如下:
Post: http://127.0.0.1:9200/demo_course/doc/_search
{
"_source": ["name", "studymodel", "description", "price"],
"query": {
"bool": {
"must": [{
"multi_match": {
"query": "开发框架",
"minimum_should_match": "50%",
"fields": ["name^10", "description"],
"type": "best_fields"
}
}],
"filter": [{
"range": {
"price": {
"gte": 0,
"lte": 100
}
}
}]
}
},
"sort": [{
"price": "asc"
}],
"highlight": {
"pre_tags": ["<tag1>"],
"post_tags": ["</tag2>"],
"fields": {
"name": {},
"description": {}
}
}
}
//高亮查询
@Test
public void testHighlight() throws IOException {
SearchRequest searchRequest = new SearchRequest(index);
searchRequest.types(type);
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
//source源字段过虑
searchSourceBuilder.fetchSource(new String[]{"name", "studymodel", "price", "description"},
new String[]{});
searchRequest.source(searchSourceBuilder);
//匹配关键字
MultiMatchQueryBuilder multiMatchQueryBuilder = QueryBuilders.multiMatchQuery("开发",
"name", "description");
searchSourceBuilder.query(multiMatchQueryBuilder);
//布尔查询
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
boolQueryBuilder.must(searchSourceBuilder.query());
//过虑
boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").gte(0).lte(100));
//排序
searchSourceBuilder.sort(new FieldSortBuilder("studymodel").order(SortOrder.DESC));
searchSourceBuilder.sort(new FieldSortBuilder("price").order(SortOrder.ASC));
//高亮设置
HighlightBuilder highlightBuilder = new HighlightBuilder();
highlightBuilder.preTags("<tag>");//设置前缀
highlightBuilder.postTags("</tag>");//设置后缀
// 设置高亮字段
highlightBuilder.fields().add(new HighlightBuilder.Field("name"));
// highlightBuilder.fields().add(new HighlightBuilder.Field("description"));
searchSourceBuilder.highlighter(highlightBuilder);
SearchResponse searchResponse = client.search(searchRequest);
SearchHits hits = searchResponse.getHits();
SearchHit[] searchHits = hits.getHits();
for (SearchHit hit : searchHits) {
Map<String, Object> sourceAsMap = hit.getSourceAsMap();
//名称
String name = (String) sourceAsMap.get("name");
//取出高亮字段内容
Map<String, HighlightField> highlightFields = hit.getHighlightFields();
if (highlightFields != null) {
HighlightField nameField = highlightFields.get("name");
if (nameField != null) {
Text[] fragments = nameField.getFragments();
StringBuffer stringBuffer = new StringBuffer();
for (Text str : fragments) {
stringBuffer.append(str.string());
}
name = stringBuffer.toString();
}
}
String index = hit.getIndex();
String type = hit.getType();
String id = hit.getId();
float score = hit.getScore();
String sourceAsString = hit.getSourceAsString();
String studymodel = (String) sourceAsMap.get("studymodel");
String description = (String) sourceAsMap.get("description");
System.out.println(name);
System.out.println(studymodel);
System.out.println(description);
}
}
个人主页-资源可下载PDF
完结,欢迎留言指正!