超详细总结Elasticsearch的动态Mapping

1,416 阅读6分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 22 天,点击查看活动详情

大家好,我是半夏之沫 😁😁 一名金融科技领域的JAVA系统研发😊😊
我希望将自己工作和学习中的经验以最朴实最严谨的方式分享给大家,共同进步👉💓👈
👉👉👉👉👉👉👉👉💓写作不易,期待大家的关注和点赞💓👈👈👈👈👈👈👈👈
👉👉👉👉👉👉👉👉💓关注微信公众号【技术探界】 💓👈👈👈👈👈👈👈👈


前言

Elasticsearch中,存储是面向文档(document)的,而文档存储的容器就叫做索引(index),表示一类文档。

每个索引在创建时,可以通过传参指定索引的mapping,而mapping就会定义当前索引下的文档的字段名称以及字段类型

本文讨论的动态Mapping,即Dynamic Mapping,就是在向Elasticsearch写入文档时,索引不存在的情况下,Elasticsearch自动帮助我们创建索引并识别文档字段类型生成mapping

那么就有如下问题:

  1. 类型如何完成自动识别;
  2. 能否修改动态生成的mapping

本文将对上述问题进行解答。

Elasticsearch版本:7.2.1

正文

一. 类型如何完成自动识别

假如testdynamic索引不存在,然后通过如下语句向Elasticsearch插入文档。

PUT /testdynamic/_doc/1

{
  "name": "Dog Lee",
  "status": false,
  "birthday": "2022-10-01T12:00:00.000+0800"
}

此时会自动创建testdynamic索引,并生成testdynamicmapping。那么通过如下语句查看一下动态生成的mapping

GET /testdynamic/_mapping

结果如下

{
  "testdynamic" : {
    "mappings" : {
      "properties" : {
        "birthday" : {
          "type" : "date"
        },
        "name" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "status" : {
          "type" : "boolean"
        }
      }
    }
  }
}

可以看到,Json中的字符串name字段被识别为了text类型,字符串birthday字段被识别为了date类型,以及布尔status字段被识别为了boolean类型。

Elasticsearch完成类型的自动识别,可以参照下表。

Json数据类型Elasticsearch数据类型
整数long
浮点数float
布尔值boolean
数组取决于第一个非空值的类型
对象object
忽略该字段

下表单独对Elasticsearch识别字符串Json数据类型进行总结。

字符串情况Elasticsearch数据类型
字符串满足日期格式date
字符串满足数字格式long或者float
其它text,并添加keyword字段

二. 能否修改动态生成的mapping

修改动态生成的mapping,这里的修改,指的是新增字段到已经存在的mapping中(mapping中已经存在的字段不允许修改的),主要由mappingdynamic属性决定。

1. 将mapping的dynamic设置为true

如果将mappingdynamic设置为true,则写入的文档有新增字段时,mapping就会被更新。如下进行演示。

首先将dynamic设置为true,语句如下。

PUT /testdynamic/_mapping

{
  "dynamic": "true"
}

然后添加有新增字段age的文档,语句如下。

PUT /testdynamic/_doc/2

{
  "name": "Lincoln",
  "status": true,
  "birthday": "2022-10-01T12:00:00.000+0800",
  "age": 35
}

此时再查看一下testdynamicmapping,结果如下。

{
  "testdynamic" : {
    "mappings" : {
      "dynamic" : "true",
      "properties" : {
        "age" : {
          "type" : "long"
        },
        "birthday" : {
          "type" : "date"
        },
        "name" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "status" : {
          "type" : "boolean"
        }
      }
    }
  }
}

可见新增字段age添加到了mapping中,且字段类型为long此时新增字段是可以作为搜索条件的。语句如下。

GET /testdynamic/_search

{
  "query": {
    "range": {
      "age": {
        "gte": 30
      }
    }
  }
}

结果如下。

{
  "took" : 43,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 1,
      "relation" : "eq"
    },
    "max_score" : 1.0,
    "hits" : [
      {
        "_index" : "testdynamic",
        "_type" : "_doc",
        "_id" : "2",
        "_score" : 1.0,
        "_source" : {
          "name" : "Lincoln",
          "status" : true,
          "birthday" : "2022-10-01T12:00:00.000+0800",
          "age" : 35
        }
      }
    ]
  }
}

2. 将mapping的dynamic设置为false

如果将mappingdynamic设置为false,则写入的文档就算有新增字段,mapping也不会被更新。如下进行演示。

首先将dynamic设置为false,语句如下。

PUT /testdynamic/_mapping

{
  "dynamic": "false"
}

然后添加有新增字段grades的文档,语句如下。

PUT /testdynamic/_doc/3

{
  "name": "Lincoln",
  "status": true,
  "birthday": "2022-10-01T12:00:00.000+0800",
  "age": 25,
  "grades": 90
}

能够添加成功,但是查看mapping,发现并没有添加gradesmapping中,如下所示。

{
  "testdynamic" : {
    "mappings" : {
      "dynamic" : "true",
      "properties" : {
        "age" : {
          "type" : "long"
        },
        "birthday" : {
          "type" : "date"
        },
        "name" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "status" : {
          "type" : "boolean"
        }
      }
    }
  }
}

此时不能以grades字段作为搜索条件,示例语句如下所示。

GET /testdynamic/_search

{
  "query": {
    "range": {
      "grades": {
        "gte": 80
      }
    }
  }
}

结果如下。

{
  "took" : 0,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 0,
      "relation" : "eq"
    },
    "max_score" : null,
    "hits" : [ ]
  }
}

3. 将mapping的dynamic设置为strict

如果将mappingdynamic设置为strict,此时如果写入的文档有新增字段,则写入会失败。如下进行演示。

首先将mappingdynamic设置为strict,语句如下。

PUT /testdynamic/_mapping

{
  "dynamic": "strict"
}

然后添加有新增字段des的文档,语句如下。

PUT /testdynamic/_doc/4

{
  "name": "Armstrong",
  "status": false,
  "birthday": "2022-10-01T18:00:00.000+0800",
  "age": 15,
  "grades": 50,
  "des": "Good people"
}

此时会直接报错,报错如下。

{
  "error": {
    "root_cause": [
      {
        "type": "strict_dynamic_mapping_exception",
        "reason": "mapping set to strict, dynamic introduction of [grades] within [_doc] is not allowed"
      }
    ],
    "type": "strict_dynamic_mapping_exception",
    "reason": "mapping set to strict, dynamic introduction of [grades] within [_doc] is not allowed"
  },
  "status": 400
}

总结

Elasticsearch在进行非字符串的类型自动识别时,会基于如下规则。

Json数据类型Elasticsearch数据类型
整数long
浮点数float
布尔值boolean
数组取决于第一个非空值的类型
对象object
忽略该字段

进行字符串的类型自动识别时,会基于如下规则。

字符串情况Elasticsearch数据类型
字符串满足日期格式date
字符串满足数字格式long或者float
其它text,并添加keyword字段

能否修改动态生成的mapping,有如下三种情况。

  1. mappingdynamic设置为true。插入文档有新增字段时,能插入成功,并且新增字段会添加到mapping中,新增字段能作为搜索条件;
  2. mappingdynamic设置为false。插入文档有新增字段时,能插入成功,但新增字段不会添加到mapping中,新增字段也不能作为搜索条件;
  3. mappingdynamic设置为strict。插入文档有新增字段时,插入会直接失败。

大家好,我是半夏之沫 😁😁 一名金融科技领域的JAVA系统研发😊😊
我希望将自己工作和学习中的经验以最朴实最严谨的方式分享给大家,共同进步👉💓👈
👉👉👉👉👉👉👉👉💓写作不易,期待大家的关注和点赞💓👈👈👈👈👈👈👈👈
👉👉👉👉👉👉👉👉💓关注微信公众号【技术探界】 💓👈👈👈👈👈👈👈👈

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 22 天,点击查看活动详情