elasticsearch 简单使用
时间:2023-02-06 18:00:00
1. 版本 信息
{
"name" : "74c1872a9b2d", "cluster_name" : "docker-cluster", "cluster_uuid" : "NtcKAJTdTd6rU-NTzwnrSg", "version" : {
"number" : "8.1.0", "build_flavor" : "default", "build_type" : "docker", "build_hash" : "3700f7679f7d95e36da0b43762189bab189bc53a", "build_date" : "2022-03-03T14:20:00.690422633Z", "build_snapshot" : false, "lucene_version" : "9.0.0", "minimum_wire_compatibility_version" : "7.17.0", "minimum_index_compatibility_version" : "7.0.0" }, "tagline" : "You Know, for Search" }
2. 字段属性
2.1. 常用属性
属性 | 描述 |
---|---|
type | 数据类型 |
index | 默认是否创建索引 true |
analyze | 分词器通常用于中文 ik 分词器的:ik_smart 或 ik_max_word |
format | 数据格式化,一般需要配置日期类型,可以配置多个,比如 yyyy-MM-dd HH:mm:ss || yyyy-MM-dd |
search_analyzer | 查询时使用的分词器不配置默认使用 analyze 的配置项 |
properties | 嵌套子字段 |
2.2. 数据类型
数据类型 | 说明 |
---|---|
字符串 | keyword:关键词类型一般作为查询条件 text:文本类型,一般是一段文字,需要分词查询等 |
数字 | long、integer、short、byte、double、float、half_float、scaled_float |
日期 | date,一般使用时都会匹配 format 属性 |
日期纳秒 | date_nacos |
布尔 | boolean |
二进制 | binary |
范围 | integer_range、float_range、long_range、double_range、date_range |
经纬度 | geo_point,{lat:维度,lon: 经度} 其中,北纬为正,南纬为负,东经为负,西经为负 |
经纬范围 | geo_shape,多边形多边形 |
网络地址 | ip |
数组 | es 中数组不需一个特定的数据类型,任何字段都默认可以包含一个或多个值 |
嵌套对象 | object |
嵌套数组 | nested |
3. 分词器
3.1. 分词器组成结构
我们可以通过下面的三个部分组成自己想要的分词器效果
组成部分 | 说明 |
---|---|
Character Filter(字符过滤器) | 该部分负责对数据的预处理,比如去除特殊字符,转化图标为文字等 |
Tokenizer(分词器) | 该部分负责对一段文字进行分词,得到若干个词元 |
Token Filter(词元处理器) | 该部分负责对词元进一步加工,比如转化为拼音,首字母大写等等 |
3.2. 内置的分词器
分词器 | 说明 |
---|---|
standard | 去除绝大多数标点符号,英文按单词分割,中文按单个字分割,对中文词汇不是很友好 |
simple | 去除所有非字母字符,英文按标点与单词分割并转为小写,中文按标点分割 |
whitespace | 按空格切分,英文不转换大小写 |
stop | 类似于 simple 分词器,去除停用词(默认英文) |
keyword | 不分词,原样返回 |
pattern | 正则分割,默认对所有非单词(非英文)字符分割 \w+ |
language | 语言分词器,未实现中文 |
fingerprint | 指纹算法分词器,对内容排序,并去重,最终只生成一个词条 |
3.3. 需要安装的常用分词器
分词器 | 说明 |
---|---|
ik_smart | 支持中文,粗粒度拆分 |
ik_max_word | 支持中文,最细粒度拆分 |
pinyin | 支持中文转拼音 |
3.4. 测试分词器
-- 非
自定义分词器测试 GET /_analyze { "analyzer": "my_analyzer", "text": "一段文本" } -- 自定义分词器的测试,需要指定索引名称,在哪个索引里配置的自定义分词器就用哪个索引 GET /索引名称/_analyze { "analyzer": "my_analyzer", "text": "一段文本" }
4. 索引操作
4.1. 创建索引
4.1.1. 分词器要求
- 按照中文词语或者英文单词拆分文本
- 中文汉字转化为拼音
- 中文汉字转化为每个字的拼音首字母连接,如 拼音 -> py
PUT /study
{
"settings": {
"analysis": {
"analyzer": {
"my_analyzer": {
"tokenizer": "ik_max_word",
"filter": "pinyin"
}
},
"filter": {
"pinyin": {
"type": "pinyin",
"keep_full_pinyin": false,
"keep_joined_full_pinyin": true,
"keep_original": true,
"remove_duplicated_term": true,
"limit_first_letter_length": 16,
"none_chinese_pinyin_tokenize": false
}
}
}
},
"mappings": {
}
}
拼音分词器有很多配置项,大致如下
- keep_first_letter:启用此选项时,例如:刘德华 > ldh,默认值:true
- keep_separate_first_letter:启用此选项时,将单独保留首字母,例如:刘德华 > l,默认:false,注意:查询结果可能过于模糊
- limit_first_letter_length:设置 first_letter 结果的最大长度,默认值:16
- keep_full_pinyin:启用此选项时,例如:刘德华 > [ liu, de, hua],默认值:true
- keep_joined_full_pinyin:启用此选项时,例如:刘德华 > [ liudehua],默认值:false
- keep_none_chinese:结果中保留非中文字母或数字,默认值:true
- keep_none_chinese_together:将非中文字母保持在一起,默认:true,例如:DJ音乐家 > DJyinyuejia
- keep_none_chinese_in_first_letter:首字母保留非中文字母,例如:刘德华AT2016 > ldhat2016,默认:true
- keep_none_chinese_in_joined_full_pinyin:保留非中文字母加入完整拼音,例如:刘德华2016 > liudehua2016,默认:false
- none_chinese_pinyin_tokenize:如果是拼音,则将非中文字母分成单独的拼音词,默认:true,例如:liudehuaalibaba13zhuanghan > liu, de, hua, a, li, ba, ba, 13, zhuang, han, 注意:需要先启用 keep_none_chinese 和 keep_none_chinese_together
- keep_original:启用此选项时,也将保留原始汉字,默认值:false
- lowercase:非中文字母转小写,默认:true
- trim_whitespace:去空格,默认真
- remove_duplicated_term:删除重复的术语以保存索引,例如:de的 > de,默认:false,注意:位置相关的查询可能会受到影响
- ignore_pinyin_offset:6.0以后,offset 被严格限制,tokens 重叠是不允许的,有了这个参数,tokens重叠会被忽略 offset,请注意,所有位置相关的查询或高亮都会变得不正确,你应该使用多个字段并为不同的设置指定不同的设置查询目的。如果需要偏移,请将其设置为 false。默认值:true。
4.1.2. 建一个索引
下方所有案例全部采用这个索引库:study
PUT /study
{
"settings": {
"analysis": {
"analyzer": {
"my_analyzer": {
"tokenizer": "ik_max_word",
"filter": ["length", "pinyin"]
}
},
"filter": {
"pinyin": {
"type": "pinyin",
"keep_full_pinyin": false,
"keep_joined_full_pinyin": true,
"keep_original": true,
"remove_duplicated_term": true,
"limit_first_letter_length": 16,
"none_chinese_pinyin_tokenize": false
},
"length":{
"type":"length",
"min":2
}
}
}
},
"mappings": {
"properties": {
"id": {
"type": "integer"},
"name": {
"type": "text", "analyzer": "my_analyzer", "search_analyzer": "ik_max_word"},
"birthday": {
"type": "date", "format": "yyyy-MM-dd HH:mm:ss"},
"weight": {
"type": "double"},
"describe": {
"type": "text", "analyzer": "my_analyzer", "search_analyzer": "ik_max_word"}
}
}
}
4.2. 删除索引
DELETE /study
4.3. 修改索引
- ES 不允许修改或者删除已经存在的字段
- 修改索引准确的来讲相当于新增字段
PUT /study/_mapping
{
"properties": {
"geo" : {
"type": "geo_point"}
}
}
4.4. 查询索引
4.4.1. 查看全部索引
GET /_cat/indices
-- 返回值
yellow open study aK2MakZXTp-y0ezGOadPiw 1 1 0 0 225b 225b
-- 返回值每一列的意思:
yellow:健康状态,yellow 警告,green 健康,单节点部署的话都为 yellow
open: 当前索引是 打开或关闭 状态
study: 索引名称
aK2MakZXTp-y0ezGOadPiw:索引的唯一标识
1:主分片数
1:副本分片数
0:可用文档
0:已删除文档
225b:主分片、副本分片总存储大小
225b:主分片存储大小
4.4.2. 查询具体索引
GET /study
5. 文档操作
5.1. 新增/覆盖 文档
- 下方命令中结尾的 2 为文档唯一 ID,字符串类型,如果不设置在会自动生成
- 如果在执行下方的命令时,发现 ID 已经存在,则会覆盖原来的数据,起到全量修改的作用
POST /study/_doc/2
{
"id": 2,
"name": "李四",
"birthday": "1992-07-16 00:00:00",
"weight": 90.20,
"describe": "这是一个品学兼优的学生",
"sex": "男",
"geo": "30,60"
}
5.2. 删除文档
- 下方命令中结尾的 1 为文档新增时设置的唯一 ID
DELETE /study/_doc/1
5.3. 修改文档
- 如果需要修改大量字段,推荐采用 新增,覆盖的方式
- 如果只是局部修改一两个字段,可以采用下方的命令
- 下方命令中结尾的 2 为文档新增时设置的唯一 ID
POST /study/_update/2
{
"doc": {
"sex": "女"
}
}
5.4. 查询文档
- 下方命令中结尾的 2 为文档新增时设置的唯一 ID
GET /study/_doc/2
6. 条件查询
- 上方已经实现了根据唯一 ID 的简单查询,但是数据的查询往往不可能这么简单,下方介绍一些常用的查询方式
6.1. 一些关键参数
{
"query": {
具体的 DSL 查询条件 },
"from": "数字:分页的起始位置,默认 0",
"size": "数字:每页的最大显示条数,默认 10",
"sort": [{
"字段": "升序 asc、降序 desc"}]
}
- from 和 size 类似于 mysql 中 limit 查询的两个参数
- size 最大值不能超过 10000
- es 的查询不存在全量,都存在分页约束
6.2. 查询全部
-
查询方式:match_all
-
由于 ES 的查询都被分页约束,所以该查询相当于没有任何条件的分页查询
-
想要多看见几条数据,可以加 from:0,size:10000 查看最多一万条数据(不推荐)
GET /study/_search
{
"query": {
"match_all": {
}}
}
6.3. 模糊查询
- 查询方式:match 和 multi_match
- 该种方式的查询字段仅适用于:text 类型的数据
-- 单字段
GET /study/_search
{
"query": {
"match": {
"name": "ZS"
}
}
}
-- 多字段
GET /study/_search
{
"query": {
"multi_match": {
"query": "调皮捣蛋",
"fields": ["describe", "name"]
}
}
}
6.4. 精确查询
精确查询不支持 text 类型的字段
6.4.1. 相等查询
- 查询方式:term
- 类似于传统数据库的 (where field = value)
GET /study/_search
{
"query": {
"term": {
"sex": "男"}
}
}
6.4.2. 范围查询
6.4.2.1. 常规范围查询
- 查询方式:range
- 适用数据类型:数字,keyword,date 等
- 类似于传统数据库的 (where field > value1 and field < field2 或者 between … and)
- gt、gte:大于 大于等于
- le、lte: 小于 大于等于
GET /study/_search
{
"query": {
"range": {
"weight": {
"gte": 10, "lte": 200}
}
}
}
6.4.2.2. 经纬度范围查询
- 查询方式:geo_bounding_box
- 适用数据类型:geo_point
- top_left:左上角的点坐标
- bottom_right:右下角的点坐标
- 查询逻辑:两个点拉成一个矩形,在矩形范围内的点都会被查询出来
- 以下为 西经50度到东京100度,北纬10度到北纬30度 范围内的数据
GET /study/_search
{
"query": {
"geo_bounding_box": {
"location": {
"top_left": {
"lat": 30, "lon": -50 },
"bottom_right": {
"lat": 10, "lon": 100 }
}
}
}
}
6.4.2.3. 经纬度距离查询
- 查询方式:geo_distance
- 适用数据类型:geo_point
- distance:距离
- location:一个坐标,维度,经度
- 查询逻辑:计算两个坐标之间的距离,获取 distance 指定范围内的数据
- 以下为 查询距离 东经59度北纬30度150公里 范围内的数据
GET /study/_search
{
"query": {
"geo_distance": {
"distance": "150km",
"location": "30,59"
}
}
}
6.5. 算分排序
6.5.1. 算分规则
ES 在未指定排序字段时,默认会给每一条文档进行打分(_score),分数越高的文档,在查询结果中的排序越靠前。再次基础上,我们可以通过算分函数(function_score)人为干预算分结果,从而实现我们想要的排序结果。如:通过 钞能力 让自己的搜索结果比较靠前
干预算分的三个条件
-
filter:过滤条件,只对满足条件的文档做算分干预,比如充过钱的
-
算分函数包括:
- weight:一个常量
- field_value_factor:用某个字段代替算分,必须是数字类型的字段
- random_score:随机数
- script_score:自定义计算公式
-
boost_mode:两种算分模式计算后的 运算模式
- multiply:query_score + function_score
- replace: function_score 替换 query_score
- sum(相加),avg(取平均),max(取最大),min(取最小) 等
6.5.2. 一个例子
- 查询方式:function_score
- 以下为查询时,给 王五 的算分在默认的基础上加上 20 分,让他排在第一
GET /study/_search
{
"query": {
"function_score": {
"query": {
"match_all": {
}},
"functions": [
{
"filter": {
"match" : {
"name": "王五"} },
"weight": 20
}
],
"boost_mode": "sum"
}
}
}
6.6. 组合查询
6.6.1. 四个组成部分
- must:必须匹配,其中的内容必须全部符合,相当于 与
- should:选择性匹配,其中的内容只需要匹配一个,相当于 或
- must_not:必须不匹配,不参与算分,其中内容必须完全不匹配,相当于 非
- filter:必须匹配,不参与算分,其中的内容必须全部符合,相当于 与
- PS:当 must 和 should 同时存在是,should 不起作用,只会增加算分
正常情况下,不参与算分的字段尽量放到 must_not 和 filter 从而提高性能
6.6.2. 一个例子
-
查询方式:bool
-
以下为查询:描述中包含学生,且 id 等于2 的数据
GET /study/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"describe": "学生" }},
{
"term": {
"id": 2 }}
]
}
}
}
6.7. 排序
6.7.1. 常规排序
- 排序方式: sort
- 以下为根据 id 倒序
GET /study/_search
{
"query": {
"match_all": {
}},
"sort": [
{
"id": "desc"}
]
}
6.7.2. 坐标距离排序
- 排序方式:_geo_distance
- 一下为距离当前位置由近到远,并计算出距离
GET /study/_search
{
"query": {
"match_all": {
}},
"sort": [
{
"_geo_distance": {
"location": {
"lat": 40,
"lon": -70
},
"order": "asc",
"unit": "km"
}
}
]
}
6.8. 关键字高亮
- 高亮方法:highlight
- pre_tags:前缀,默认
- post_tags:后缀,默认
- require_field_match:高亮字段是否和原字段要一致,默认 true
GET /study/_search
{
"query": {
"match": {
"describe": "一个学生"
}},
"highlight": {
"fields": {
"describe": {
"pre_tags": "",
"post_tags": "",
"require_field_match": "false"
}
}
}
}
7. 初始化脚本
POST /study/_doc/1
{
"id": 1,
"name": "张三",
"birthday": "1991-07-16 00:00:00",
"weight": 45.32,
"describe": "这是一个调皮捣蛋的学生",
"sex": "女",
"location": "30,-45"
}
POST /study/_doc/2
{
"id": 2,
"name": "李四",
"birthday": "1992-07-16 00:00:00",
"weight": 90.20,
"describe": "这是一个品学兼优的学生",
"sex": "男",
"location": "30,60"
}
POST /study/_doc/3
{
"id": 3,
"name": "王五",
"birthday": "2020-07-16 00:00:00",
"weight": 120.32,
"describe": "这是一个中规中矩的学生",
"sex": "男",
"location": "30,-45"
}