Elasticsearch语法知多少之mappings详解
了解mappings常用属性,通过设置mappings属性约束文档。本文介绍会一些常用的静态映射方法,包括:约束文档中字段的数量和名称,实现字段合并检索,字段禁止被搜索等。了解部分settings属性,这些属性和mappings属性关联较大,官网文档也将这些属性和mapping放在一起分析,所以本文也会做一些案例讲解。............
目录
目标
- 了解mappings常用属性,通过设置mappings属性约束文档。本文介绍会一些常用的静态映射方法,包括:约束文档中字段的数量和名称,实现字段合并检索,字段禁止被搜索等。
- 了解部分settings属性,这些属性和mappings属性关联较大,官网文档也将这些属性和mapping放在一起分析,所以本文也会做一些案例讲解。
ES版本信息
7.17.5
官方文档
Mappinghttps://www.elastic.co/guide/en/elasticsearch/reference/7.17/mapping.html
相关术语
动态映射
比如:在MySQL中需要建立数据库和表才可以插入数据,而ES不需要提前建立映射就能插入数据,因为索引(创建)文档时ES会自动识别属性的类型,这种机制称为动态映射。
动态映射 | 文档类型 |
---|---|
字符串 | 自动识别为text,还有子类型为keyword。 如果是日期格式则自动识别为date类型。 字符串类型的数字也可以自动识别为float和long类型,但是该配置默认关闭。 |
布尔值 | boolean |
浮点数 | float |
整数 | long |
对象 | object,属性被properties包裹。 |
数组 | 由最前面不为空的元素类型决定。 |
null | null虽然可以索引文档,但是mappings忽略了对其类型的识别。 |
静态映射(也称为显示映射)
ES在索引文档前可以设置文档的字段名称、字段数量,分词器等,这称为静态映射。
字段合并
多个字段合并为一个新字段,但是该字段并不会出现在_source中。最常见的需求就是合并地址和英文姓名字段。检索条件可以直接对合并后的新字段进行匹配。
索引爆炸
settings下的属性。索引下的文档拥有过多字段会引发性能下降和内存问题,尤其是在负载高或资源少的集群中。通过设置index.mapping.total_fields.limit(文档字段数量最大值)来解决这一问题。字段和对象映射以及字段别名计入此限制。默认值为1000。
字段的深度
settings下的属性。文档下有字段,如果这些字段都没有下级字段,则深度为1,如果有字段为对象类型,且对象类型里的字段没有下级字段,则深度为2。总的来说,深度实际上就是文档子目录的层数。通过设置index.mapping.depth.limit来控制文档字段的层级数量,默认为20。
实战
动态映射
第一步:直接索引一个文档。
PUT /mappings_test/_doc/1
{
"name":"张三",
"birthday":"2022-01-01",
"boo":true,
"source":34.35,
"age":20,
"favoriteNovels":["凡人修仙传","诛仙"],
"examinationResults":{
"English":98,
"Chinese":100,
"match":98.9
}
}
第二步:查询自动映射的类型。
GET /mappings_test/_mapping
静态映射
查看索引的映射
GET /my-index-000001/_mapping
查看某字段的映射
GET /my-index-000001/_mapping/field/age
约束字段的名称
需求:创建索引,约束该索引只能有姓名、年龄、性别,成绩四个字段,设置成绩字段为对象类型且对象的属性不做数量的约束。
第一步:实现需求。
PUT /student
{
"mappings": {
"dynamic": "strict",
"properties": {
"name": {
"type": "text"
},
"age": {
"type": "long"
},
"sex": {
"type": "boolean"
},
"grade": {
"type": "object",
"dynamic": "true"
}
}
}
}
第二步:索引以下3条文档,测试该索引字段数据的约束是否有效。结果发现王五这条文档索引失败,说明测试符合需求。
PUT /student/_doc/1
{
"name":"张三",
"age":12,
"sex":true,
"grade":{
"English":98.5,
"Math":100.0
}
}
PUT /student/_doc/2
{
"name":"李四",
"sex":true,
"grade":{
"English":96.5,
"Math":92.5,
"Chinese":92.5
}
}
PUT /student/_doc/3
{
"name":"王五",
"age":23,
"birthday":"2022-01-01",
"sex":true,
"grade":{
"Physics":96.5
}
}
约束字段的数量
需求:约束文档的字段数量。需要注意:index.mapping.total_fields.limit属于settings属性,但是官网文档将其与映射限制设置放在了一起描述。所以我这里也做一个案例来讲解。
第一步:实现需求。
PUT /fields_num_db
{
"settings": {
"index.mapping.total_fields.limit": 6
}
}
第二步:索引文档,测试文档字段数量的约束是否有效。发现前两条文档可以索引成功,最后一条文档索引失败。
PUT /fields_num_db/_doc/1
{
"name":"张三"
}
PUT /fields_num_db/_doc/2
{
"name": "李四",
"address": {
"province": "湖南省"
}
}
PUT /fields_num_db/_doc/3
{
"name":"李四",
"address":{
"province":"湖南省",
"city":"岳阳市"
}
}
约束字段的深度
需求:约束文档的字段的层级数量。需要注意:index.mapping.depth.limit属于settings属性,但是官网文档将其与映射限制设置放在了一起描述。所以我这里也做一个案例来讲解。
第一步:实现需求。
PUT /depth_num_db
{
"settings": {
"index.mapping.depth.limit": 2
}
}
第二步:索引文档,发现第三条文档索引失败,因为第三条文档的深度是3,说明测试通过。
PUT /depth_num_db/_doc/1
{
"name": "李四",
"sex": false,
"age": 12,
"examination_results": {
"Math": 98.5,
"English": 100
}
}
PUT /depth_num_db/_doc/2
{
"name": "王五",
"sex": false,
"age": 12,
"hobby": {
"sports":["跑步","篮球"]
}
}
PUT /depth_num_db/_doc/3
{
"name": "王五",
"sex": false,
"age": 12,
"hobby": {
"novel":{
"凡人修仙传":"忘语",
"天龙八部":"金庸"
}
}
}
约束字段名称的长度
需求:需要注意:index.mapping.field_name_length.limit属于settings属性,但是官网文档将其与映射限制设置放在了一起描述。所以我这里也做一个案例来讲解。
第一步:实现需求。
PUT /name_length_db
{
"settings": {
"index.mapping.field_name_length.limit": 50
}
}
第二步:索引文档,测试通过。
PUT /name_length_db/_doc/1
{
"name": "张三"
}
PUT /name_length_db/_doc/2
{
"name": "李四",
"a12345678901234567890123456789012345678901234567890":"1"
}
字段合并
需求:创建索引,对省市县字段进行合并。
第一步:实现需求。
PUT /address_list
{
"mappings": {
"properties": {
"province": {
"type": "text",
"copy_to": "fullAddress"
},
"city": {
"type": "text",
"copy_to": "fullAddress"
},
"county": {
"type": "text",
"copy_to": "fullAddress"
}
}
},
"settings": {
"index": {
"analysis.analyzer.default.type": "ik_max_word"
}
}
}
第二步:批量索引文档。
PUT /address_list/_bulk
{ "index": { "_id": "1"} }
{"province": "湖南省","city": "长沙市","county":"天心区"}
{ "index": { "_id": "2"} }
{"province": "湖南省","city": "长沙市","county":"芙蓉区"}
{ "index": { "_id": "3"} }
{"province": "广东省","city": "广州市","county":"白云区"}
{ "index": { "_id": "4"} }
{"province": "湖北省","city": "武汉市","county":"江夏区"}
第三步:用ik分词器查看分词效果。这里以查看"湖南省长沙市天心区"为例。
POST _analyze
{
"analyzer": "ik_max_word",
"text": "湖南省长沙市天心区"
}
第四步:检索文档,测试fullAddress字段是否有效。这里以查看"湖南天心"为例,结果查不到,后来用ik分词器查看,原来分词出现了"南天",而"湖南省长沙市天心区"分词以后没有南天,所以在用"operator": "and"条件查询时无效。反之,如果查询条件是"湖南省天心"时,就可以查到结果。
GET /address_list/_search
{
"query": {
"match": {
"fullAddress": {
"query": "湖南天心",
"operator": "and"
}
}
}
}
GET /address_list/_search
{
"query": {
"match": {
"fullAddress": {
"query": "湖南省天心",
"operator": "and"
}
}
}
}
字段禁止被搜索
需求:创建索引,约束该索引下的文档中age字段不能被搜索。
第一步:实现需求。
PUT /ban_db
{
"mappings": {
"properties": {
"name": {
"type": "text"
},
"sex": {
"type": "boolean"
},
"age": {
"type": "long",
"index":false
}
}
}
}
第二步:索引几条文档。
PUT /ban_db/_bulk
{"index":{"_id":"1"}}
{"name":"孟浩然","sex":false,"age":12}
{"index":{"_id":"2"}}
{"name":"陈永生","sex":true,"age":23}
{"index":{"_id":"3"}}
{"name":"李长春","sex":false,"age":15}
{"index":{"_id":"4"}}
{"name":"陈天相","sex":true,"age":20}
第三步:根据条件查询文档。发现当搜索条件为age时报错,提示无法在该字段上搜索,需求测试通过。
GET /ban_db/_doc/1
GET /ban_db/_search
{
"query": {
"term": {
"sex": {
"value": true
}
}
}
}
GET /ban_db/_search
{
"query": {
"term": {
"age": {
"value": 12
}
}
}
}
对null值进行搜索搜索
需求:只有字段为keyword类型时才可以对null值进行搜索。开发环境最常见的就是设置name为keyword类型,下面就用这个为案例进行演示。
第一步:实现需求。name属性设置null_value=value_is_null,其中value_is_null可以自定义,因为它的实际含义就是在精确搜索时,匹配条件值为value_is_null时表示搜索name为null值。
PUT /null_db
{
"mappings": {
"properties": {
"name": {
"type": "keyword",
"null_value": "value_is_null"
},
"sex": {
"type": "boolean"
},
"age": {
"type": "long"
}
}
},
"settings": {
"index": {
"analysis.analyzer.default.type": "ik_max_word"
}
}
}
第二步:索引几条文档。
PUT /null_db/_bulk
{"index":{"_id":"1"}}
{"name":"null","sex":false,"age":12}
{"index":{"_id":"2"}}
{"name":"陈永生","sex":true,"age":23}
{"index":{"_id":"3"}}
{"name":null,"sex":false,"age":15}
{"index":{"_id":"4"}}
{"name":"陈天相","sex":true,"age":20}
第三步:搜索name为null的文档。发现只搜索出了id=3的文档,说明需求测试通过。
GET /null_db/_search
{
"query": {
"term": {
"name": {
"value": "value_is_null"
}
}
}
}
将字段添加到现有映射
需求:在一个已经定义好的索引中加入新的字段。注意:如果要变更现有字段,大家可以参考我的另一篇关于索引重建的文档。
第一步:创建索引。
PUT /my-index-000001
{
"mappings": {
"dynamic": "strict",
"properties": {
"name": {
"type": "keyword",
"null_value": "value_is_null"
},
"sex": {
"type": "boolean"
},
"age": {
"type": "long"
}
}
},
"settings": {
"index": {
"analysis.analyzer.default.type": "ik_max_word"
}
}
}
第二步:实现需求。
PUT /my-index-000001/_mapping
{
"properties": {
"weight": {
"type": "float"
},
"height": {
"type": "float"
}
}
}
第三步:查看映射。
GET /my-index-000001/_mapping
Boost调整字段评分权重
分析:比如文章的标题和内容,按照业务来看,明显应该是标题评分权重大于内容评分权重。
需求:将标题评分增加1倍。
第一步:新增测试数据。实现标题评分增加到原来的10倍。
PUT /article_db
{
"mappings": {
"properties": {
"title": {
"type": "text",
"boost": 10
},
"content": {
"type": "text"
}
}
}
}
PUT /article_db/_bulk
{"index":{"_id":"1"}}
{"title":"公园里有一只棕色的狐狸","content":"这只狐狸很好看。"}
{"index":{"_id":"2"}}
{"title":"棕色的兔子","content":"公园里有一只棕色的兔子,它比棕色的狐狸更好看。"}
第二步:搜索验证。发现文档2排在文档1的前面,此时如果将权重取消,则发现结果与现在正好相反,这说明Boost生效了,评分权重更偏向标题字段。
GET /article_db/_search
{
"query": {
"multi_match": {
"query": "棕色的狐狸",
"fields": [
"title",
"content"
]
}
}
}
等同于这种方法(推荐):
GET /article_db/_search
{
"query": {
"multi_match": {
"query": "棕色的狐狸",
"fields": [
"title^10",
"content"
]
}
}
}
更多推荐
所有评论(0)