elasticSearch
1、认识elasticsearch
搜索引擎
搜索引擎是指根据一定的策略、运用特定的计算机程序从互联网上采集信息,在对信息进行组织和处理后,为用户提供检索服务,将检索的相关信息展示给用户的系统。搜索引擎是工作于互联网上的一门检索技术,它旨在提高人们获取搜集信息的速度,为人们提供更好的网络使用环境。从功能和原理上搜索引擎大致被分为全文搜索引擎、元搜索引擎、垂直搜索引擎和目录搜索引擎等四大类
分类
-
全文搜索引擎
一般网络用户适用于全文搜索引擎。这种搜索方式方便、简捷,并容易获得所有相关信息。但搜索到的信息过于庞杂,因此用户需要逐一浏览并甄别出所需信息。尤其在用户没有明确检索意图情况下,这种搜索方式非常有效
-
元搜索引擎
元搜索引擎适用于广泛、准确地收集信息。不同的全文搜索引擎由于其性能和信息反馈能力差异,导致其各有利弊。元搜索引擎的出现恰恰解决了这个问题,有利于各基本搜索引擎间的优势互补。而且本搜索方式有利于对基本搜索方式进行全局控制,引导全文搜索引擎的持续改善
-
垂直搜索引擎
垂直搜索引擎适用于有明确搜索意图情况下进行检索。例如,用户购买机票、火车票、汽车票时,或想要浏览网络视频资源时,都可以直接选用行业内专用搜索引擎,以准确、迅速获得相关信息。
-
目录搜索引擎
目录搜索引擎是网站内部常用的检索方式。本搜索方式旨在对网站内信息整合处理并分目录呈现给用户,但其缺点在于用户需预先了解本网站的内容,并熟悉其主要模块构成。总而观之,目录搜索方式的适应范围非常有限,且需要较高的人工成本来支持维护
常见全文搜索引擎框架
Lucene
Lucene的开发语言是Java,也是Java家族中最为出名的一个开源搜索引擎,在Java世界中已经是标准的全文检索程序,它提供了完整的查询引擎和索引引擎,没有中文分词引擎,需要自己去实现,因此用Lucene去做一个搜素引擎需要自己去架构,另外它不支持实时搜索。但是solr和elasticsearch都是基于Lucene封装。
-
优点
成熟的解决方案,有很多的成功案例。apache 顶级项目,正在持续快速的进步。庞大而活跃的开发社区,大量的开发人员。它只是一个类库,有足够的定制和优化空间:经过简单定制,就可以满足绝大部分常见的需求;经过优化,可以支持 10亿+ 量级的搜索。
-
缺点
需要额外的开发工作。所有的扩展,分布式,可靠性等都需要自己实现;非实时,从建索引到可以搜索中间有一个时间延迟,而当前的“近实时”(Lucene Near Real Time search)搜索方案的可扩展性有待进一步完善.
Apache Solr
Solr是一个高性能,采用Java开发,基于Lucene的全文搜索服务器。文档通过Http利用XML加到一个搜索集合中。查询该集合也是通过 http收到一个XML/JSON响应来实现。它的主要特性包括:高效、灵活的缓存功能,垂直搜索功能,高亮显示搜索结果,通过索引复制来提高可用性,提 供一套强大Data Schema来定义字段,类型和设置文本分析,提供基于Web的管理界面等。
-
优点
- Solr有一个更大、更成熟的用户、开发和贡献者社区。
- 支持添加多种格式的索引,如:HTML、PDF、微软 Office 系列软件格式以及 JSON、XML、CSV 等纯文本格式。
- Solr比较成熟、稳定。
- 不考虑建索引的同时进行搜索,速度更快。
-
缺点
建立索引时,搜索效率下降,实时索引搜索效率不高
ElasticSearch
ElasticSearch是一个基于Lucene构建的开源,分布式,RESTful搜索引擎。设计用于云计算中,能够达到实时搜索,稳定,可靠,快速,安装使用方便。支持通过HTTP使用JSON进行数据索引。
-
优点
-
Elasticsearch是分布式的。不需要其他组件,分发是实时的,被叫做”Push replication”。
-
Elasticsearch 完全支持 Apache Lucene 的接近实时的搜索。
-
处理多租户(multitenancy)不需要特殊配置,而Solr则需要更多的高级设置。
-
Elasticsearch 采用 Gateway 的概念,使得完备份更加简单。
各节点组成对等的网络结构,某些节点出现故障时会自动分配其他节点代替其进行工作。
-
-
缺点
还不够自动(不适合当前新的Index Warmup API) 索引预热API
ES or Solr
https://www.cnblogs.com/jajian/p/9801154.html
什么是elasticsearch
实时的全文搜索引擎(近实时:数据更新有1秒的延时)
在操作上,狭义的上的理解:专注于搜索的非关系型数据库,类似于mongodb
数据获取
1:爬虫获取数据
2:从关系型数据库同步数据
涉及到ES使用方式
数据处理
存储:
es是一种类似于非关系型数据库的方式存放数据
数据存储结构类似mongodb文档结构,但是比mongodb更为复杂
处理
涉及到数据分词,倒排等相关操作
数据检索
全文搜索
高亮搜索
条件搜索
elasticsearch应用场景
多条件搜索
站内搜索
地理位置搜索
elasticsearch的安装
ES服务器
检查是否配置 **java_home ** 变量
解压 elasticsearch-6.5.4 压缩包,双击启动解压后 bin 目录下的 elasticsearch.bat 文件
浏览器输入:http://localhost:9200,看到浏览器输出服务器的信息,表示安装成功,可以使用了
ES可视化客户端-head
观察ES数据
浏览器插件版本的安装:在谷歌的应用商店 chrome://apps/ 中找到 elasticsearch-head 插件,然后安装,使用该插件能比较直观的展示服务器中的数据
打开浏览器设置,点击扩展程序
打开右上角的 开发者模式
拖拽进文件即可
ES操作客户端-kibana
操作ES数据:操作就是类似于 Restful 接口风格
注意:路径不可带空格、中文
-
步骤1:下载kibana
注意:版本必须跟es一致
-
步骤2:修改配置
根 /config/kibana.yml ,都是默认情况,可以不用修改
-
步骤3:启动kibana ,双击 /bin/kibana.bat
-
步骤4:访问客户端 , http://localhost:5601
分词器
直接把压缩文件中的内容解压,然后放在 elasticsearch/plugins/analysis-ik 下,然后重启即可
有问题,参考:https://blog.csdn.net/wsms007/article/details/86569285
2、核心概念
索引(Index)
含有相同属性的文档集合
类型(Type)
索引可以定义一个或多个类型,文档必须属于一个类型
es6 之后要求一个索引只能包含一个类型
所以es6操作对对版本要求较高
映射
定义索引库中索引的 字段名称 及其 数据类型
Mapping,就是对索引库中索引的字段名称及其数据类型进行定义,类似于mysql中的表结构信息。不过es的mapping比数据库灵活很多,它可以动态识别字段。一般不需要指定mapping都可以,因为es会自动根据数据格式识别它的类型,如果你需要对某些字段添加特殊属性(如:定义使用其它分词器、是否分词、是否存储等),就必须手动添加mapping。
需要注意的是映射是不可修改的,一旦确定就不允许改动,在使用自动识别功能时,会以第一个存入的文档为参考来建立映射,后面存入的文档也必须符合该映射才能存入
文档(Document)
文档可以被索引的基本单位, 一般是json格式,包含一些field
列(Field)
es的最小单位,相当于数据的某个列
其他概念
逻辑概念:
- 近实时(NRT):数据更新有1秒的延时
- 节点(Node):单个的装有es服务并且提供故障专员的拓展服务器
- 集群(Cluster):一个集群就是由一个或多个node组织在一起,共同工作,共同分享整个数据,具有负载均衡功能的集群
物理概念:
- 分片(Shards):每个索引都有多个分片,每个分片是一个Lucene索引
- 备份(Replicas):拷贝一份分片就是完成分片的备份
分片:
(1)单个节点由于物理机硬件限制,存储的文档是有限的,如果一个索引包含海量文档,则不能在单个节点存储。ES提供分 片机制,同一个索引可以存储在不同分片(数据容器)中,这些分片又可以存储在集群中不同节点上
(2)分片分为 主分片(primary shard) 以及 从分片(replica shard)
(3)主分片与从分片关系:从分片只是主分片的一个副本,它用于提供数据的冗余副本
(4)从分片应用:在硬件故障时提供数据保护,同时服务于搜索和检索这种只读请求
(5)是否可变:索引中的主分片的数量在索引创建后就固定下来了,但是从分片的数量可以随时改变
(6)索引默认创建的分片:默认设置5个主分片和一组从分片(即每个主分片有一个从分片对应),但是从分片没有被启用 (主从分片在同一个节点上没有意义),因此集群健康值显示为黄色(yellow)
3、常规操作
索引操作
添加
语法:PUT /索引名
在没有特殊设置的情况下,默认有5个分片,1个备份,也可以通过请求参数的方式来指定
参数格式:
默认:
PUT my_index
明确指定:
PUT /my_index
{
"settings": {
"number_of_shards": 5, //设置5个片区
"number_of_replicas": 1 //设置1个备份
}
}
注意:
- 索引 不能有大写 字母
- 参数格式必须是标准的 json 格式
查看
#看单个
GET /索引名
#看所有
GET _cat/indices
删除
语法:DELETE /索引名
DELETE /索引名
类型-映射
创建
类型跟映射一起创建
语法:
PUT /索引名
{
"mappings": {
类型名: {
"properties": {
字段名: {
"type": 字段类型,
"analyzer": 分词器类型,
"search_analyzer": 分词器类型,
...
},
...
}
}
}
}
字段类型就是:数据类型
配置:analyzer search_analyzer ,前提是 type:text类型
示例:创建类型并设置映射(类似于mysql创建表)
# 创建一个索引,同时创建一个 user 类型
PUT /my_index
{
"mappings": {
"users":{
"properties":{
"id":{
"type":"long"
},
"name":{
"type":"keyword"
},
"age":{
"type":"integer"
}
}
}
}
}
查看
语法:
GET /索引名/_mapping
示例: 查看映射
GET /my_index/_mapping
4、文档操作
数据类型
https://www.jianshu.com/p/01f489c46c38
有很多很多
核心类型:
- text 类型:当一个字段是要被全文搜索的,比如Email内容、产品描述,应该使用text类型。设置text类型以后,字段内容会被分析,在生成倒排索引以前,字符串会被分析器分成一个一个词项。text类型的字段不用于排序,很少用于聚合。
- **keyword类型:**适用于索引结构化的字段,比如email地址、主机名、状态码和标签。如果字段需要进行过滤(比如查找已发布博客中status属性为published的文章)、排序、聚合。keyword类型的字段只能通过精确值搜索到。
数值类型:
- byte 、short、 integer 、doule 、float
- date 类型、boolean 类型、binary 类型、array 类型、object 类型、ip 类型
文档基本操作
文档添加
语法:
POST /索引名/类型名/文档ID
{
field1: value1,
field2: value2,
...
}
注意:
- 如果不指定 文档ID 的添加, es 会自动补齐添加一个字符串类型 id ,类似于MongoDB
- 操作时,如果指定文档 id, 并且索引库中已经存在, 则执行更新操作, 否则执行添加
- 当 索引 / 类型 / 映射 不存在时,会使用默认设置自动添加
- ES 中的数据一般是从别的数据库导入的,所以文档的ID 会沿用原数据库中的ID
**示例:**新增一个文档
POST /es_index/users/1
{
"id":1,
"name":"dafei",
"age":18
}
结果字段解释:
{
"_index" : "es_index", //所属索引
"_type" : "users", //所属类型
"_id" : "1", //文档ID
"_version" : 1, //乐观锁版本号
"result" : "created", //命令操作类型
"_shards" : { //分片相关信息
"total" : 2,
"successful" : 1,
"failed" : 0
},
"_seq_no" : 0,
"_primary_term" : 1
}
_source:数据内容
文档更新
语法:
PUT /索引名/类型名/文档ID
{
field1: value1,
field2: value2,
...
}
注意:
- 操作跟 POST 的指定 id 更新操作一样
- 如果不指定 id, 操作失败
- 注意,是全量更新,若未指定属性值,就会为空
示例: 替换一个文档
//注意是全量更新
PUT /es_index/users/1
{
"id":1,
"age":88
}
示例: 部分更新字段
POST /es_index/users/1/_update
{
"doc" : {
"name" : "aaaaa"
}
}
文档查看
语法:
# 根据ID查询
GET /索引名/类型名/文档ID
# 查询所有(基本查询语句)
GET /索引名/类型名/_search
结果字段解释:
took:耗时
_shards.total:分片总数
hits.total:查询到的数量
hits.max_score:最大匹配度
hits.hits:查询到的结果
hits.hits._score:匹配度
示例:
# 需求1:根据文档ID查询一个文档
GET /es_index/users/1
# 需求2:查询所有的文档
GET /es_index/users/_search #默认查询10个
文档删除
语法:
DELETE /索引名/类型名/文档ID
注意:
- es 中的删除并不会将数据真实删除,仅仅是修改数据中状态
- 清空内容,文档空间仍然存在,标记为删除状态
- 若再次添加相同 id 的文档,就会存储到此位置,并且仍会更新 version 乐观锁版本,说明真的就是更新操作
示例:
# 需求1:根据文档ID删除一个文档
DELETE /es_index/users/1
# 需求2:替换刚刚删除的文档
PUT /es_index/users/1
{
"id":1,
"name":"xiaofei",
"age":18
}
高级查询(DSL)
Elasticsearch基于JSON提供完整的查询DSL(Domain Specific Language:领域特定语言)来定义查询。
基本语法:
GET /索引名/类型名/_search
一般都是需要 配合查询参数 来使用的,配合不同的参数有不同的查询效果
Search APIs
搜索数据,查询语法多,功能强大
REST request URI:轻便快速的URI查询方法
REST request body:可以有许多限制条件的json格式查询方法
- “query”:在请求消息体中的query允许我们用Query DSL的方式查询。
- “term”:查询时判断某个document是否包含某个具体的值,不会对被查询的值进行分词查询
- “match”: 将被查询值进行分词,然后用评分机制(TF/IDF)进行打分
- “match_phrase”:查询指定段落
- “Bool”:结合其他真值查询,通常和must should mustnot(与或非)一起组合出复杂的查询
- “range”:查询时指定某个字段在某个特定的范围
- “from”:以一定的偏移量来查看我们检索的结果,缺省从检索的第一条数据开始显示
- “size”:指定检索结果中输出的数据条数,缺省为10条
- “sort”:允许我们将检索的结果以指定的字段进行排序显示
- “_source”:指定检索结果输出的字段
- “script_fields”:该类型允许我们通过一个脚本来计算document中不存在的值,比如我们需要计算install/click得到cti之类的
参数配置项可以参考博客:<https://www.jianshu.com/p/6333940621ec
数据准备
准备索引、类型
PUT /es_shop
{
"mappings": {
"shop_product": {
"properties": {
"id": {
"type": "long"
},
"title": {
"type": "text",
"analyzer": "ik_smart",
"search_analyzer": "ik_smart",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"price": {"type": "integer"},
"intro": {
"type": "text",
"analyzer": "ik_smart",
"search_analyzer": "ik_smart",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"brand": {
"type": "keyword"
}
}
}
}
}
准备文档内容:
POST /es_shop/shop_product/_bulk
{"create":{"_id": 1}}
{"id":1,"title":"Apple iPhone XR (A2108) 128GB 白色 移动联通电信4G手机 双卡双待","price":5299,"intro":"【iPhoneXR限时特惠!】6.1英寸视网膜显示屏,A12仿生芯片,面容识别,无线充电,支持双卡!选【换修无忧版】获 AppleCare 原厂服务,享只换不修!更有快速换机、保值换新、轻松月付!","brand":"Apple"}
{"create":{"_id": 2}}
{"id":2,"title":"Apple 2019款 Macbook Pro 13.3【带触控栏】八代i7 18G 256G RP645显卡 深空灰 苹果笔记本电脑 轻薄本 MUHN2CH/A","price":15299,"intro":"【八月精选】Pro2019年新品上市送三重好礼,现在购买领满8000减400元优惠神劵,劵后更优惠!","brand":"Apple"}
{"create":{"_id": 3}}
{"id":3,"title":"Apple iPad Air 3 2019年新款平板电脑 10.5英寸(64G WLAN版/A12芯片/Retina显示屏/MUUL2CH/A)金色","price":3788,"intro":"8月尊享好礼!买iPad即送蓝牙耳机!领券立减!多款产品支持手写笔!【新一代iPad,总有一款适合你】选【换修无忧版】获 AppleCare 原厂服务,享只换不修!更有快速换机、保值换新、轻松月付!","brand":"Apple"}
{"create":{"_id": 4}}
{"id":4,"title":"华为HUAWEI MateBook X Pro 2019款 英特尔酷睿i5 13.9英寸全面屏轻薄笔记本电脑(i5 8G 512G 3K 触控) 灰","price":7999,"intro":"3K全面屏开启无界视野;轻薄设计灵动有型,HuaweiShare一碰传","brand":"华为"}
{"create":{"_id": 5}}
{"id":5,"title":"华为 HUAWEI Mate20 X (5G) 7nm工艺5G旗舰芯片全面屏超大广角徕卡三摄8GB+256GB翡冷翠5G双模全网通手机","price":6199,"intro":"【5G双模,支持SA/NSA网络,7.2英寸全景巨屏,石墨烯液冷散热】5G先驱,极速体验。","brand":"华为"}
{"create":{"_id": 6}}
{"id":6,"title":"华为平板 M6 10.8英寸麒麟980影音娱乐平板电脑4GB+64GB WiFi(香槟金)","price":2299,"intro":"【华为暑期购】8月2日-4日,M5青春版指定爆款型号优惠100元,AI语音控制","brand":"华为"}
{"create":{"_id": 7}}
{"id":7,"title":"荣耀20 PRO DXOMARK全球第二高分 4800万四摄 双光学防抖 麒麟980 全网通4G 8GB+128GB 蓝水翡翠 拍照手机","price":3199,"intro":"白条6期免息!麒麟980,4800万全焦段AI四摄!荣耀20系列2699起,4800万超广角AI四摄!","brand":"荣耀"}
{"create":{"_id": 8}}
{"id":8,"title":"荣耀MagicBook Pro 16.1英寸全面屏轻薄性能笔记本电脑(酷睿i7 8G 512G MX250 IPS FHD 指纹解锁)冰河银","price":6199,"intro":"16.1英寸无界全面屏金属轻薄本,100%sRGB色域,全高清IPS防眩光护眼屏,14小时长续航,指纹一健开机登录,魔法一碰传高速传输。","brand":"荣耀"}
{"create":{"_id": 9}}
{"id":9,"title":"荣耀平板5 麒麟8核芯片 GT游戏加速 4G+128G 10.1英寸全高清屏影音平板电脑 WiFi版 冰川蓝","price":1549,"intro":"【爆款平板推荐】哈曼卡顿专业调音,10.1英寸全高清大屏,双喇叭立体环绕音,配置多重护眼,值得拥有!","brand":"荣耀"}
{"create":{"_id": 10}}
{"id":10,"title":"小米9 4800万超广角三摄 6GB+128GB全息幻彩蓝 骁龙855 全网通4G 双卡双待 水滴全面屏拍照智能游戏手机","price":2799,"intro":"限时优惠200,成交价2799!索尼4800万广角微距三摄,屏下指纹解锁!","brand":"小米"}
{"create":{"_id": 11}}
{"id":11,"title":"小米(MI)Pro 2019款 15.6英寸金属轻薄笔记本(第八代英特尔酷睿i7-8550U 16G 512GSSD MX250 2G独显) 深空灰","price":6899,"intro":"【PCIE固态硬盘、72%NTSC高色域全高清屏】B面康宁玻璃覆盖、16G双通道大内存、第八代酷睿I7处理器、专业级调校MX150","brand":"小米"}
{"create":{"_id": 12}}
{"id":12,"title":"联想(Lenovo)拯救者Y7000P 2019英特尔酷睿i7 15.6英寸游戏笔记本电脑(i7 9750H 16G 1T SSD GTX1660Ti 144Hz)","price":9299,"intro":"超大1T固态,升级双通道16G内存一步到位,GTX1660Ti电竞级独显,英特尔9代i7H高性能处理器,144Hz电竞屏窄边框!","brand":"联想"}
结果排序
参数格式:
GET /索引名/类型名/_search
{
"sort": [
{field: 排序规则},
...
]
}
排序规则:
- asc表示升序
- desc:表示降序
- 没有配置排序的情况下,默认按照评分降序排列
示例:
# 需求:查询所有商品
GET /es_shop/shop_product/_search
示例:
# 需求:查询所有商品按照价格降序排列
GET /es_shop/shop_product/_search
{
"sort": {
"price": {
"order": "desc"
}
}
}
# 简写
GET /es_shop/shop_product/_search
{
"sort": {
"price":"desc"
}
}
执行结果:
{
"took" : 335, //耗时
"timed_out" : false,
"_shards" : {
"total" : 5, //分片总数
"successful" : 5,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : 12, //查询到的数量
"max_score" : null, //最大匹配度
"hits" : [ //查询到的结果
{...},
... //仅会显示10条数据,若想显示全部,需要列表
]
}
}
示例:
# 多个列排序
GET /es_shop/shop_product/_search
{
"sort": [
{
"price": {
"order": "desc"
}
},
{
"id":{
"order":"desc"
}
}
]
}
分页查询
参数格式:
GET /索引名/类型名/_search
{
"from": start,
"size": pageSize
}
注意: es查询时,默认显示前10条
示例: 分页查询
# 需求:分页查询
# 第一页
GET /es_shop/shop_product/_search
{
"sort":{
"id":"asc"
},
"from": 0, # 从第 1 条开始
"size": 3 # 显示 3 条数据
}
# 第二页:
GET /es_shop/shop_product/_search
{
"sort":{
"id":"asc"
},
"from": 3, # 从第 4 条开始
"size": 3
}
示例: 分页查询商品按照价格id排列,显示第3页,每页显示3个
# 需求2:分页查询商品按照价格id排列,显示第3页,每页显示3个
GET /es_shop/shop_product/_search
{
"sort":{
"id":"asc"
},
"from":6,
"size": 3
}
检索查询
参数格式:
GET /索引名/类型名/_search
{
"query": {
检索方式: {field: value}
}
}
检索方式:
-
term 表示精确匹配,value值不会被分词器拆分,按照倒排索引匹配
-
match 表示全文检索,value值会被分词器拆分,然后去倒排索引中匹配
-
range 表示范围检索,其value值是一个对象,如{ “range”: {field: {比较规则: value, …}} }
比较规则有gt / gte / lt / lte 等
注意: term 和 match 都能用在数值和字符上,range用在数值上
1、term =
如果判断参数值是多个值,可以使用terms
参数格式:
GET /索引名/类型名/_search
{
"query": {
"term": {
field: value
}
}
}
**示例:**查询商品价格等于15299的商品
# 需求:查询商品价格等于15299的商品
GET /es_shop/shop_product/_search
{
"query": {
"term":{
"price": 15299
}
}
}
如果判断参数值是多个值,可以使用terms
示例: 查询id为1,2的商品信息
# 需求:查询id为1 ,2的商品信息
GET /es_shop/shop_product/_search
{
"query": {
"terms":{
"id": [1,2]
}
}
}
2、range > <
范围检索,其value值是一个对象,如 { "range": {field: {比较规则: value, ...}} }
比较规则有gt / gte / lt / lte 等
参数格式:
GET /索引名/类型名/_search
{
"query": {
"range":{
field: {
比较规则: value,
比较规则: value,
...
}
}
}
}
示例: 查询商品价格在5000~10000之间商品,按照价格升序排列
# 需求:查询商品价格在5000~10000之间商品,按照价格升序排列
GET /es_shop/shop_product/_search
{
"query": {
"range":{
"price":{
"gte":5000,
"lte":10000
}
}
},
"sort":{
"price":"asc"
}
}
3、match 包含
表示全文检索,也可也做精确查询,value值会被分词器拆分,然后去倒排索引中匹配
参数格式:
GET /索引名/类型名/_search
{
"query": {
"match": {
field: value
}
}
}
示例: 查询商品标题中符合"游戏 手机"的字样的商品 【暂时理解为模糊查询】
# 需求:查询商品标题中符合"游戏 手机"的字样的商品 【暂时理解为模糊查询】
GET /es_shop/shop_product/_search
{
"query": {
"match":{
"title": "游戏 手机"
}
}
}
注意: 查询出来之后,会将 title 列匹配 “游戏 手机” 的匹配度进行打分,分数越高(匹配越精确)的显示在前面
关键字查询 multi_match
multi_match:表示在多个字段间做检索,只要其中一个字段满足条件就能查询出来,多用在字段上
【暂时理解为模糊查询】
参数格式:
GET /索引名/类型名/_search
{
"query": {
"multi_match": {
"query": value,
"fields": [field1, field2, ...]
}
}
}
//有点类似
/*
select * from product where 列fields like "%field1%" or 列2 like "%field2%"
select * from product where 列1 like "%keyword%" or 列2 like "%keyword%"
*/
示例: 查询商品 (标题或简介) 中符合 (“蓝牙 指纹 双卡”) 的字样的商品
# 需求:查询商品标题或简介中符合 "蓝牙 指纹 双卡" 的字样的商品
GET /es_shop/shop_product/_search
{
"query": {
"multi_match":{
"query":"蓝牙 指纹 双卡",
"fields": ["title", "intro"]
}
}
}
逻辑查询
逻辑规则:must / should / must_not,相当于and / or / not
参数格式:
GET /索引名/类型名/_search
{
"query": {
"bool": {
逻辑规则: [
{检索方式: {field: value}},
...
],
...
}
}
}
示例: 查询商品 (标题) 中符合 ( “i7”) 的字样 (并且 ) (价格大于) 7000的商品
# 需求:查询商品标题中符合 "i7" 的字样并且价格大于7000的商品
GET /es_shop/shop_product/_search
{
"query": {
"bool": {
"must": [
{
"range": {
"price": {"gte": 7000}
}
},
{
"match": {
"title": "i7"
}
}
]
}
}
}
示例:查询商品标题中符合 “pro” 的字样或者价格在1000~3000的商品
# 需求:查询商品标题中符合"pro"的字样或者价格在1000~3000的商品
GET /es_shop/shop_product/_search
{
"query": {
"bool": {
"should": [
{"range": {"price": {"lte":1000 ,"gte": 3000}}},
{"match": {"title": "pro"}}
]
}
}
}
过滤查询
从效果上讲过滤查询和检索查询能做一样的效果
- 区别在于过滤查询不评分,结果能缓存;
- 检索查询要评分,结果不缓存
一般是不会直接使用过滤查询,都是在检索了一定数据的基础上再使用
https://blog.csdn.net/laoyang360/article/details/80468757
参数格式:
GET /索引名/类型名/_search
{
"query": {
"bool": {
"filter": [
{ 检索方式: { field: value } },
...
]
}
}
}
示例: 查询商品标题中符合 “pro” 的字样
# 需求:查询商品标题中符合 "pro" 的字样
GET /es_shop/shop_product/_search
{
"query": {
"bool": {
"filter": [
{"match": {"title": "pro"}}
]
}
}
}
示例: 查询商品标题中符合 “pro” 的字样
# 需求:查询商品标题中符合"pro"的字样
GET /es_shop/shop_product/_search
{
"query": {
"match": {"title": "pro"}
}
}
全文搜索
中文分词器
把文本内容按照标准进行切分,默认的是standard
标准切分standard: 该分词器按照单词切分,内容转变为小写,去掉标点,遇到每个中文字符都当成1个单词处理
后面会安装开源的中文分词器插件(ik)
示例: 使用默认分词器
# 使用默认分词器
GET /es_shop/_analyze
{
"text":"I am Groot" #结果: I、am、Groot
}
GET /es_shop/_analyze
{
"text":"英特尔酷睿i7处理器" #结果: 英、特、尔、酷、睿、i7、处、理、器
}
结论: 默认的分词器只能对英文正常分词(根据空格隔开分词),不能对中文正常分词(根据中文单个字分词)
IK分词器:
-
ik_max_word 【细力度分词】
会将文本做最细粒度的拆分
比如会将 “中华人民共和国人民大会堂” 拆分为 中华人民共和国、中华人民、中华、华人、人民共和国、人民、共和国、大会堂、大会、会堂等词语
-
ik_smart 【粗力度分词】
会做最粗粒度的拆分
比如会将 “中华人民共和国人民大会堂” 拆分为 中华人民共和国、人民大会堂
示例: 使用 IK分词器
# 使用 IK分词器
GET /es_shop/_analyze
{
"text":"I am Groot",
"analyzer":"ik_smart" #结果: I、am、Groot
}
GET /es_shop/_analyze
{
"text":"英特尔酷睿i7处理器", #结果: 英特尔、酷、睿、i7、处理器
"analyzer":"ik_smart"
}
GET /es_shop/_analyze
{
"text":"英特尔酷睿i7处理器", #结果: 英特尔、英、特、尔、酷睿、酷、睿、i7、处理器、处、理、器
"analyzer":"ik_max_word"
}
结论: 都能正常分词
拓展词库
最简单的方式就是找到 IK 插件中的 config/main.dic 文件,往里面添加新的词汇,然后重启服务器即可
倒排索引
es 搜索原理:
倒排索引源于实际应用中需要根据属性的值来查找记录。这种索引表中的每一项都包括一个属性值和具有该属性值的各记录的地址。由于不是由记录来确定属性值,而是由属性值来确定记录的位置,因而称为倒排索引(inverted index)
带有倒排索引的文件我们称为倒排索引文件,简称倒排文件(inverted file)
https://blog.csdn.net/starzhou/article/details/87519973
match
表示全文检索,也可也做精确查询,value值会被分词器拆分,然后去倒排索引中匹配
参数格式:
GET /索引名/类型名/_search
{
"query": {
"match": {field: value}
}
}
**示例: ** 查询商品 标题 中符合 “游戏 手机” 的字样的商品 【暂时理解为模糊查询】
# 需求:查询商品标题中符合"游戏 手机"的字样的商品 【暂时理解为模糊查询】
GET /es_shop/shop_product/_search
{
"query": {
"match":{
"title": "游戏 手机"
}
}
}
multi_match
multi_match:表示在多个字段间做检索,只要其中一个字段满足条件就能查询出来,多用在字段上
【暂时理解为模糊查询】
参数格式:
GET /索引名/类型名/_search
{
"query": {
"multi_match": {
"query": value,
"fields": [field1, field2, ...]
}
}
}
//有点类似
/*
select * from product where 列fields like "%field1%" or 列2 like "%field2%"
select * from product where 列1 like "%keyword%" or 列2 like "%keyword%"
*/
示例: 查询商品 (标题或简介) 中符合 (“蓝牙 指纹 双卡”) 的字样的商品
# 需求:查询商品标题或简介中符合 "蓝牙 指纹 双卡" 的字样的商品
GET /es_shop/shop_product/_search
{
"query": {
"multi_match":{
"query":"蓝牙 指纹 双卡",
"fields": ["title", "intro"]
}
}
}
高亮显示
highlight:表示高亮显示,需要在 fields 中配置哪些字段中检索到该内容需要高亮显示
必须配合检索 (term / match) 一起使用
如果没有指定 开始标签、结束标签,默认是加粗样式
GET /索引名/类型名/_search
{
"query": { ... },
"highlight": {
"fields": {
field1: {},
field2: {},
...
},
"pre_tags": 开始标签,
"post_tags" 结束标签
}
}
示例: 查询商品标题或简介中符合"蓝牙 指纹 双卡"的字样的商品,并且高亮显示
# 需求:查询商品标题或简介中符合"蓝牙 指纹 双卡"的字样的商品,并且高亮显示
GET /es_shop/shop_product/_search
{
"query": {
"multi_match":{
"query":"蓝牙 指纹 双卡",
"fields": ["title", "intro"]
}
},
"highlight": {
"fields": {
"title": {},
"intro": {}
},
"pre_tags": "<span style='color:red;'>",
"post_tags": "</span>"
}
}
5、springboot集成
依赖
注意,SpringBoot 的版本是 2.2.6 ,对应es版本
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.9.4</version>
</dependency>
配置
#application.properties
# 配置集群名称,名称写错会连不上服务器,默认elasticsearch
spring.data.elasticsearch.cluster-name=elasticsearch
# 配置集群节点
spring.data.elasticsearch.cluster-nodes=localhost:9300
#是否开启本地存储
spring.data.elasticsearch.repositories.enabled=true
过期原因:
es 官方打算在 es7 设置 TransportClient 过期,es8之后抛弃,直接使用 High Level REST Client 替换TransportClient
https://docs.spring.io/spring-data/elasticsearch/docs/3.2.3.RELEASE/reference/html/#reference
domain
@Getter
@Setter
@ToString
@NoArgsConstructor
@AllArgsConstructor
@Document(indexName="es_shop", type="shop_product")
public class Product {
@Id
private String id;
//表名分词时使用的分词器
@Field(analyzer="ik_smart",searchAnalyzer="ik_smart",type = FieldType.Text)
private String title;
private Integer price;
@Field(analyzer="ik_smart",searchAnalyzer="ik_smart",type = FieldType.Text)
private String intro;
@Field(type=FieldType.Keyword)
private String brand;
}
CRUD
@Repository
public interface ProductRepository extends ElasticsearchRepository<Product, String>{}
public interface IProductService {
void save(Product product);
void update(Product product);
void delete(String id);
Product get(String id);
List<Product> list();
}
ElasticsearchTemplate
@Service
public class ProductServiceImpl implements IProductService {
@Autowired
private ProductRepository repository;
@Autowired
private ElasticsearchTemplate template;
@Override
public void save(Product product) {
repository.save(product);
}
@Override
public void update(Product product) {
repository.save(product);
}
@Override
public void delete(String id) {
repository.deleteById(id);
}
@Override
public Product get(String id) {
//return repository.findById(id).get();
return repository.findById(id).orElse(null);
}
@Override
public List<Product> list() {
Iterable<Product> all = repository.findAll();
List<Product> p = new ArrayList<>();
all.forEach(a->p.add(a));
return p;
}
}
测试
@SpringBootTest
public class ElasticsearchDemoApplicationTests {
@Autowired
private IProductService productService;
@Test
public void testSave() {
Product p = new Product();
p.setBrand("dafei");
p.setIntro("dafei手机");
p.setPrice(1000);
p.setTitle("全球最帅的手机");
productService.save(p);
}
@Test
public void testUpate() {
Product p = new Product();
p.setId("ue5r1m4BXlaPW5P0TegF");
p.setBrand("dafei");
p.setIntro("dafei手机");
p.setPrice(1000);
p.setTitle("全球最sb的手机");
productService.update(p);
}
@Test
public void testDelete() {
productService.delete("ue5r1m4BXlaPW5P0TegF");
}
@Test
public void testGet() {
System.out.println(productService.get("ue5r1m4BXlaPW5P0TegF"));
}
@Test
public void testList() {
System.err.println(productService.list());
}
}
各类查询
page_sort
// 分页查询文档按照价格降序排列,显示第2页,每页显示3个
@Test
public void testQuery1() throws Exception {
/*
{
"sort":{"price":"desc"},
"from": 3,
"size": 3
}
*/
//构建者模式
// DSL 语句构建对象(封装对象)
NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();
//拼接条件
builder.withPageable(PageRequest.of(1, 3));
builder.withSort(SortBuilders.fieldSort("price").order(SortOrder.DESC));
//Pageable pageable = PageRequest.of(1, 3, Sort.Direction.DESC, "price");
//builder.withPageable(pageable);
Page<Product> search = repostitory.search(builder.build());
System.out.println(search.getTotalElements()); //总记录数
System.out.println(search.getTotalPages()); //总页数
search.forEach(System.out::println);
}
term
// 查询商品价格等于15299的商品
@Test
public void testQuery2() throws Exception {
/**
* {
* query:{
* term:{price:15299}
* }
* }
*/
NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();
//拼接条件
builder.withQuery(
QueryBuilders.termQuery("price", 15299)
);
Page<Product> search = repostitory.search(builder.build());
search.forEach(System.out::println);
}
range
// 查询商品价格在5000~9000之间商品,按照价格升序排列
@Test
public void testQuery4() throws Exception {
/**
* {
* query:{
* range:{price:{gt:5000, lt:9000}}
* }
* }
*/
NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();
//拼接条件
builder.withQuery(
QueryBuilders.rangeQuery("price").gte(5000).lte(9000)
);
builder.withSort(SortBuilders.fieldSort("price").order(SortOrder.ASC));
Page<Product> search = repostitory.search(builder.build());
search.forEach(System.out::println);
}
match
// 查询商品标题中符合"游戏 手机"的字样的商品
@Test
public void testQuery2() throws Exception {
/**
* {
* query:{
* match:{title:"游戏 手机"}
* }
* }
*/
NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();
//拼接条件
builder.withQuery(
QueryBuilders.matchQuery("title", "游戏 手机")
);
Page<Product> search = repostitory.search(builder.build());
search.forEach(System.out::println);
}
multi_match
// 查询商品标题或简介中符合"蓝牙 指纹 双卡"的字样的商品
@Test
public void testQuery5() throws Exception {
/**
* {
* query:{
* multi_match:{
* "query":"蓝牙 指纹 双卡",
* "fields":["title", "intro"]
* }
* }
* }
*/
NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();
//拼接条件
builder.withQuery(
QueryBuilders.multiMatchQuery("蓝牙 指纹 双卡", "title", "intro")
);
Page<Product> search = repostitory.search(builder.build());
search.forEach(System.out::println);
}
must
// 查询商品标题中符合"i7"的字样 并且 价格大于7000的商品
@Test
public void testQuery6() throws Exception {
/**
* {
* query:{
* bool:{
* must:[
* {
* match: {title:"i7"}
* },
* {
* range: {price: {gt:7000}}
* }
* ]
* }
* }
* }
*/
NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();
//拼接条件
builder.withQuery(
QueryBuilders.boolQuery()
.must(QueryBuilders.matchQuery("title", "i7"))
.must(QueryBuilders.rangeQuery("price").gt(7000))
);
Page<Product> search = repostitory.search(builder.build());
search.forEach(System.out::println);
}
should
// 查询商品标题中符合"pro"的字样或者价格在1000~3000的商品
@Test
public void testQuery7() throws Exception {
/**
* {
* query:{
* bool:{
* should:[
* {
* match: {title:"pro"}
* },
* {
* range: {price: {gt:1000, lt:3000}}
* }
* ]
* }
* }
* }
*/
NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();
//拼接条件
builder.withQuery(
QueryBuilders.boolQuery()
.should(QueryBuilders.matchQuery("title", "pro"))
.should(QueryBuilders.rangeQuery("price").gte(1000).lte(3000))
);
Page<Product> search = repostitory.search(builder.build());
search.forEach(System.out::println);
}
高亮显示
@Autowired
private ElasticsearchTemplate template;
// 查询商品标题或简介中符合"蓝牙 指纹 双卡"的字样的商品,并且高亮显示
@Test
public void testHighlight() throws Exception {
// Java与JSON互转的工具对象
ObjectMapper mapper = new ObjectMapper();
NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();
// 设置查询哪个索引中的哪个类型
builder.withIndices("es_shop").withTypes("shop_product");
builder.withQuery(QueryBuilders.multiMatchQuery("蓝牙 指纹 双卡","title","intro"));
builder.withHighlightFields(
new HighlightBuilder.Field("title")
.preTags("<span style='color:red'>").postTags("</span>"),
new HighlightBuilder.Field("intro")
.preTags("<span style='color:red'>").postTags("</span>")
);
AggregatedPage<Product> search = template.queryForPage(builder.build(),
Product.class,
new SearchResultMapper(){
@Override
public <T> AggregatedPage<T> mapResults(SearchResponse response, Class<T> clazz, Pageable pageable) {
List<T> list = new ArrayList<>();
for (SearchHit hit : response.getHits().getHits()) {
list.add(mapSearchHit(hit, clazz));
}
long total = response.getHits().totalHits;
return new AggregatedPageImpl<>(list, pageable, total);
}
@Override
public <T> T mapSearchHit(SearchHit searchHit, Class<T> type) {
T t = null;
try {
t = mapper.readValue(searchHit.getSourceAsString(), type);
for (HighlightField field : searchHit.getHighlightFields().values()) {
// 替换需要高亮显示的字段,用到Apache的BeanUtils工具
BeanUtils.setProperty(t, field.getName(), field.getFragments()[0].string());
}
}catch (Exception e){
e.printStackTrace();
return null;
}
return t;
}
});
search.forEach(System.out::println);
}