ElasticSearch介绍
ES是一个使用Java语言并且基于Lucene编写的搜索引擎框架,它提供了分布式的全文搜索功能,提供了一个统一的基于RESTful风格的WEB接口,官方客户端也对多种语言都提供了相应的API。
- Lucene:本身就是一个搜索引擎的底层。直接使用成本高。官网地址:https://lucene.apache.org/
- 分布式:突出的横向扩展能力(集群)
- 全文检索:将一段词语进行分词,并且将分出的单个词语统一的放到分词库中,在搜索时,根据关键字去分词库中检索,找到匹配的内容。(倒排索引)eg:将“我爱编程”这个词语拆分成“我”、“爱”、“编程”,在搜索“爱我中华”的时候会将搜索内容拆分成“爱”、“我”、“中华”等,去分词库中查询有没有匹配的单个词。
- RESTful风格接口:操作简单,只需要发送一个HTTP请求,并且根据请求方式以及携带参数的不同来执行不同的操作。
- 倒排索引
- 将存放的数据,以一定的方式进行分词,并且将分词的内容存放到一个单独的分词库中。
- 当用户去查询数据时,会将用户查询的关键字进行分词。
- 然后去分词库匹配内容,最终得到数据的id标识。
- 根据id标识去存放数据的位置拉取到指定的数据。
ElasticSearch安装
- docker-compose的安装https://docs.docker.com/compose/install/
- 使用docker-compose安装es和kibana
- 在/opt/es_docker下新建docker-compose.yml,内容如下:
version: '2'
services:
elasticsearch:
container_name: elasticsearch
image: daocloud.io/library/elasticsearch:7.7.0
ports:
- "9200:9200"
environment:
- "ES_JAVA_OPTS=-Xms64m -Xmx128m"
- "discovery.type=single-node"
- "COMPOSE_PROJECT_NAME=elasticsearch-server"
restart: always
kibana:
container_name: kibana
image: daocloud.io/library/kibana:7.7.0
ports:
- "5601:5601"
restart: always
environment:
- ELASTICSEARCH_HOSTS=http://192.168.127.188:9200
注意填写自己的IP地址。
2. 执行docker-compose up -d
3. 验证安装成功
分别在浏览器中输入http://192.168.127.188:9200/、http://192.168.127.188:5601/当看见以下内容时说明安装成功
IK分词器安装
ES默认的分词器会将每一个中文都进行分词,这样分词就没有什么意义了。
- github地址:https://github.com/medcl/elasticsearch-analysis-ik
- tag选择对应的版本,复制链接地址
- 进入ES容器/bin/elasticsearch-plugin目录下,执行install+链接地址,下载分词器,安装成功后重启ES
- 测试分词结果
中文文档https://learnku.com/docs/elasticsearch73/7.3
ES基本操作
分片和备份
ES中Field支持的类型
字符串:
- text:被用于全文检索,将当前Field进行分词。eg:商品描述。
- keyword:当前Field不会被分词。eg:地名。
数值: - long
- integer
- short
- byte
- double
- float
- half_float
- scaled_float:根据一个long和scaled来表达一个浮点类型,eg:long=345,scaled=100,对应结果3.45
时间: - date:可以指定具体的格式
布尔类型: - boolean
二进制类型: - binary类型支持Base64 encode string
范围类型: - long_range:指定gt,lt,gte,lte即可
- float_range
- double_range
- date_range
- ip_range
经纬度类型: - geo_point:用来存储经纬度
IP类型 - ip_range:可以存储IPV4或者IPV6
其他数据类型参考官网:https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping-types.html
创建索引并指定数据结构
PUT /book
{
"settings": {
"number_of_replicas": 1, #备份数
"number_of_shards": 5 #分片数
},
# 指定数据结构
"mappings": {
# 文档存储的Field
"properties": {
# Field名
"name": {
# 类型
"type": "text",
# 指定分词器
"analyzer": "ik_max_word",
# 指定当前Field可以作为查询条件
"index": true,
# 是否需要额外存储
"store": false
},
"author": {
"type": "keyword"
},
"count": {
"type": "long"
},
"onSale": {
"type": "date",
"format": [
# 时间格式化方式
"yyyy-MM-dd HH:mm:ss"
]
},
"desc": {
"type": "text",
"analyzer": "ik_max_word"
}
}
}
}
文档操作
文档在ES服务中的唯一标识,_index,_doc,_id三个内容为组合,锁定一个文档。
新建文档
# 添加文档,自动生成id
POST /book/_doc
{
"name":"风声",
"author":"麦家",
"onSale":"2020-07-23 00:00:00",
"desc":"经历过大孤独、大绝望的人,会懂得《风声》给你的大坚韧和大智慧。",
"count":123902
}
# 添加文档,手动指定id
PUT /book/_doc/1
{
"name":"繁花",
"author":"金宇澄",
"onSale":"2010-02-12 00:00:00",
"desc":"矛盾文学奖获奖作品",
"count":83902
}
修改文档
#覆盖修改
PUT /book/_doc/1
{
"name":"繁花",
"author":"金宇澄",
"onSale":"2010-02-12 00:00:00",
"desc":"矛盾文学奖获奖作品",
"count":2383902
}
#修改文档,基于doc方式
POST /book/_doc/1/_update
{
"doc":{
"count":1234567
}
}
删除文档
# 根据id删除文档
DELETE /book/_doc/tOMn73UB90LCY1QIu_nJ
Java操作ES的几种方式可以看看这篇文章
https://blog.youkuaiyun.com/jacksonary/article/details/82729556
ES的各种查询
term&terms查询
term查询代表完全匹配,搜索之前不会对搜索的关键字进行分词,直接去分词库中匹配,找到相应的文档。
terms和term的查询机制是一样的,都不会将指定的关键字进行分词,直接去分词库中匹配,找到相应的文档内容。terms是用一个字段去匹配多个值的时候使用。
term:where province = 北京
terms:where province = 北京 or province = 深圳
match查询
match查询会根据查询的字段类型不一样,采用不同的查询方式。
- 查询的是日期或者数值的话,将会把查询的字符串内容转换为日期或者数值对待。
- 如果查询的是一个不能被分词的内容(keyword),match查询不会对指定的查询关键字进行分词。
- 如果查询的内容是一个可以被分词的内容(text),match会将指定的查询内容根据一定的方式分词,去分词库中匹配指定的内容。
match查询,实际底层就是多个term查询,将多个term查询的结果分装到了一起。
指定一个Field作为筛选条件
布尔match查询
基于一个Field匹配的内容,采用and或者or的方式连接
multi_match查询
match针对一个filed做检索,multi_match针对多个field进行检索,多个filed对应一个text,多个字段是or的关系。
match_all查询
查询全部内容,不指定任何查询条件。
其他查询
id查询
根据id查询 where id = ?
ids查询
根据多个id查询,where id in (?,?,…)
prefix查询
前缀查询,可以通过一个关键字去指定一个分词(text)field的前缀,从而查询到指定的文档。where field like ?%
fuzzy查询
模糊查询,我们输入大概的字符,ES就可以根据输入的内容大概去匹配一下结果。
可以通过prefix_length指定需要精确匹配的长度。
wildcard查询
通配查询,可以在查询时,在字符串中指定通配符*和占位符?
range查询
范围查询,只针对数值类型,对某一个filed进行大于或者小于的范围指定。
regexp查询
正则查询,通过正则表达式匹配内容。
ps:prefix,fuzzy,wildcard和regexp查询效率相对term,terms比较低,要求效率比较高时避免使用。
深分页Scroll
ES对from+size是有限制的,from和size二者之和不能超过1W
查询原理:
from+size在ES查询数据的方式:
- 将用户指定的关键字进行分词。
- 将分词结果去分词库中检索,得到多个文档的id。
- 去各个分片中拉取指定的数据。(耗时较长)
- 将数据根据score进行排序。(耗时较长)
- 根据from的值,将查询到的数据舍弃一部分。
- 返回结果
scroll+size在ES查询数据的方式:
- 将用户指定的关键字进行分词。
- 将分词结果去分词库中检索,得到多个文档的id。
- 将文档的id存放在一个ES的上下文中。
- 根据指定的size去ES中检索指定个数的数据,拿到数据的文档id,会从上下文中移除。
- 如果需要下一页数据,直接去ES的上下文中找后续内容。(重复4、5)
scroll查询方式,不适合做实时的查询(将文档id存在内存中)
# 执行scroll查询,zhiidng返回第一页数据,并且将文档id信息存放在ES上下文中,指定有效时长1分钟
GET province/_search?scroll=1m #指定文档id在内存中的存储时间
{
"query": { #查询条件
"match_all": {}
},
"size": 2, #每一页大小
"sort": [ #排序
{
"code": {
"order": "asc"
}
}
]
}
# 根据上一页返回的scroll,查询下一页数据,同时指定文档id在内存中的有效时长
GET _search/scroll
{
"scroll_id":"FGluY2x1ZGVfY29udGV4dF91dWlkDnF1ZXJ5VGhlbkZldGNoBRRoYTR1RkhZQnI5Z1BPU0dWMnhZcAAAAAAAAADZFm9NUWx2YVM4U2E2M1NsZGt2VmRuMXcUaHE0dUZIWUJyOWdQT1NHVjJ4WXAAAAAAAAAA2xZvTVFsdmFTOFNhNjNTbGRrdlZkbjF3FGlLNHVGSFlCcjlnUE9TR1YyeFlwAAAAAAAAANwWb01RbHZhUzhTYTYzU2xka3ZWZG4xdxRoNjR1RkhZQnI5Z1BPU0dWMnhZcAAAAAAAAADaFm9NUWx2YVM4U2E2M1NsZGt2VmRuMXcUaWE0dUZIWUJyOWdQT1NHVjJ4WXAAAAAAAAAA3RZvTVFsdmFTOFNhNjNTbGRrdlZkbjF3",
"scroll":"1m"
}
# 删除scroll在ES上下文中的数据
DELETE _search/scroll/FGluY2x1ZGVfY29udGV4dF91dWlkDnF1ZXJ5VGhlbkZldGNoBRRoYTR1RkhZQnI5Z1BPU0dWMnhZcAAAAAAAAADZFm9NUWx2YVM4U2E2M1NsZGt2VmRuMXcUaHE0dUZIWUJyOWdQT1NHVjJ4WXAAAAAAAAAA2xZvTVFsdmFTOFNhNjNTbGRrdlZkbjF3FGlLNHVGSFlCcjlnUE9TR1YyeFlwAAAAAAAAANwWb01RbHZhUzhTYTYzU2xka3ZWZG4xdxRoNjR1RkhZQnI5Z1BPU0dWMnhZcAAAAAAAAADaFm9NUWx2YVM4U2E2M1NsZGt2VmRuMXcUaWE0dUZIWUJyOWdQT1NHVjJ4WXAAAAAAAAAA3RZvTVFsdmFTOFNhNjNTbGRrdlZkbjF3
delete_by_query
根据term,match等查询方式删除文档
PS:如果需要删除的内容是index下的大部分数据(删除比较耗时),推荐创建一个新的index,将保留的文档内容添加到新的索引
复合查询
bool查询
复合过滤器,可以将多个查询条件以一定的逻辑组合在一起。
- must:表示必须匹配。
- must_not:表示必须不匹配。
- should:当查询中没有must语句时,至少需要匹配一个should。默认情况下should语句可以一个都不匹配。
booting查询
boosting查询会影响查询后的score
- positive:只有匹配上positive的查询内容,才会被放到返回的结果集中。
- negative:如果同时匹配上了positive和negative,就会降低文档的score。
- negative_boost:指定系数,必须小于1.0。同时匹配positive和negative分数乘以negative_boost。
- 必须同时指定以上三个条件。
查询时的分数计算:
- 搜索的关键字在文档中出现的频次越高,分数越高。
- 文档的内容越短,分数越高。
- 搜索时指定的关键字会被分词,被分词的内容,被分词库匹配的个数越多,分数越高。
filter查询
query:根据查询条件,计算文档的匹配度得到一个分数,并且根据分数进行排序,不会做缓存。
filter:根据查询条件去查询文档,不会计算分数,而且会对经常被过滤的数据进行缓存。
高亮查询
高亮查询以一定的特殊样式将用户输入的关键字展示出来。
ES的highlight属性
和query同级别
fragment_size:指定高亮数据展示多少字符。
pre_tag:指定前缀标签,eg:
post_tag:指定后缀标签,eg:
fields:指定哪些field以高亮形式放回
聚合查询
ES的聚合查询相比MYSQL要强大,ES提供的数据统计方式多种多样。
GET index/_search
{
"aggs":{ #聚合查询关键字
"agg":{ #聚合查询的名字
"agg_type":{ #聚合查询的类型
"属性":"值"
}
}
}
}
去重统计
cardinality,将返回文档中的一个指定filed去重,统计一共有多少条数据。
范围统计
统计指定field在一定范围内出现的文档个数。可以统计数值类型range、时间类型date_range、IP类型ip_range。
from:>=
to:<
统计聚合查询
extended_stats,查询指定field的最大值、最小值、平均值、平方和…
地图经纬度搜索
检索方式
- geo_distance:以一个点为中心,一定距离为半径的圆,获取在圆形内的全部数据。
- geo_bounding_box:以两点连线为对角线确定一个矩形,获取在矩形内的全部数据。
- geo_polygon:以多个点确定一个多边形,获取多边形内的全部数据。
geo_distance
GET city/_search
{
"query": {
"geo_distance": {
"location": { #指定属性,确定一个点
"lon": 116.397128,
"lat": 39.916527
},
"distance": 3000, #确定半径
"distance_type": "arc" #确定形状
}
}
}
geo_bounding_box
GET city/_search
{
"query": {
"geo_bounding_box": {
"location": {
"top_left": { #左上角坐标
"lon": 115.06640900585936,
"lat": 40.595651124351214
},
"bottom_right": { #右下角坐标
"lon": 116.6237210175781,
"lat": 39.69709306733553
}
}
}
}
}