动态映射

edit

Elasticsearch 最重要的特性之一是它试图尽量减少你的操作,让你能够尽快开始探索你的数据。要索引一个文档,你不需要先创建索引、定义映射类型和定义字段——你只需索引一个文档,索引、类型和字段将自动显示:

PUT data/_doc/1 
{ "count": 5 }

创建 data 索引,_doc 映射类型,以及一个名为 count 的字段,数据类型为 long

自动检测和添加新字段称为动态映射。可以通过自定义动态映射规则来适应您的需求,使用:

Dynamic field mappings
动态字段检测的规则。
Dynamic templates
自定义规则以配置动态添加字段的映射。

索引模板允许您配置新索引的默认映射、设置和别名,无论是自动创建还是显式创建。

动态字段映射

edit

当Elasticsearch检测到文档中的新字段时,默认情况下它会动态地将该字段添加到类型映射中。dynamic参数控制此行为。

您可以明确指示 Elasticsearch 根据传入的文档动态创建字段,方法是将 dynamic 参数设置为 trueruntime。当启用动态字段映射时,Elasticsearch 使用下表中的规则来确定如何为每个字段映射数据类型。

下表中的字段数据类型是 Elasticsearch 能够动态检测到的唯一字段数据类型。您必须显式映射所有其他数据类型。

Elasticsearch 数据类型

JSON 数据类型

"dynamic":"true"

"dynamic":"运行时"

空值

未添加字段

未添加字段

truefalse

布尔值

布尔值

双精度

浮点数

双精度

长整型

长整型

长整型

对象

对象

未添加字段

数组

取决于数组中第一个非null的值

取决于数组中第一个非null的值

字符串 通过 日期检测

日期

日期

字符串 通过 数字检测

floatlong

doublelong

string 不通过 date 检测或 numeric 检测

文本 带有一个 .keyword 子字段

关键词

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

使用更新映射 API 来更新现有字段上的 dynamic 设置。

您可以自定义动态字段映射规则,用于 日期检测数值检测。 要定义可应用于其他动态字段的定制映射规则,请使用 dynamic_templates

日期检测

edit

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

默认值为 dynamic_date_formats

[ "严格日期可选时间","yyyy/MM/dd HH:mm:ss Z||yyyy/MM/dd Z"]

例如:

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

GET my-index-000001/_mapping 

已添加 create_date 字段作为 date 字段,格式为:
"yyyy/MM/dd HH:mm:ss Z||yyyy/MM/dd Z"

禁用日期检测

edit

动态日期检测可以通过将 date_detection 设置为 false 来禁用:

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

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

已添加 create_date 字段作为 text 字段。

自定义检测到的日期格式

edit

或者,可以自定义 dynamic_date_formats 以支持您自己的 日期格式

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

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

在配置日期模式数组和在单个字符串中配置由||分隔的多个模式之间存在差异。当你配置日期模式数组时,第一个文档中未映射日期字段的匹配模式将决定该字段的映射:

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

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

生成的映射将是:

{
  "my-index-000001": {
    "mappings": {
      "dynamic_date_formats": [
        "yyyy/MM",
        "MM/dd/yyyy"
      ],
      "properties": {
        "create_date": {
          "type": "date",
          "format": "MM/dd/yyyy"
        }
      }
    }
  }
}

在单个字符串中配置多个用 || 分隔的模式,会生成一个支持任何日期格式的映射。这使您能够索引使用不同格式的文档:

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

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

生成的映射将是:

{
  "my-index-000001": {
    "mappings": {
      "dynamic_date_formats": [
        "yyyy/MM||MM/dd/yyyy"
      ],
      "properties": {
        "create_date": {
          "type": "date",
          "format": "yyyy/MM||MM/dd/yyyy"
        }
      }
    }
  }
}

时间戳格式(epoch_millisepoch_second)不支持作为动态日期格式。

数值检测

edit

虽然 JSON 支持原生的浮点数和整数数据类型,但某些应用程序或语言有时可能会将数字呈现为字符串。通常正确的解决方案是显式映射这些字段,但可以启用数字检测(默认情况下禁用)来自动执行此操作:

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

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

my_float 字段添加为 float 字段。

my_integer 字段添加为 long 字段。

动态模板

edit

动态模板允许您对Elasticsearch如何映射您的数据进行更大的控制,超越默认的动态字段映射规则。您可以通过将动态参数设置为trueruntime来启用动态映射。然后,您可以使用动态模板定义自定义映射,这些映射可以根据匹配条件应用于动态添加的字段:

在映射规范中使用 {name}{dynamic_type} 模板变量 作为占位符。

动态字段映射仅在字段包含具体值时才会添加。当字段包含null或空数组时,Elasticsearch不会添加动态字段映射。如果在dynamic_template中使用了null_value选项,它仅在包含该字段具体值的第一个文档被索引后才会应用。

动态模板被指定为一个命名对象的数组:

  "dynamic_templates": [
    {
      "my_template_name": { 
        ... match conditions ... 
        "mapping": { ... } 
      }
    },
    ...
  ]

模板名称可以是任何字符串值。

匹配条件可以包括以下任意一项:match_mapping_typematchmatch_patternunmatchpath_matchpath_unmatch

匹配字段应使用的映射。

验证动态模板

edit

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

  • 如果没有指定match_mapping_type,但模板至少对一种预定义的映射类型有效,则映射片段被认为是有效的。然而,如果在索引时有一个匹配模板的字段被索引为不同类型,则会返回验证错误。例如,配置一个没有match_mapping_type的动态模板被认为是有效的字符串类型,但如果一个匹配动态模板的字段被索引为长整型,则在索引时会返回验证错误。建议将match_mapping_type配置为预期的JSON类型,或在映射片段中配置所需的type
  • 如果在映射片段中使用了{name}占位符,则在更新动态模板时会跳过验证。这是因为此时字段名称是未知的。相反,验证发生在模板在索引时应用的时候。

模板按顺序处理——第一个匹配的模板获胜。当通过更新映射 API 添加新的动态模板时,所有现有的模板都会被覆盖。这允许在最初添加后重新排序或删除动态模板。

在动态模板中映射运行时字段

edit

如果您希望 Elasticsearch 将某些类型的新字段动态映射为运行时字段,请在索引映射中设置 "dynamic":"runtime"。这些字段不会被索引,而是在查询时从 _source 加载。

或者,您可以使用默认的动态映射规则,然后创建动态模板将特定字段映射为运行时字段。您在索引映射中设置"dynamic":"true",然后创建一个动态模板,将特定类型的新字段映射为运行时字段。

假设你有一组数据,其中每个字段都以 ip_ 开头。根据 动态映射规则,Elasticsearch 会将通过 numeric 检测的任何 string 映射为 floatlong。然而,你可以创建一个动态 模板,将新字符串映射为类型为 ip 的运行时字段。

以下请求定义了一个名为 strings_as_ip 的动态模板。当 Elasticsearch 检测到匹配 ip* 模式的新 string 字段时,它会将这些字段映射为类型为 ip 的运行时字段。由于 ip 字段不会动态映射,因此您可以将此模板与 "dynamic":"true""dynamic":"runtime" 一起使用。

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

参见此示例,了解如何使用动态模板将string字段映射为索引字段或运行时字段。

match_mapping_typeunmatch_mapping_type

edit

参数 match_mapping_type 根据 JSON 解析器检测到的数据类型匹配字段,而 unmatch_mapping_type 则根据数据类型排除字段。

因为 JSON 不区分 longintegerdoublefloat,任何解析的浮点数都被视为 double JSON 数据 类型,而任何解析的 integer 数字都被视为 long

使用动态映射时,Elasticsearch 总是会选择更宽的数据类型。唯一的例外是 float,它比 double 需要的存储空间更少,并且对于大多数应用程序来说精度已经足够。运行时字段不支持 float,这就是为什么 "dynamic":"runtime" 使用 double

Elasticsearch 自动检测以下数据类型:

Elasticsearch 数据类型

JSON 数据类型

"dynamic":"true"

"dynamic":"运行时"

空值

未添加字段

未添加字段

truefalse

布尔值

布尔值

双精度

浮点数

双精度

长整型

长整型

长整型

对象

对象

未添加字段

数组

取决于数组中第一个非null的值

取决于数组中第一个非null的值

字符串 通过 日期检测

日期

日期

字符串 通过 数字检测

floatlong

doublelong

string 不通过 date 检测或 numeric 检测

文本 带有一个 .keyword 子字段

关键词

您可以为 match_mapping_typeunmatch_mapping_type 参数指定单一数据类型或数据类型列表。您还可以为 match_mapping_type 参数使用通配符 (*) 来匹配所有数据类型。

例如,如果我们想将所有整数字段映射为integer而不是long,并将所有string字段映射为textkeyword,我们可以使用以下模板:

PUT my-index-000001
{
  "mappings": {
    "dynamic_templates": [
      {
        "numeric_counts": {
          "match_mapping_type": ["long", "double"],
          "match": "count",
          "mapping": {
            "type": "{dynamic_type}",
            "index": false
          }
        }
      },
      {
        "integers": {
          "match_mapping_type": "long",
          "mapping": {
            "type": "integer"
          }
        }
      },
      {
        "strings": {
          "match_mapping_type": "string",
          "mapping": {
            "type": "text",
            "fields": {
              "raw": {
                "type":  "keyword",
                "ignore_above": 256
              }
            }
          }
        }
      },
      {
        "non_objects_keyword": {
          "match_mapping_type": "*",
          "unmatch_mapping_type": "object",
          "mapping": {
            "type": "keyword"
          }
        }
      }
    ]
  }
}

PUT my-index-000001/_doc/1
{
  "my_integer": 5, 
  "my_string": "Some string", 
  "my_boolean": "false", 
  "field": {"count": 4} 
}

字段 my_integer 被映射为 integer

字段 my_string 被映射为 text,并带有一个 keyword 多字段

字段 my_boolean 被映射为 keyword

字段 field.count 被映射为 long 类型。

matchunmatch

edit

参数 match 使用一个或多个模式来匹配字段名称,而 unmatch 使用一个或多个模式来排除由 match 匹配的字段。

参数match_pattern调整了match参数的行为,以支持在字段名称上进行完整的Java正则表达式匹配,而不仅仅是简单的通配符。例如:

  "match_pattern": "regex",
  "match": "^profit_\d+$"

以下示例匹配所有名称以 long_ 开头(但不包括以 _text 结尾的字段)的 string 字段,并将它们映射为 long 字段:

PUT my-index-000001
{
  "mappings": {
    "dynamic_templates": [
      {
        "longs_as_strings": {
          "match_mapping_type": "string",
          "match":   "long_*",
          "unmatch": "*_text",
          "mapping": {
            "type": "long"
          }
        }
      }
    ]
  }
}

PUT my-index-000001/_doc/1
{
  "long_num": "5", 
  "long_text": "foo" 
}

字段 long_num 被映射为 long

字段 long_text 使用默认的 string 映射。

您可以使用JSON数组为matchunmatch字段指定一组模式。

下一个示例匹配所有名称以 ip_ 开头或以 _ip 结尾的字段, 但不包括以 one 开头或以 two 结尾的字段,并将它们映射为 ip 字段:

PUT my-index-000001
{
  "mappings": {
    "dynamic_templates": [
      {
        "ip_fields": {
          "match":   ["ip_*", "*_ip"],
          "unmatch": ["one*", "*two"],
          "mapping": {
            "type": "ip"
          }
        }
      }
    ]
  }
}

PUT my-index/_doc/1
{
  "one_ip":   "will not match", 
  "ip_two":   "will not match", 
  "three_ip": "12.12.12.12", 
  "ip_four":  "13.13.13.13" 
}

字段 one_ip 未匹配,因此使用默认的 text 映射。

字段 ip_two 未匹配,因此使用默认的 text 映射。

字段 three_ip 被映射为类型 ip

字段 ip_four 被映射为类型 ip

path_matchpath_unmatch

edit

The path_matchpath_unmatch 参数的工作方式与 matchunmatch 相同,但它们作用于字段的全路径,而不仅仅是最终名称,例如 some_object.*.some_field

此示例将name对象中任何字段的值复制到顶级full_name字段,除了middle字段:

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

PUT my-index-000001/_doc/1
{
  "name": {
    "first":  "John",
    "middle": "Winston",
    "last":   "Lennon"
  }
}

以下示例同时使用了 path_matchpath_unmatch 的模式数组。

name 对象或 user.name 对象中的任何字段的值,除了 middlemidinitial 字段外,都会被复制到顶层的 full_name 字段中:

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

PUT my-index-000001/_doc/1
{
  "name": {
    "first":  "John",
    "middle": "Winston",
    "last":   "Lennon"
  }
}

PUT my-index-000001/_doc/2
{
  "user": {
    "name": {
      "first":      "Jane",
      "midinitial": "M",
      "last":       "Salazar"
    }
  }
}

请注意,path_matchpath_unmatch 参数不仅匹配叶子字段,还匹配对象路径。例如,索引以下文档将导致错误,因为 path_match 设置也匹配对象字段 name.title,而该字段不能映射为文本:

PUT my-index-000001/_doc/2
{
  "name": {
    "first":  "Paul",
    "last":   "McCartney",
    "title": {
      "value": "Sir",
      "category": "order of chivalry"
    }
  }
}

模板变量

edit

mapping中,{name}{dynamic_type}占位符被替换为字段名称和检测到的动态类型。以下示例将所有字符串字段设置为使用与字段名称相同的analyzer,并禁用所有非字符串字段的doc_values

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
          }
        }
      }
    ]
  }
}

PUT my-index-000001/_doc/1
{
  "english": "Some English text", 
  "count":   5 
}

The english 字段被映射为一个 string 字段,并使用 english 分析器。

字段 count 被映射为一个禁用了 doc_valueslong 字段。

动态模板示例

edit

以下是一些可能有用动态模板的示例:

结构化搜索

edit

当你设置"dynamic":"true"时,Elasticsearch会将字符串字段映射为text字段,并带有一个keyword子字段。如果你只索引结构化内容,并且对全文搜索不感兴趣,你可以让Elasticsearch仅将你的字段映射为keyword字段。然而,你必须搜索与索引时完全相同的值才能搜索这些字段。

PUT my-index-000001
{
  "mappings": {
    "dynamic_templates": [
      {
        "strings_as_keywords": {
          "match_mapping_type": "string",
          "mapping": {
            "type": "keyword"
          }
        }
      }
    ]
  }
}

仅文本字符串映射

edit

与前面的示例相反,如果您只关心字符串字段上的全文搜索,并且不打算运行聚合、排序或精确搜索,您可以指示 Elasticsearch 将字符串映射为 text

PUT my-index-000001
{
  "mappings": {
    "dynamic_templates": [
      {
        "strings_as_text": {
          "match_mapping_type": "string",
          "mapping": {
            "type": "text"
          }
        }
      }
    ]
  }
}

或者,您可以创建一个动态模板,将您的字符串字段映射为映射运行时部分中的keyword字段。当Elasticsearch检测到新的string类型字段时,这些字段将被创建为keyword类型的运行时字段。

尽管您的 string 字段不会被索引,但它们的值存储在 _source 中,并且可以在搜索请求、聚合、过滤和排序中使用。

例如,以下请求创建了一个动态模板,将string字段映射为类型为keyword的运行时字段。尽管runtime定义为空,但新的string字段将根据Elasticsearch用于向映射添加字段类型的动态映射规则被映射为keyword运行时字段。任何未通过日期检测或数字检测的string字段都会自动映射为keyword

PUT my-index-000001
{
  "mappings": {
    "dynamic_templates": [
      {
        "strings_as_keywords": {
          "match_mapping_type": "string",
          "runtime": {}
        }
      }
    ]
  }
}

您索引一个简单文档:

PUT my-index-000001/_doc/1
{
  "english": "Some English text",
  "count":   5
}

当你查看映射时,你会看到english字段是一个类型为keyword的运行时字段:

GET my-index-000001/_mapping
{
  "my-index-000001" : {
    "mappings" : {
      "dynamic_templates" : [
        {
          "strings_as_keywords" : {
            "match_mapping_type" : "string",
            "runtime" : { }
          }
        }
      ],
      "runtime" : {
        "english" : {
          "type" : "keyword"
        }
      },
      "properties" : {
        "count" : {
          "type" : "long"
        }
      }
    }
  }
}

禁用的规范

edit

范数是索引时评分因子。如果你不关心评分,例如如果你从不按分数对文档进行排序,你可以禁用这些评分因子在索引中的存储,从而节省一些空间。

PUT my-index-000001
{
  "mappings": {
    "dynamic_templates": [
      {
        "strings_as_keywords": {
          "match_mapping_type": "string",
          "mapping": {
            "type": "text",
            "norms": false,
            "fields": {
              "keyword": {
                "type": "keyword",
                "ignore_above": 256
              }
            }
          }
        }
      }
    ]
  }
}

子字段 keyword 出现在此模板中,以与动态映射的默认规则保持一致。当然,如果您不需要它们,因为您不需要对该字段执行精确搜索或聚合,您可以按照上一节中的描述将其删除。

时间序列

edit

在使用 Elasticsearch 进行时间序列分析时,通常会有许多数值字段,这些字段你经常会在聚合操作中使用,但从不进行过滤。在这种情况下,你可以禁用这些字段的索引,以节省磁盘空间,并可能提高索引速度:

PUT my-index-000001
{
  "mappings": {
    "dynamic_templates": [
      {
        "unindexed_longs": {
          "match_mapping_type": "long",
          "mapping": {
            "type": "long",
            "index": false
          }
        }
      },
      {
        "unindexed_doubles": {
          "match_mapping_type": "double",
          "mapping": {
            "type": "float", 
            "index": false
          }
        }
      }
    ]
  }
}

与默认的动态映射规则类似,双精度数被映射为浮点数,这些浮点数通常足够精确,但所需的磁盘空间只有一半。