ElasticSearch的Mapping和DSL语言
文章目录
- ElasticSearch的Mapping和DSL语言
- mapping(映射)
- 动态映射
- 类型自动识别
- 静态映射
- 关于后期更改mapping字段类型?
- 重建索引步骤
- 常用mapping参数配置
- index_template 和mapping_template
- Query DSL
- 数据初始化
- 无条件查询,默认返回十条数据
- 分页查询
- from和size关键字
- 页码限制
- 深分页查询Scroll
- 深分页问题
- 排序查询
- 过滤返回字段
- 条件查询(重点)
- 分词匹配(match)
- 短语匹配(match_phrase)
- slop属性
- 多字段查询(multi_match)
- Query String
- 未指定字段查询,查询索引中所有的字段。
- 指定单个字段查询
- 指定多个字段查询
- Simple Query String
- 关键词查询 (term)
- constant_score
- 前缀查询(prefix)
- 通配符查询(wildcard)
- 范围查询(range)
- 模糊查询(fuzzy)
- 高亮(highlight)
- 自定义标签
- 多字段高亮
mapping(映射)
- 定义索引中的字段名称
- 定义字段的数据类型,例如字符串,数字,布尔等。
- 字段,倒排索引的相关配置(Analyzer)
es中的Mapping分为动态映射和静态映射。
动态映射
在文档写入索引中时,会根据字段自动识别类型,这种机制成为动态映射。
类型自动识别
ES 的动态映射会尝试自动推断字段的类型,比如:
- 字符串 类型字段:默认情况下,ES 会将字符串字段映射为
text
类型,并且创建一个keyword
子字段以支持精确匹配。 - 数值 类型字段:会自动映射为
integer
,long
,float
,double
等类型。 - 布尔值 类型字段:会映射为
boolean
类型。 - 日期 类型字段:会映射为
date
类型。 - 对象 或 嵌套 类型字段:会自动映射为
object
或nested
类型。
静态映射
静态映射时在index创建时可以事先定义好映射,包含文档的各字段的类型,分词器等。这种方式成为静态映射。类似于关系型数据库中创建表结构的形式。
关于后期更改mapping字段类型?
-
新增加字段
-
dynamic设置为true时,一单有新增字段的文档写入,mapping也同时更新
-
dynamic设置为false时,mapping不会被更新,新增字段的数据无法被索引,但是信息会出现在_source中
-
dynamic设置为strict,文档写入失败,抛出异常
-
-
已有字段是不支持映射更新的
-
如果修改了字段类型,会导致已被索引的数据无法搜索。
-
lucene实现的倒排索引,一旦生成后,就不支持修改
-
如果希望改变字段类型,可以利用reindex api,重建索引。
-
重建索引步骤
- 创建一个和原索引类似的索引,需要更改的mapping同步创建
- 利用reindex命令重建索引
- 保证兼容性,把原有的索引干掉,再创建新索引的别名为原索引的名称 PUT /destIndex/_alias/sourceIndex
POST _reindex
{"source" : {"index" : "user"},"dest" : {"index" : "user2"}
}
常用mapping参数配置
-
type: 定义字段类型
- 数值类型 (Numeric Types)
integer
: 32-bit 整数,范围从 -2,147,483,648 到 2,147,483,647。long
: 64-bit 整数,范围从 -9,223,372,036,854,775,808 到 9,223,372,036,854,775,807。short
: 16-bit 整数,范围从 -32,768 到 32,767。byte
: 8-bit 整数,范围从 -128 到 127。float
: 单精度浮点数,通常用于表示小数或科学记数法中的数据,精度为 32 位。double
: 双精度浮点数,通常用于高精度计算,精度为 64 位。scaled_float
: 用于存储浮点数,但以整数形式存储,并通过一个缩放因子来恢复浮点值。适用于对高精度浮点数有严格要求的场景。
- 字符串类型 (Text Types)
text
: 用于存储全文本数据,通常用于需要分词和全文搜索的字段。Elasticsearch 会对text
字段进行分词,并建立反向索引。keyword
: 用于存储精确匹配的数据,不会被分词。适合用于过滤、排序、聚合等操作。常用于存储标签、类别、ID 等精确值。
- 日期类型 (Date Type)
date
: 用于存储日期和时间数据。支持多种日期格式,可以使用yyyy-MM-dd
、epoch_millis
等格式。
- 布尔类型 (Boolean Type)
boolean
: 用于存储布尔值(true
或false
)。
- 对象类型 (Object Type)
object
: 用于存储嵌套的 JSON 对象,可以包含其他字段和子对象。每个字段都会被索引,适合存储结构化的数据。nested
: 类似于object
,但允许在查询时处理嵌套文档中的子对象。这在需要对嵌套对象进行复杂查询时非常有用。
- 地理空间类型 (Geo Types)
geo_point
: 用于存储地理坐标(经度和纬度)。支持地理位置相关的查询,如范围查询和距离计算。geo_shape
: 用于存储复杂的地理形状(如多边形、圆形、线段等)。适合需要精确地理计算和空间查询的场景。
- 二进制类型 (Binary Type)
binary
: 用于存储二进制数据,如文件或图像的内容。数据以 Base64 编码形式存储。
- 其他类型 (Special Types)
ip
: 用于存储 IP 地址,支持 IPv4 和 IPv6 地址的存储和查询。alias
: 用于创建别名,可以将别名指向一个或多个索引,使得可以通过别名来进行索引操作。
-
index: 默认为true,如果设置成false,则该字段不可搜索。
-
analyzer:定义分词器类型,如ik_smart,ik_max_word
-
null_value: 指定为空值的替代值,如null_value = “null”,可通过查询null查询空值
-
copy_to:将字段赋值到规定的一个字段中。如省市县分字段,另外定义一个address字段,可以使用copy_to赋值到address字段中
index_template 和mapping_template
可以通过预定义索引模板和映射模板,通用的配置
Query DSL
restApi + JSON构建查询请求体的一种查询语句。
数据初始化
创建hotel数据,对应的mapping如下
PUT hotel
{"mappings": {"properties" : {"address" : {"type" : "text","analyzer": "ik_max_word", "fields" : {"keyword" : {"type" : "keyword","ignore_above" : 256}}},"brand" : {"type" : "keyword"},"business" : {"type" : "text","analyzer": "ik_max_word", "fields" : {"keyword" : {"type" : "keyword","ignore_above" : 256}}},"city" : {"type" : "keyword"},"id" : {"type" : "long"},"location" : {"type" : "text","analyzer": "ik_max_word", "fields" : {"keyword" : {"type" : "keyword","ignore_above" : 256}}},"name" : {"type" : "text","analyzer": "ik_max_word", "fields" : {"keyword" : {"type" : "keyword","ignore_above" : 256}}},"pic" : {"type" : "keyword","index": false},"price" : {"type" : "long"},"score" : {"type" : "long"}}}
}
无条件查询,默认返回十条数据
SELECT * FROM HOTEL LIMIT 10 OFFSET 0;
POST hotel/_search
{"query": {"match_all": {}}
}
分页查询
from和size关键字
POST hotel/_search
{"query": {"match_all": {}},"from": 0,"size": 10
}
页码限制
Result window is too large, from + size must be less than or equal to: [10000]
es限制from + size 不能大于10000,否则会抛异常。
可通过修改单个索引的index.max_reuslt_window值将该限制更改。
PUT /INDEXNAME/_setttings
{"index.max_result_window" : "20000"
}
名称为注意index.的形式
修改索引的索引限制,但后面新增的索引限制还是为10000,后续新增的索引不受该更改影响
PUT /_all/_settings
{"index.max_result_window" : "20000"
}
es不推荐使用from,size的方式进行深度分页
深分页查询Scroll
scroll
API 适用于需要大量数据的提取任务,例如批量处理或数据迁移。它提供了一种机制,通过创建一个持续的上下文来逐步检索大量数据。
使用 scroll
API 的基本步骤:
1.初始化滚动上下文:会返回一个scroll_id给第二步使用
GET /my_index/_search/scroll
{"scroll": "1m", // 设置滚动上下文的生存时间"size": 1000, // 每次检索的文档数量"query": {"match_all": {}}
}
2. 继续获取数据:使用 _scroll_id
继续获取数据。
GET /_search/scroll
{"scroll": "1m","scroll_id": "DnF1ZXJ5VGhlbkZldGNoZTM1N...."
}
3. 清除滚动上下文(当不再需要时):
DELETE /_search/scroll
{"scroll_id": ["DnF1ZXJ5VGhlbkZldGNoZTM1N...."]
}
深分页问题
深分页查询由于是使用快照保存查询出的结果,所以在这段时间的数据准确性不能保证。
排序查询
POST hotel/_search
{"query": {"match_all": {}},"sort": [{"price": {"order": "asc"}},{"score": {"order": "desc"}}]
}
利用sort排序查询,支持多个字段排序,有先后顺序,如根据价格升序后,如果价格相同,则根据score倒序。
过滤返回字段
通过_source属性筛选返回的字段
POST hotel/_search
{"query": {"match_all": {}},"sort": [{"price": {"order": "asc"}},{"score": {"order": "desc"}}],"_source": ["name","address","price","score","brand"]
}
条件查询(重点)
分词匹配(match)
match匹配时会对所查找的关键词进行分词,然后按分词匹配查询
match支持以下参数
- query: 指定匹配的值
- operator: 匹配条件的类型
- and 所有条件都要匹配
- or 匹配一个即可
- minmum should match: 最低匹配度,即条件在倒排索引中的最低匹配度,匹配分词的最少数量
POST hotel/_search
{"query": {"match": {"address": {"query": "宝安广场","operator": "or"}}}
}
默认的operator为or,则上述条件可通过宝安和广场两个词语去地址查询。只要满足一个词语都可以被查询出来
operator为and,则address必须满足宝安和广场两个词语才能被查询出来,需要精确结果的时候需要更换成and
注意match下的query不要跟最开始的query混淆
POST hotel/_search
{"query": {"match": {"address": {"query": "宝安广场","minimum_should_match": 2}}}
}
minimum_should_match 定义了必须至少匹配到个词语才能满足。
短语匹配(match_phrase)
match_phrase的分词结果必须在被检索的字段的分词中都包含,而且顺序必须相同,默认顺序必须都是连续,所以叫短语匹配,如匹配创业二路步行街
就是在一段话中截取一小段话来匹配。
注意,如查询词汇为创业二路,查看IK分词器结果
{"tokens" : [{"token" : "创业","start_offset" : 0,"end_offset" : 2,"type" : "CN_WORD","position" : 0},{"token" : "二路","start_offset" : 2,"end_offset" : 4,"type" : "CN_WORD","position" : 1},{"token" : "二","start_offset" : 2,"end_offset" : 3,"type" : "TYPE_CNUM","position" : 2},{"token" : "路","start_offset" : 3,"end_offset" : 4,"type" : "COUNT","position" : 3}]
}
此时,创业二路为连续的分词,而创业二为不连续的分词。所以在短语查询的时候
POST hotel/_search
{"query": {"match_phrase": {"address": "创业二路"}}
}
此时能查询出结果
而
POST hotel/_search
{"query": {"match_phrase": {"address": "创业二"}}
}
查询创业二的时候不能查出结果来。
slop属性
slop属性告诉match_phrase查询词条能够相隔多远时仍然将文档视为匹配。类似于跳过分词中的position
POST hotel/_search
{"query": {"match_phrase": {"address": {"query": "创业二","slop" : 1}}}
}
此时能查询出结果
多字段查询(multi_match)
如在name和address中查询七天酒店
POST hotel/_search
{"query": {"multi_match": {"query": "7天酒店","fields": ["name","address"],"operator": "and"}}
}
默认operator为or。
Query String
Query String 允许我们在查询字符串中指定AND,OR,NOT等条件,同时也和multi_match一样,可以指定多字段查询
AND,OR,NOT必须得大写
未指定字段查询,查询索引中所有的字段。
POST hotel/_search
{"query": {"query_string": {"query": "如家 OR 七天"}}
}
指定单个字段查询
POST hotel/_search
{"query": {"query_string": {"default_field": "brand", "query": "速8 OR 7天"}}
}
指定多个字段查询
POST hotel/_search
{"query": {"query_string": {"fields": ["brand","name"], "query": "速8 OR 7天"}}
}
Simple Query String
类似Query String ,但是会忽略错误的语法,同时只支持部分查询语法,不支持AND OR NOT,会当做字符串处理。支持部分逻辑:
- +替代AND
- | 替代 OR
- -替代NOT
下面两张写法含义一样
POST hotel/_search
{"query": {"simple_query_string": {"query": "速8酒店","fields": ["name","brand"],"default_operator": "and"}}
}POST hotel/_search
{"query": {"simple_query_string": {"query": "速8 + 酒店","fields": ["name","brand"]}}
}
关键词查询 (term)
Term用来使用关键词查询(精确匹配),还可以用来查询没有进行分词的数据类型(keyword,date,integer,long,double,boolean or ip)。
模糊匹配用match,精确匹配用term
term会直接将查询不分词进行精确匹配,但是查询的内容是可以分词的,一旦内容的某个分词和查询的内容匹配上。也可以查出来
这条可以查出来,因为地址有西城这个分词
POST hotel/_search
{"query": {"term": {"address": {"value": "西城"}}}
}
下面这条查询不到数据
POST hotel/_search
{"query": {"term": {"address": {"value": "西城北京市西城"}}}
}
term查询不分词字段没必要进行算分,因为算分会损耗性能
constant_score
constant_score可以不用算分直接返回
POST hotel/_search
{"query": {"constant_score": {"filter": {"term": {"address.keyword": "西城北京市西城区德胜门内大街兴华胡同五福里2号"}}}}
}
前缀查询(prefix)
它会对分词后的term进行前缀搜索。
- 它不会分析要搜索字符串,传入的前缀就是想要查找的前缀
- 默认状态下,前缀查询不做相关度查询计算,它只是将所有匹配的文档返回,然后赋予所有相关分数值为1。它的行为更像是一个过滤器而不是查询。两者的实际区别是过滤器可以被缓存,而前缀查询不行。
prefix的原理: 需要遍历所有倒排索引,并比较每个term是否以所指定的前缀开头。
这玩意儿会遍历所有倒排索引,所以严重损耗性能。
POST hotel/_search
{"query": {"prefix": {"address": {"value": "北京"}}}
}
通配符查询(wildcard)
类似于mysql中的like,不建议使用,有了倒排索引还需要这个干嘛?
POST hotel/_search
{"query": {"wildcard": {"name": {"value": "*上海*"}}}
}
范围查询(range)
查询价格在100到200之间的所有酒店
POST hotel/_search
{"query": {"range": {"price": {"gte": 100,"lte": 200}}},"sort": [{"price": {"order": "asc"}}]
}
模糊查询(fuzzy)
在实际搜索中,如商品搜索,有时候用户输入的商品名称可能会有错别字,但是我们应该就算有错别字也能搜索到相应的产品信息,这个时候fuzzy功能就来了。
fuzzy查询的两个重要参数:
-
fuzziness: 表示输入的关键字通过几次操作可以转变成ES库里面的对应Filed字段
- 操作是指,新增一个字符,删除一个字符,修改一个字符,每次操作可以记做编辑距离
为1 - 如中文集团到中威集团编辑距离就是1,只需要修改一个字符;
- 该参数默认值为0,即不开启模糊查询
- 如果fuzziness值在这里设置成2,会把编辑距离为2的东东集团也查出来
- 操作是指,新增一个字符,删除一个字符,修改一个字符,每次操作可以记做编辑距离
-
prefix length: 表示限制输入关键字和ES对应查询field的内容开头的第n个字符必须完全匹
配,不允许错别字匹配- 如这里等于1,则表示开头的字必须匹配,不匹配则不返回
- 默认值也是0
- 加大prefix length的值可以提高效率和准确率
查询名称中包含如家的酒店,通过儒家查询,可以查询出如家的酒店信息
POST hotel/_search
{"query": {"fuzzy": {"name": {"value": "儒家","fuzziness": 1}}}
}
另外一种写法,推荐使用
POST hotel/_search
{"query": {"match": {"name": {"query": "儒家" ,"fuzziness": 1}}}
}
高亮(highlight)
highlight 关键字: 可以让符合条件的文档中的关键词高亮
highlight相关属性
- pre_tags 前缀标签
- post_tags 后缀标签
- tags_schema 设置为styled可以使用内置高亮样式
- require_field_match 多字段高亮需要设置为false
POST hotel/_search
{"query": {"match": {"name": "如家"}},"highlight": {"fields": {"name" : {}}}
}
会多返回一个hignlight字段,只是多了个html标签
"highlight" : {"name" : ["<em>如家</em>酒店(北京良乡西路店)"]
}
自定义标签
POST hotel/_search
{"query": {"match": {"name": "如家"}},"highlight": {"fields": {"name" : {}},"pre_tags": ["<span style='color:red'>"],"post_tags": ["</span>"]}
}
"highlight" : {"name" : ["<span style='color:red'>如家</span>酒店(北京良乡西路店)"]}
多字段高亮
如果查询的其他字段也需要对查询的内容进行高亮,则需要将 require_field_match 设置成false,默认为true
POST hotel/_search
{"query": {"match": {"name": "北京"}},"highlight": {"fields": {"name" : {},"address": {}},"pre_tags": ["<span style='color:red'>"],"post_tags": ["</span>"],"require_field_match": "false"}
}
"highlight" : {"address" : ["西城<span style='color:red'>北京</span>市西城区德胜门内大街兴华胡同五福里2号"],"name" : ["速8酒店<span style='color:red'>北京</span>后海店"]}