Elasticsearch7——动态映射与显式映射

1,648 阅读9分钟

[这是我参与11月更文挑战的第18天,活动详情查看:2021最后一次更文挑战]

这是Elasticsearch7。15:Mapping文档的译文,根据资料与个人理解梳理而成。


一、什么是映射(Mapping)

由 【Elasticsearch7——索引、type、文档与字段】 一节可知,Elasticsearch中是按文档的形式存储数据的,处理后通过索引可以进行查询。而映射(mapping)是原生JSON到文档的转换器;也决定了索引与文档如何进行关联

如下所示:

分词.png

每个文档都是字段的集合,每个字段都有自己的 数据类型。映射数据时可以定义一个映射,包含与文档相关的字段列表。映射定义还包括元数据字段,例如_source字段定义了如何处理文档关联的元数据

防止字段无限增加

如果使用了动态映射,插入的每个新文档都可能引入新字段。在索引中定义太多字段会导致映射爆炸,从而导致内存不足的错误和难以恢复的情况。使用映射限制设置来限制字段映射的数量(手动或动态创建)并防止映射爆炸。

  • index.mapping.total_fields.limit:限制了索引中的字段最大数量;字段对象映射以及字段别名计入此限制,默认值为1000。限制的目的是为了防止映射和搜索变得太大。较高的值会导致性能下降和内存问题,尤其是在负载高或资源很少的集群中。
  • index.mapping.depth.limit:表示映射的深度。例如,如果所有字段都在根对象级别定义,则深度为1。如果有一个对象映射,则深度为 2等。默认为20
  • index.mapping.nested_fields.limit:定义了nested索引中不同映射的最大数量,nested类型只应在需要相互独立地查询对象数组时使用;默认为50。
  • index.mapping.nested_objects.limit:单个文档可以包含的嵌套 JSON 对象(nested类型)的最大数量,默认为10000
  • index.mapping.field_name_length.limit:设置字段名称的最大长度,默认为 Long.MAX_VALUE(无限制)。
  • index.mapping.dimension_fields.limit:仅供 Elastic 内部使用,索引的最大时间序列维度数;默认为16

dynamic 参数

dynamic参数控制是否动态添加新字段,取值如下所示:

取值
true新字段被添加到映射中(默认)
runtime新字段作为运行时字段添加到映射中,这些字段未编入索引,并_source在查询时加载
false新字段将被忽略,这些字段不会被索引或可搜索
strict如果检测到新字段,则会抛出异常并拒绝文档,新字段必须显式添加到映射中

搜索分为两个阶段:查询时输入即搜索、索引时输入即搜索,这里暂时挖个坑[todo]。而运行时字段是在查询时计算的字段,可以:

  • 在不重新索引数据的情况下向现有文档添加字段
  • 在不了解数据结构的情况下开始处理数据
  • 在查询时覆盖从索引字段返回的值
  • 为特定用途定义字段而不修改底层架构

好处

  • 由于运行时字段未编入索引,因此添加运行时字段不会增加索引大小;
  • 如果将运行时字段设为索引字段,则无需修改任何引用运行时字段的查询;
  • 简化了映射决策,不必预先决定如何解析数据;
  • 使用异步搜索 API运行包含运行时字段的搜索。这种搜索方法有助于抵消为包含该字段的每个文档中的运行时字段计算值的性能影响。

使用运行时字段无需重新索引即可更改架构,可以结合使用运行时字段索引字段来平衡资源使用和性能。这样可以使索引更小,搜索性能更慢。

二、动态映射

动态映射允许 Elasticsearch 动态地添加其他字段,只需索引文档即可。Elasticsearch 最重要的特性之一是约定,可以直接添加索引文档,无需创建索引、定义映射类型和定义字段。

新字段的自动检测和添加称为 动态映射,也可以自定义动态映射规则:

  • 动态字段映射:管理检测动态字段的规则
  • 动态模板:为动态添加的字段配置自定义映射规则;索引模板可以为新索引配置默认映射、设置和别名,无论是自动创建还是显式创建。

可以在文档和object级别禁用动态映射,将dynamic参数设置为 false忽略新字段,设置为strict会在 Elasticsearch 遇到未知字段时拒绝文档。

动态字段映射

当 Elasticsearch 在文档中检测到新字段时,它默认动态地将该字段添加到类型映射中;可以通过dynamic 参数控制此行为。

启用动态映射:

  • dynamic=true时,索引时输入即搜索
  • dynamic=runtime时,查询时输入即搜索

dynamic 参数的设置影响JSON数据解析,默认数据类型解析如下:

json数据dynamic=truedynamic=runtime
null不添加不添加
true 或者 falsebooleanboolean
doublefloatdouble
integerlonglong
objectobject不添加字段
array取决于数组中的第一个非null值取决于数组中的第一个非null值
string【通过日期检测】datedate
string【通过数值检测float 或者 longdouble 或者 long
string【未通过日期或数值检测】text带有.keyword子字段keyword

date_detection(日期检测)

如果date_detection是enabled(默认),检查新的字符串字段以查看其内容是否与 中指定的任何日期模式匹配 dynamic_date_formats。如果找到匹配项,date则会添加一个具有相应格式的新字段。

dynamic_date_formats的默认值为:

  • yyyy-MM-dd'T'HH:mm:ss.SSSSSSZ
  • yyyy-MM-dd
  • yyyy/MM/dd HH:mm:ss Z
  • yyyy/MM/dd Z

禁用日期检测:

PUT my-index-000001
{
  "mappings": {
    "date_detection": false
  }
}

自定义日期检测:

PUT my-index-000001
{
  "mappings": {
    "dynamic_date_formats": ["MM/dd/yyyy"]
  }
}

PUT my-index-000001/_doc/1
{
  "create_date": "09/25/2015"
}

数值检测(numeric_detection):

PUT my-index-000001
{
  "mappings": {
    "numeric_detection": true
  }
}

PUT my-index-000001/_doc/1
{
  "my_float":   "1.0", 
  "my_integer": "1" 
}

动态映射模板

除了默认动态字段映射规则之外,将dynamic设置为true或者runtime,然后就可以通过动态模板自定义映射规则了。动态模板在添加后可以重新排序或删除,匹配规则如下:

  • match_mapping_type:按类型验证,支持默认的数据类型
  • match/unmatch:按字段名称进行模式匹配
  • path_match/path_unmatch:按属性访问路径进行模式匹配
  • 未定义以上的匹配规则时,不匹配任何字段

验证映射

如果提供的映射包含无效的映射片段,则返回验证错误。在应用索引的动态模板时进行验证,并且在大多数情况下,在动态模板更新时进行验证。在某些情况下,提供无效的映射片段可能会导致动态模板的更新或验证失败:

  • 如果match_mapping_type没有指定,但是模板至少能匹配一个预定义的映射类型,则该映射片段被视为有效的。如果一个字段在匹配时可以被不同类型进行索引,那么在索引时会返回验证错误。所以,建议将match_mapping_type配置为预期的JSON类型。

  • 如果在映射片段中使用{name}占位符,在更新动态模板时跳过验证,因为名称未知。

match_mapping_type、match/unmatch、path_match/path_unmatch

  1. 动态模板设置运行时字段

动态模板可以设置运行时字段,如下所示。当匹配时,会将字段映射为类型为ip运行时字段

PUT my-index-000001/
{
  "mappings": {
    "dynamic_templates": [
      {
        "strings_as_ip": {
          "match_mapping_type": "string",
          "match": "ip*",
          "runtime": {
            "type": "ip"
          }
        }
      }
    ]
  }
}
  1. match_mapping_type

match_mapping_type中的数据类型可以被JSON解析器检测。因为 JSON 不区分 longintegerdoublefloat,所以它总是选择更广泛的数据类型,例如long整数和double浮点数。

例如,将long映射为integer

PUT my-index-000001
{
  "mappings": {
    "dynamic_templates": [
      {
        "integers": {
          "match_mapping_type": "long",
          "mapping": {
            "type": "integer"
          }
        }
      },
      {
        "strings": {
          "match_mapping_type": "string",
          "mapping": {
            "type": "text",
            "fields": {
              "raw": {
                "type":  "keyword",
                "ignore_above": 256
              }
            }
          }
        }
      }
    ]
  }
}
  1. match/unmatch

match参数使用模式匹配字段名称;而unmatch使用模式排除匹配的字段。

  "match_pattern": "regex",
  "match": "^profit_\d+$"
  1. path_match 和 path_unmatch

path_matchpath_unmatch参数与match/unmatch类似,只是匹配的是路径而不是字段名称。例如:

PUT my-index-000001
{
  "mappings": {
    "dynamic_templates": [
      {
        "full_name": {
          "path_match":   "name.*",
          "path_unmatch": "*.middle",
          "mapping": {
            "type":       "text",
            "copy_to":    "full_name"
          }
        }
      }
    ]
  }
}

模板变量

{name}占位符在mapping 中会被替换为字段名称;{dynamic_type}占位符在mapping 中会被替换为动态类型。

PUT my-index-000001
{
  "mappings": {
    "dynamic_templates": [
      {
        "named_analyzers": {
          "match_mapping_type": "string",
          "match": "*",
          "mapping": {
            "type": "text",
            "analyzer": "{name}"
          }
        }
      },
      {
        "no_doc_values": {
          "match_mapping_type":"*",
          "mapping": {
            "type": "{dynamic_type}",
            "doc_values": false
          }
        }
      }
    ]
  }
}

查询映射

GET test/_mapping

Result:
{
  "test" : {
    "mappings" : {
      "properties" : {
        "description" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "name" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "title" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        }
      }
    }
  }
}

三、显式映射

显式映射:可以精确的约束映射的定义,如

  • 哪些字符串字段应被视为全文字段
  • 哪些字段包含数字日期地理位置
  • 日期值 的格式
  • 控制动态添加字段映射的自定义规则

创建索引时显式设置映射:

PUT /my-index-000003
{
  "mappings": {
    "properties": {
      "age":    { "type": "integer" },  
      "email":  { "type": "keyword"  }, 
      "name":   { "type": "text"  }     
    }
  }
}


更新映射(添加属性):

PUT /my-index-000003/_mapping
{
  "properties": {
    "employee-id": {
      "type": "keyword",
      "index": false
    }
  }
}

更改现有字段可能会使已编入索引的数据无效,如果需要更改现有字段的映射,请创建一个新的数据流并将您的数据重新索引到其中。


查看索引:

# 查看索引
GET /my-index-000003/_mapping

API 返回以下响应:

{
  "my-index-000003" : {
    "mappings" : {
      "properties" : {
        "age" : {
          "type" : "integer"
        },
        "email" : {
          "type" : "keyword"
        },
        "employee-id" : {
          "type" : "keyword",
          "index" : false
        },
        "name" : {
          "type" : "text"
        }
      }
    }
  }
}

查看索引的特定字段:

# 查看索引的特定字段
GET /my-index-000003/_mapping/field/age

API 返回以下响应:

{
  "my-index-000003" : {
    "mappings" : {
      "age" : {
        "full_name" : "age",
        "mapping" : {
          "age" : {
            "type" : "integer"
          }
        }
      }
    }
  }
}

四、参考文档

Mapping