ElasticSearch的Mapping和DSL语言
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>后海店"
]
}