Elasticsearch基础篇(六):es映射和常用的字段类型

es创建映射和设置

官方地址:Mapping

一、什么是 Elasticsearch 映射?

在 Elasticsearch 中,映射是索引的关键组成部分,它定义了文档的结构和字段。每个文档都包含一个或多个字段,而映射定义了这些字段的数据类型、如何分析文本、字段是否可搜索等信息。

映射的主要作用包括:

  1. 定义字段类型:你可以指定字段是文本、数字、日期、地理位置等数据类型。

  2. 文本分析:决定如何对文本字段进行分析,例如拆分为单词、去除停用词、转换大小写等。

  3. 字段是否存储:你可以设置字段是否需要存储原始值,以便在检索时使用。

  4. 索引选项:控制字段是否被索引,以及如何索引。

  5. 多字段支持:Elasticsearch 允许你创建多个字段别名,以便更好地支持不同类型的查询。

  6. 复杂数据类型:Elasticsearch 支持复杂数据类型,如对象、数组和嵌套字段。

二、映射中的字段类型

(来自官方翻译)

常见字段类型 (Common data types)

  • 二进制(binary):以Base64字符串编码的二进制值。
  • 布尔值(boolean):true 和 false 值。
  • 关键字(Keywords):包括关键字(keyword)、常量关键字(constant_keyword)和通配符(wildcard)。
  • 数值(Numbers):数值类型,如长整型(long)和双精度浮点型(double),用于表示数量。
  • 日期(Dates):日期类型,包括日期(date)和日期(纳秒精度)(date_nanos)。
  • 别名(alias):定义现有字段的别名。

对象和关联类型(Objects and relational types)

  • object:JSON对象。
  • flattened:将整个JSON对象作为单个字段值。
  • nested:保留JSON对象子字段之间的关系。
  • join:定义同一索引中文档之间的父子关系。

结构化数据类型(Structured data types)

  • Range:范围类型,如 long_rangedouble_rangedate_rangeip_range
  • ip:IPv4 和 IPv6 地址。
  • version:软件版本。支持语义化版本优先规则。
  • murmur3:计算并存储值的哈希。

聚合数据类型(Aggregate data types)

  • aggregate_metric_double:预聚合的度量值。
  • histogram:以直方图形式预聚合的数值。

文本搜索类型(Text search types)

  • text fields:文本字段,包括textmatch_only_text。经过分析的、非结构化的文本。
  • annotated-text:包含特殊标记的文本,用于识别命名实体。
  • completion:用于自动完成建议。
  • search_as_you_type:类似文本的类型,用于按输入实时完成建议。
  • token_count:文本中标记的计数。

文档排名类型(Document ranking types)

  • dense_vector:记录浮点值的密集向量。
  • sparse_vector:记录浮点值的稀疏向量。
  • rank_feature:记录一个数值特征,以在查询时提高命中。
  • rank_features:记录数值特征,以在查询时提高命中。

空间数据类型(Spatial data types)

  • geo_point:纬度和经度点。
  • geo_shape:复杂的形状,例如多边形。
  • point:任意笛卡尔坐标点。
  • shape:任意笛卡尔几何图形。

其他类型(other types)

  • percolator:索引使用 Query DSL 编写的查询。

数组(Arrays)

Elasticsearch 中,数组不需要专门的字段数据类型。默认情况下,任何字段可以包含零个或多个值,但数组中的所有值必须属于相同的字段类型。有关更多信息,请参阅数组。

多字段(multi-fields)

通常,对于不同目的,将同一字段以不同方式进行索引很有用。例如,将字符串字段映射为文本字段以进行全文搜索,将其映射为关键字字段以进行排序或聚合。或者,可以使用标准分析器、英语分析器和法语分析器对文本字段进行索引。

这就是多字段的目的。大多数字段类型支持多字段,通过 fields 参数实现。

三、映射限制 Mapping limit settings

以下是用于限制字段映射数量以防止映射爆炸的设置:

  1. index.mapping.total_fields.limit:这是索引中字段的最大数量。字段和对象映射以及字段别名都计入这一限制。默认值为1000。此限制旨在防止映射和搜索变得过大。较高的值可能导致性能下降和内存问题,尤其是在负载较高或资源较少的集群中。如果您增加此设置,建议同时增加indices.query.bool.max_clause_count设置,该设置限制了查询中的布尔子句的最大数量。

  2. index.mapping.depth.limit:这是字段的最大深度,以内部对象数量来衡量。例如,如果所有字段都在根对象级别定义,那么深度为1。如果存在一个对象映射,则深度为2,依此类推。默认值为20。

  3. index.mapping.nested_fields.limit:这是索引中不同嵌套映射的最大数量。嵌套类型应仅在特殊情况下使用,当需要独立查询对象数组时。为防止不良设计的映射,此设置限制了每个索引中唯一嵌套类型的数量。默认值为50。

  4. index.mapping.nested_objects.limit:这是单个文档中包含的所有嵌套 JSON 对象的最大数量,跨所有嵌套类型。此限制有助于防止文档包含过多嵌套对象时发生内存不足错误。默认值为10000。

  5. index.mapping.field_name_length.limit:此设置用于限制字段名称的最大长度。通常情况下,不需要设置此设置,因为默认设置是足够的,除非用户开始添加具有非常长名称的大量字段。默认值是Long.MAX_VALUE(没有限制)。

  6. index.mapping.dimension_fields.limit(技术预览):此功能处于技术预览阶段,可能在将来的版本中更改或删除。它是 Elastic 内部使用的设置。

四、创建映射

创建映射是在 Elasticsearch 中定义索引结构的过程你可以使用 REST API 或 Elasticsearch客户端库执行此操作。

使用 REST API 创建映射

使用 PUT 请求指定映射配置,创建了一个名为 my_index 的索引并定义了一些字段映射:

PUT /my_index
{
  "mappings": {
    "properties": {
      "title": {
        "type": "text",
        "analyzer": "standard"
      },
      "description": {
        "type": "text",
        "analyzer": "english"
      },
      "timestamp": {
        "type": "date"
      }
    }
  }
}

在这个示例中,我们创建了一个索引 my_index 并定义了三个字段:titledescriptiontimestamp。下面是一些字段映射的常见示例和说明:

常用映射示例和说明

文本字段映射

文本字段用于存储文本数据,例如文章内容、产品描述等。以下是一个文本字段映射的示例:

"title": {
  "type": "text",
  "analyzer": "standard"
}
  • type 设置为 text 表示这是一个文本字段。
  • analyzer 指定了用于分析文本的分析器。在这个示例中,我们使用标准分析器,它将文本拆分成单词,并转换为小写。

关键字字段映射

关键字字段通常用于存储不分析的原始文本,例如标签、类别等。以下是一个关键字字段映射的示例:

"tags": {
  "type": "keyword"
}
  • type 设置为 keyword 表示这是一个关键字字段。关键字字段不会被分析,文本将以原样存储。

日期字段映射

日期字段用于存储日期和时间信息。以下是一个日期字段映射的示例:

"timestamp": {
  "type": "date"
}
  • type 设置为 date 表示这是一个日期字段。Elasticsearch 会自动解析日期格式。

数值字段映射

数值字段可用于存储数字数据,如价格、评分等。以下是一个数值字段映射的示例:

"price": {
  "type": "float"
}
  • type 设置为 float 表示这是一个浮点数字段。

地理位置字段映射

地理位置字段用于存储地理坐标信息,如经度和纬度。以下是一个地理位置字段映射的示例:

"location": {
  "type": "geo_point"
}
  • type 设置为 geo_point 表示这是一个地理位置字段。你可以用经度和纬度表示地理坐标。

特殊映射

当需要更复杂的数据结构和搜索需求时,可以使用嵌套字段、多字段支持和自定义分析器等来扩展 Elasticsearch 映射。以下是这些示例:

嵌套字段

嵌套字段允许你在文档中创建嵌套的 JSON 结构,以表示复杂的关系。例如,如果你想建立一个博客文章的索引,每篇文章可以包含多个评论,每个评论又可以包含作者信息和评论文本。以下是一个嵌套字段映射的示例:

"comments": {
  "type": "nested",
  "properties": {
    "author": {
      "type": "text"
    },
    "comment_text": {
      "type": "text"
    }
  }
}

在这个示例中创建了一个类型为 nestedcomments 字段,并定义了 authorcomment_text 作为嵌套字段。这能够在每篇文章中存储多个评论,每个评论都有自己的作者和评论文本。

多字段支持

多字段支持允许为同一字段创建多个不同的表示,以满足不同的查询需求。例如,你可以为一个字段同时创建一个全文搜索版本和一个精确匹配版本。以下是一个多字段支持的示例:

"product_name": {
  "type": "text",
  "fields": {
    "standard": {
      "type": "text",
      "analyzer": "standard"
    },
    "keyword": {
      "type": "keyword"
    }
  }
}

在这个示例中创建了一个 product_name 字段,其类型为 text,然后定义了两个多字段:standardkeywordstandard 多字段使用标准分析器,用于全文搜索,而 keyword 多字段使用 keyword 类型,用于精确匹配。这同样配置可以同时执行全文搜索和精确匹配查询。

自定义分析器

自定义分析器允许你定义如何处理文本字段的文本分析。例如,你可以创建一个自定义分析器来处理特定语言的文本,去除停用词,或执行其他自定义文本处理。以下是一个自定义分析器的示例:

{
  "settings": {
    "analysis": {
      "analyzer": {
        "custom_analyzer": {
          "type": "custom",
          "tokenizer": "standard",
          "filter": ["lowercase", "custom_filter"]
        }
      },
      "filter": {
        "custom_filter": {
          "type": "stop",
          "stopwords": ["the", "is", "in", "on", "and"]
        }
      }
    }
  },
  "mappings": {
    "properties": {
      "content": {
        "type": "text",
        "analyzer": "custom_analyzer"
      }
    }
  }
}

在上述索引设置中,我们配置了一个名为 “custom_analyzer” 的自定义分析器,包括以下部分:

  1. "tokenizer": "standard":使用标准分析器作为分词器。这将文本拆分为单词。

  2. "filter": ["lowercase", "custom_filter"]:使用过滤器链,首先将文本转换为小写(“lowercase” 过滤器),然后应用自定义过滤器 “custom_filter”。

  3. "filter": "custom_filter" 中的 “stop” 过滤器定义了一组停用词(“stopwords”)。这些停用词将在文本分析过程中被过滤掉,以提高搜索质量。

然后,在索引映射中,我们将 “content” 字段的分析器指定为 “custom_analyzer”,这表示在索引和搜索 “content” 字段的文本时,将使用自定义分析器进行处理。

五、官方文档翻译(可做)

显式映射 Explicit mapping

您对数据的了解比 Elasticsearch 所能猜测的还要多,因此虽然动态映射对于入门很有用,但在某些时候您会想要指定自己的显式映射。
您可以在创建索引并将字段添加到现有索引时创建字段映射。
创建具有显式映射的索引
您可以使用create index API来创建一个新的索引,并定义其映射。

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

这将创建以下字段:

  • 创建一个名为age的整数字段。
  • 创建一个名为email的关键字字段。
  • 创建一个名为name的文本字段。

向现有映射添加字段
您可以使用update mapping API向现有索引添加一个或多个新字段。以下示例向现有索引添加一个名为employee-id的关键字字段,同时将该字段的index参数值设置为false,表示employee-id字段的值将被存储,但不会被索引或用于搜索。

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

更新字段的映射
除了受支持的映射参数外,您无法更改现有字段的映射或字段类型。更改现有字段可能会使已经索引的数据无效。如果需要更改数据流支持的索引的字段映射,请参阅更改数据流的映射和设置。如果需要更改其他索引中的字段映射,请创建一个具有正确映射的新索引,并将数据重新索引到该索引中。重命名字段将使已经使用旧字段名称索引的数据无效。相反,可以添加一个别名字段来创建一个替代字段名称。

要查看索引的映射,您可以使用get mapping API。

GET /my-index-000001/_mapping

此API将返回如下响应:

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

如果只想查看一个或多个特定字段的映射,您可以使用get field mapping API。这在您不需要完整的索引映射或索引包含大量字段时非常有用。

以下请求检索employee-id字段的映射。

GET /my-index-000001/_mapping/field/employee-id

此API将返回如下响应:

{
  "my-index-000001" : {
    "mappings" : {
      "employee-id" : {
        "full_name" : "employee-id",
        "mapping" : {
          "employee-id" : {
            "type" : "keyword",
            "index" : false
          }
        }
      }
    }
  }
}

聚合度量字段类型 Aggregate metric field type

聚合度量字段类型用于存储预先聚合的数值数据,以供度量聚合使用。aggregate_metric_double字段是一个包含以下度量子字段之一的对象:min、max、sum和value_count。

当您对aggregate_metric_double字段运行特定的度量聚合时,聚合会使用相关的子字段值。例如,在aggregate_metric_double字段上运行min聚合将返回所有min子字段的最小值。

aggregate_metric_double字段为每个度量子字段存储单个数值文档值。不支持数组值。min、max和sum的值是双精度浮点数,value_count是正整数。

以下是一个示例请求,演示如何在索引中创建aggregate_metric_double字段:

PUT my-index
{
  "mappings": {
    "properties": {
      "my-agg-metric-field": {
        "type": "aggregate_metric_double",
        "metrics": [ "min", "max", "sum", "value_count" ],
        "default_metric": "max"
      }
    }
  }
}

aggregate_metric_double字段的参数包括:

  • metrics:必需,度量子字段的字符串数组,每个值对应一个度量聚合。有效值包括min、max、sum和value_count,必须至少指定一个值。
  • default_metric:必需,用于查询、脚本和不使用子字段的聚合的默认度量子字段。必须是度量数组中的一个值。

aggregate_metric_double字段支持以下聚合类型:

  • min聚合:返回所有min子字段的最小值。
  • max聚合:返回所有max子字段的最大值。
  • sum聚合:返回所有sum子字段值的总和。
  • value_count聚合:返回所有value_count子字段值的总和。
  • avg聚合:没有avg子字段;avg聚合的结果使用sum和value_count度量来计算。要运行avg聚合,字段必须同时包含sum和value_count度量子字段。

如果对aggregate_metric_double字段运行其他任何聚合,将失败并出现“不支持的聚合”错误。

此外,aggregate_metric_double字段支持以下查询,其中它将根据其default_metric子字段的行为充当双精度数:

  • exists
  • range
  • term
  • terms

以下是示例请求,演示如何创建一个具有aggregate_metric_double字段的索引并添加包含预先聚合数据的文档,然后对该字段运行各种聚合:

PUT stats-index
{
  "mappings": {
    "properties": {
      "agg_metric": {
        "type": "aggregate_metric_double",
        "metrics": [ "min", "max", "sum", "value_count" ],
        "default_metric": "max"
      }
    }
  }
}

PUT stats-index/_doc/1
{
  "agg_metric": {
    "min": -302.50,
    "max": 702.30,
    "sum": 200.0,
    "value_count": 25
  }
}

PUT stats-index/_doc/2
{
  "agg_metric": {
    "min": -93.00,
    "max": 1702.30,
    "sum": 300.00,
    "value_count": 25
  }
}

对于aggregate_metric_double字段的查询将使用default_metric值。
您可以在agg_metric字段上运行min、max、sum、value_count和avg聚合。以下是一个示例查询请求,其中运行了这些聚合,并结果基于相关的度量子字段值生成:

POST stats-index/_search?size=0
{
  "aggs": {
    "metric_min": { "min": { "field": "agg_metric" } },
    "metric_max": { "max": { "field": "agg_metric" } },
    "metric_value_count": { "value_count": { "field": "agg_metric" } },
    "metric_sum": { "sum": { "field": "agg_metric" } },
    "metric_avg": { "avg": { "field": "agg_metric" } }
  }
}

聚合的结果基于相关的度量子字段值,例如min、max、sum、value_count等。

对于aggregate_metric_double字段的查询将使用default_metric的值。以下是一个示例查询请求,查询agg_metric字段以获取与default_metric字段(此处为max)匹配的文档:

GET stats-index/_search
{
  "query": {
    "term": {
      "agg_metric": {
        "value": 702.30
      }
    }
  }
}

查询结果会返回匹配default_metric字段(max)值的文档。

别名字段类型 Alias

别名映射定义了索引中字段的替代名称。可以在搜索请求和选定的其他 API(如字段能力)中使用别名代替目标字段。

PUT trips
{
  "mappings": {
    "properties": {
      "distance": {
        "type": "long"
      },
      "route_length_miles": {
        "type": "alias",
        "path": "distance"
      },
      "transit_mode": {
        "type": "keyword"
      }
    }
  }
}

在上述示例中,“route_length_miles” 是 “distance” 字段的别名。在搜索请求中,您可以使用 “route_length_miles” 来代替 “distance”。

GET _search
{
  "query": {
    "range" : {
      "route_length_miles" : {
        "gte" : 39
      }
    }
  }
}

“route_length_miles” 是别名,它实际上代表了 “distance” 字段。别名可以在查询、聚合、排序字段、请求 docvalue_fields、stored_fields、建议和高亮时使用。在访问字段值时,脚本也支持别名。请参阅不受支持的 API 部分以了解例外情况。

在搜索请求的某些部分和在请求字段能力时,可以提供字段别名的通配符模式。在这些情况下,通配符模式将匹配字段别名以及具体字段:

GET trips/_field_caps?fields=route_*,transit_mode

字段别名的目标字段上存在一些限制:

  • 目标必须是一个具体字段,而不是一个对象或另一个字段别名。
  • 在创建别名时,目标字段必须存在。
  • 如果定义了嵌套对象,字段别名必须具有与其目标相同的嵌套范围。
  • 此外,字段别名只能有一个目标。这意味着不可能使用字段别名在单个子句中查询多个目标字段。

可以通过映射更新将别名更改为引用新目标。已知的限制是,如果存储的感知器查询包含字段别名,则它们仍将引用其原始目标。更多信息可以在感知器文档中找到。

不支持对字段别名的写入:尝试在索引或更新请求中使用别名将导致失败。同样,别名不能用作 copy_to 的目标或多字段的一部分。

因为文档源中没有别名名称,所以在执行源筛选时不能使用别名。例如,以下请求将返回 _source 的空结果:

GET /_search
{
  "query" : {
    "match_all": {}
  },
  "_source": "route_length_miles"
}

目前只有搜索和字段能力 API 将接受和解析字段别名。不支持字段别名的其他 API,如 term vectors,不能与字段别名一起使用。

最后,一些查询,例如 terms、geo_shape 和 more_like_this,允许从索引文档中提取查询信息。由于在提取文档时不支持字段别名,指定查找路径的查询部分不能引用别名的字段。

数组 Arrays

在 Elasticsearch 中,没有专门的数组数据类型。默认情况下,任何字段都可以包含零个或多个值,但数组中的所有值必须是相同的数据类型。例如:

字符串数组:[ “one”, “two” ]
整数数组:[ 1, 2 ]
数组中嵌套数组:[ 1, [ 2, 3 ]],等同于 [ 1, 2, 3 ]
对象数组:[ { “name”: “Mary”, “age”: 12 }, { “name”: “John”, “age”: 10 } ]

对象数组
对象数组的工作方式与您预期的不同:无法单独查询数组中的每个对象,而不考虑数组中的其他对象。如果需要这样做,那么应该使用嵌套数据类型而不是对象数据类型。

这在 Nested 中有更详细的解释。

动态添加字段时,数组中的第一个值确定字段类型。所有后续的值必须具有相同的数据类型,或者必须至少能够将后续的值强制转换为相同的数据类型。

不支持混合数据类型的数组:[ 10, “some string” ]

数组可以包含空值,空值可以替换为已配置的 null_value,或者完全跳过。空数组 [] 被视为缺失字段,即没有值的字段。

要在文档中使用数组,无需预先进行任何配置,它们可以直接使用:

PUT my-index-000001/_doc/1
{
  "message": "this document has some arrays...",
  "tags":  [ "elasticsearch", "wow" ], 
  "lists": [ 
    {
      "name": "prog_list",
      "description": "programming list"
    },
    {
      "name": "cool_list",
      "description": "cool stuff list"
    }
  ]
}

第一个文档包含了多个数组字段:tags 和 lists。tags 是字符串数组,lists 是对象数组。

PUT my-index-000001/_doc/2 
{
  "message": "no arrays in this document...",
  "tags":  "elasticsearch",
  "lists": {
    "name": "prog_list",
    "description": "programming list"
  }
}

第二个文档不包含数组,但可以索引到相同的字段。

GET my-index-000001/_search
{
  "query": {
    "match": {
      "tags": "elasticsearch" 
    }
  }
}

查询寻找 tags 字段中的 “elasticsearch” 并匹配了两个文档。

多值字段和倒排索引

所有字段类型默认支持多值字段的事实是 Lucene 的起源。Lucene 被设计为全文搜索引擎。为了能够在大块文本内搜索单词,Lucene会将文本标记化为单独的项,并将每个项单独添加到倒排索引中。

这意味着即使简单文本字段也必须默认支持多个值。当添加其他数据类型,如数字和日期时,它们使用与字符串相同的数据结构,因此可以自由获得多个值的支持。

二进制 Binary field type
二进制类型接受Base64编码的二进制值作为字符串。默认情况下,该字段不会被存储,也不可搜索:

PUT my-index-000001
{
  "mappings": {
    "properties": {
      "name": {
        "type": "text"
      },
      "blob": {
        "type": "binary"
      }
    }
  }
}
PUT my-index-000001/_doc/1
{
  "name": "Some binary blob",
  "blob": "U29tZSBiaW5hcnkgYmxvYg=="
}

Base64编码的二进制值不能包含嵌套的换行符 \n。

binary字段的参数编辑
binary字段接受以下参数:

  • doc_values

字段是否应以列存储的方式存储在磁盘上,以便以后可用于排序、聚合或脚本?接受 true 或 false(默认值)。

  • store

字段值是否应存储,并可从_source字段中单独检索?接受 true 或 false(默认值)。

布尔字段类型 Boolean field type

布尔字段接受JSON中的true和false值,但也可以接受被解释为true或false的字符串:

false值

false, “false”, “”(空字符串)

true值

true, “true”

例如:

PUT my-index-000001
{
  "mappings": {
    "properties": {
      "is_published": {
        "type": "boolean"
      }
    }
  }
}
POST my-index-000001/_doc/1?refresh
{
  "is_published": "true"
}
GET my-index-000001/_search
{
  "query": {
    "term": {
      "is_published": true
    }
  }
}

将带有"true"的文档索引,它将被解释为true。

搜索具有JSON true的文档

聚合如terms聚合使用1和0作为键,字符串"true"和"false"作为key_as_string。在脚本中使用布尔字段时,返回true和false:

POST my-index-000001/_doc/1?refresh
{
  "is_published": true
}
POST my-index-000001/_doc/2?refresh
{
  "is_published": false
}
GET my-index-000001/_search
{
  "aggs": {
    "publish_state": {
      "terms": {
        "field": "is_published"
      }
    }
  },
  "sort": [ "is_published" ],
  "fields": [
    {"field": "weight"}
  ],
  "runtime_mappings": {
    "weight": {
      "type": "long",
      "script": "emit(doc['is_published'].value ? 10 : 0)"
    }
  }
}

布尔字段在脚本中使用时返回true和false。

布尔字段的参数
布尔字段接受以下参数:

  • boost
    映射字段级别的查询时增强。接受浮点数,默认为1.0。

  • doc_values
    字段是否应以列存储的方式存储在磁盘上,以便以后可用于排序、聚 合或脚本?接受true(默认值)或false。

  • index
    字段是否应可搜索?接受true(默认)和false。

  • null_value
    接受上面列出的true或false值中的任何一个。该值将替代任何显式的 null值。默认为null,这意味着该字段被视为缺失。请注意,如果使用 脚本参数,则不能设置此参数。

  • on_script_error
    定义如果脚本由脚本参数在索引时引发错误时应该执行什么操作。接受fail(默认值),它将导致整个文档被拒绝,和continue,它将在文档的_ignored元数据字段中注册该字段,并继续索引。只有在还设置了script字段时才能设置此参数。

  • script
    如果设置了此参数,字段将索引由此脚本生成的值,而不是直接从源中读取值。如果在输入文档中为此字段设置了值,那么将拒绝该文档并显示错误。脚本的格式与其运行时等效版本相同。

  • store
    字段值是否应存储,并且是否可以从_source字段中单独检索?接受true或false(默认值)。

  • meta
    关于字段的元数据。

日期字段类型 Date

JSON没有日期数据类型,因此Elasticsearch中的日期可以是:

包含格式化日期的字符串,例如"2015-01-01"或"2015/01/01 12:10:30"。
●表示自纪元以来的毫秒数的数字。
●表示自纪元以来的秒数(配置)。
●自纪元以来的毫秒数值必须为非负数。要表示1970年之前的日期,请使用格式化日期。

在内部,日期将被转换为协调世界时(如果指定了时区)并存储为表示自纪元以来的毫秒数的长数字。

日期上的查询在内部转换为对此长表示的范围查询,聚合和存储字段的结果将根据与字段关联的日期格式转换回字符串。

日期始终以字符串形式呈现,即使它们最初作为JSON文档中的长整数提供。

日期格式可以自定义,但如果没有指定格式,则使用默认格式:

“strict_date_optional_time||epoch_millis”

这意味着它将接受带有可选时间戳的日期,符合strict_date_optional_time或毫秒自纪元支持的格式。

例如:

PUT my-index-000001
{
  "mappings": {
    "properties": {
      "date": {
        "type": "date"
      }
    }
  }
}
PUT my-index-000001/_doc/1
{ "date": "2015-01-01" }
PUT my-index-000001/_doc/2
{ "date": "2015-01-01T12:10:30Z" }
PUT my-index-000001/_doc/3
{ "date": 1420070400001 }
GET my-index-000001/_search
{
  "sort": { "date": "asc" }
}

日期字段使用默认格式。
此文档使用普通日期。
此文档包含时间。
此文档使用自纪元以来的毫秒数。
请注意,返回的排序值都以自纪元以来的毫秒数为单位。

日期将接受带有小数点的数字,如{“date”: 1618249875.123456},但有一些情况(#70085)下会丢失这些日期的精度,因此应避免使用它们。

多日期格式
可以通过在它们之间使用||作为分隔符来指定多个格式。将逐个尝试每个格式,直到找到匹配的格式。将使用第一个格式将自纪元以来的毫秒数值转换回字符串。

PUT my-index-000001
{
  "mappings": {
    "properties": {
      "date": {
        "type":   "date",
        "format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis"
      }
    }
  }
}

日期字段的参数
日期字段接受以下参数:
boost
映射字段级别的查询时增强。接受浮点数,默认为1.0。
doc_values
字段是否应以列存储的方式存储在磁盘上,以便以后可用于排序、聚合或脚本?接受true(默认值)或false。
format
可以解析的日期格式。默认为strict_date_optional_time||epoch_millis。
locale
解析日期时要使用的区域设置,因为不同语言的月份名称和/或缩写不同。默认是ROOT区域设置。
ignore_malformed
如果为true,则会忽略格式不正确的数字。如果为false(默认值),则格式不正确的数字会引发异常并拒绝整个文档。请注意,如果使用脚本参数,无法设置此参数。
index
字段是否应可搜索?接受true(默认)和false。
null_value
接受任何配置格式的日期值,以替代任何明确的null值。默认为null,这意味着该字段被视为缺失。请注意,如果使用脚本参数,则无法设置此参数。
on_script_error
定义如果由脚本参数定义的脚本在索引时引发错误时应执行的操作。接受fail(默认值),它将导致整个文档被拒绝,以及continue,它将在文档的_ignored元数据字段中注册字段并继续索引。只有在还设置了script字段时才能设置此参数。
script
如果设置了此参数,字段将索引由此脚本生成的值,而不是直接从源中读取值。如果在输入文档中为此字段设置了值,那么将拒绝该文档并显示错误。脚本的格式与其运行时等效版本相同,应该发出长整数时间戳。
store
字段值是否应存储,并且是否可以从_source字段中单独检索?接受true或false(默认值)。
meta
关于字段的元数据。

纪元秒
如果需要将日期发送为自纪元以来的秒数,请确保格式中列出了epoch_second:

PUT my-index-000001
{
  "mappings": {
    "properties": {
      "date": {
        "type":   "date",
        "format": "strict_date_optional_time||epoch_second"
      }
    }
  }
}
PUT my-index-000001/_doc/example?refresh
{ "date": 1618321898 }
POST my-index-000001/_search
{
  "fields": [ {"field": "date"}],
  "_source": false
}

这将回复一个类似的日期:

{
  "hits": {
    "hits": [
      {
        "_id": "example",
        "_index": "my-index-000001",
        "_type": "_doc",
        "_score": 1.0,
        "fields": {
          "date": ["2021-04-13T13:51:38.000Z"]
        }
      }
    ]
  }
}

日期纳秒字段类型 Date nanoseconds field type

此数据类型是日期数据类型的一个补充。但两者之间有一个重要的区别。现有的日期数据类型以毫秒分辨率存储日期。日期纳秒数据类型以纳秒分辨率存储日期,这限制了日期范围,大约从1970年到2262年,因为日期仍然存储为表示自纪元以来的纳秒的长整数。

日期的纳秒上的查询在内部转换为对此长整数表示的范围查询,聚合和存储字段的结果将根据与字段关联的日期格式转换为字符串。

日期格式可以自定义,但如果没有指定格式,则使用默认格式:

“strict_date_optional_time_nanos||epoch_millis”

例如:

PUT my-index-000001?include_type_name=true
{
  "mappings": {
    "_doc": {
      "properties": {
        "date": {
          "type": "date_nanos"
        }
      }
    }
  }
}
PUT my-index-000001/_bulk?refresh
{ "index" : { "_id" : "1" } }
{ "date": "2015-01-01" }
{ "index" : { "_id" : "2" } }
{ "date": "2015-01-01T12:10:30.123456789Z" }
{ "index" : { "_id" : "3" } }
{ "date": 1420070400000 }
GET my-index-000001/_search
{
  "sort": { "date": "asc"},
  "runtime_mappings": {
    "date_has_nanos": {
      "type": "boolean",
      "script": "emit(doc['date'].value.nano != 0)"
    }
  },
  "fields": [
    {
      "field": "date",
      "format": "strict_date_optional_time_nanos"
    },
    {
      "field": "date_has_nanos"
    }
  ]
}

日期字段使用默认格式。

此文档使用普通日期。

此文档包含时间。

此文档使用自纪元以来的毫秒数。

请注意,返回的排序值都以自纪元以来的纳秒为单位。

在脚本中使用 .nano 可以返回日期的纳秒组件。

您可以在使用字段参数提取数据时指定格式。使用strict_date_optional_time_nanos,否则会得到一个四舍五入的结果。

您还可以指定用||分隔的多个日期格式。与日期字段一样,可以使用相同的映射参数。

日期纳秒将接受带有小数点的数字,例如{“date”: 1618249875.123456},但在某些情况下(#70085)会丢失这些日期的精度,因此应避免使用它们。

限制
即使使用date_nanos字段,聚合仍然以毫秒分辨率进行,这一限制也影响到了转换。

密集向量字段类型 Dense vector field type

密集向量字段存储浮点值的密集向量。向量中可以包含的维数不应超过2048。密集向量字段是单值字段。

密集向量字段不支持查询、排序或聚合。它们只能通过专用的向量函数在脚本中访问。

您可以将密集向量索引为浮点数数组。

PUT my-index-000001
{
  "mappings": {
    "properties": {
      "my_vector": {
        "type": "dense_vector",
        "dims": 3
      },
      "my_text" : {
        "type" : "keyword"
      }
    }
  }
}
PUT my-index-000001/_doc/1
{
  "my_text" : "text1",
  "my_vector" : [0.5, 10, 6]
}
PUT my-index-000001/_doc/2
{
  "my_text" : "text2",
  "my_vector" : [-0.5, 10, 10]
}

dims - 向量中的维数,必填参数。

展平字段类型 Flattened field type

默认情况下,对象中的每个子字段都会被单独映射和索引。如果子字段的名称或类型事先不知道,那么它们会动态映射。

flattened 类型提供了另一种方法,它会将整个对象映射为单个字段。给定一个对象,flattened 映射将解析出其叶子值并将它们索引为关键字。然后,可以通过简单的查询和聚合搜索对象的内容。

这种数据类型对于索引具有大量或未知数量唯一键的对象非常有用。整个 JSON 对象只创建一个字段映射,这有助于防止映射爆炸导致具有太多不同字段映射。

另一方面,展平的对象字段在搜索功能方面存在一种权衡。只允许基本查询,不支持数值范围查询或高亮显示。更多关于限制的信息可以在支持的操作部分找到。

展平映射类型不应用于索引所有文档内容,因为它将所有值视为关键字,不提供完整的搜索功能。在大多数情况下,每个子字段都有自己的映射条目的默认方法运行良好。

可以创建展平的对象字段如下:

PUT bug_reports
{
  "mappings": {
    "properties": {
      "title": {
        "type": "text"
      },
      "labels": {
        "type": "flattened"
      }
    }
  }
}
POST bug_reports/_doc/1
{
  "title": "Results are not sorted correctly.",
  "labels": {
    "priority": "urgent",
    "release": ["v1.2.5", "v1.3.0"],
    "timestamp": {
      "created": 1541458026,
      "closed": 1541457010
    }
  }
}

在索引期间,将为 JSON 对象中的每个叶子值创建标记。这些值将作为字符串关键字进行索引,而不会进行分析或对数字或日期进行特殊处理。

查询顶级展平字段将搜索对象中的所有叶子值:

POST bug_reports/_search
{
  "query": {
    "term": {"labels": "urgent"}
  }
}

要在展平对象中的特定键上查询,使用对象点表示法:

POST bug_reports/_search
{
  "query": {
    "term": {"labels.release": "v1.3.0"}
  }
}

支持的操作
由于展平字段索引值的方式相似,展平字段与关键字字段共享许多相同的映射和搜索功能。

目前,展平对象字段可与以下查询类型一起使用:

  • term、terms 和 terms_set
  • prefix
  • range
  • match 和 multi_match
  • query_string 和 simple_query_string
  • exists

在查询时,无法使用通配符引用字段键,如 { “term”: {“labels.time*”: 1541457010}}。请注意,所有查询,包括范围查询,都将值视为字符串关键字。展平字段不支持高亮显示。

可以对展平的对象字段进行排序,并执行类似关键字的简单聚合,例如 terms。与查询一样,没有为数值提供特殊支持,JSON 对象中的所有值都被视为关键字进行比较。

目前,展平的对象字段无法存储。无法在映射中指定 store 参数。

检索展平字段的值
可以使用 fields 参数检索字段值和具体子字段。由于展平字段将一个整个对象映射为单个字段,响应包含来自 _source 的不经修改的结构。

但是,可以通过在请求中明确指定它们来获取单个子字段。这仅适用于具体路径,而不能使用通配符:

PUT my-index-000001
{
  "mappings": {
    "properties": {
      "flattened_field": {
        "type": "flattened"
      }
    }
  }
}
PUT my-index-000001/_doc/1?refresh=true
{
  "flattened_field" : {
    "subfield" : "value"
  }
}
POST my-index-000001/_search
{
  "fields": ["flattened_field.subfield"],
  "_source": false
}

您还可以使用 Painless 脚本从展平字段的子字段中检索值。在 Painless 脚本中,不要使用 doc[‘<field_name>’].value,而要使用 doc[‘<field_name>.<sub-field_name>’].value。例如,如果映射中包含两个字段,其中一个是展平类型:

PUT my-index-000001
{
  "mappings": {
    "properties": {
      "title": {
        "type": "text"
      },
      "labels": {
        "type": "flattened"
      }
    }
  }
}

为映射的字段编制一些包含标签字段的文档。标签字段具有三个子字段:

POST /my-index-000001/_bulk?refresh
{"index":{}}
{"title":"Something really urgent","labels":{"priority":"urgent","release":["v1.2.5","v1.3.0"],"timestamp":{"created":1541458026,"

地理点字段类型 Geopoint field type

地理点字段(geo_point)接受纬度-经度对,可用于以下用途:

  1. 查找位于边界框内、距中心点一定距离内、位于多边形内或在geo_shape查询内的地理点。
  2. 按地理或距离从中心点聚合文档。
  3. 将距离集成到文档的相关性评分中。
  4. 按距离对文档进行排序。

地理点可以以以下五种方式指定,如下所示:

PUT my-index-000001
{
  "mappings": {
    "properties": {
      "location": {
        "type": "geo_point"
      }
    }
  }
}

PUT my-index-000001/_doc/1
{
  "text": "地理点作为对象",
  "location": { 
    "lat": 41.12,
    "lon": -71.34
  }
}

PUT my-index-000001/_doc/2
{
  "text": "地理点作为字符串",
  "location": "41.12,-71.34" 
}

PUT my-index-000001/_doc/3
{
  "text": "地理点作为地理哈希",
  "location": "drm3btev3e86" 
}

PUT my-index-000001/_doc/4
{
  "text": "地理点作为数组",
  "location": [ -71.34, 41.12 ] 
}

PUT my-index-000001/_doc/5
{
  "text": "地理点作为WKT POINT原语",
  "location" : "POINT (-71.34 41.12)" 
}

GET my-index-000001/_search
{
  "query": {
    "geo_bounding_box": { 
      "location": {
        "top_left": {
          "lat": 42,
          "lon": -72
        },
        "bottom_right": {
          "lat": 40,
          "lon": -74
        }
      }
    }
  }
}
  1. 以对象形式表示的地理点,带有latlon键。
  2. 以字符串形式表示的地理点,格式为"纬度,经度"。
  3. 以地理哈希形式表示的地理点
  4. 以数组形式表示的地理点,格式为[经度,纬度]
  5. 以WKT POINT原语形式表示的地理点,格式为"POINT(经度 纬度)"。

重要提示: 请注意,字符串形式的地理点按纬度,经度排序,而数组形式的地理点按相反的顺序,即经度,纬度

初始情况下,纬度,经度形式同时用于数组和字符串,但很早就更改为符合GeoJSON使用的格式。

注意: 地理哈希(geohash)是将纬度和经度的位用base32编码字符串交错在一起而成。地理哈希中的每个字符为精度添加额外的5位。因此,哈希越长,精度越高。对于索引目的,地理哈希转换为纬度-经度对。在此过程中,仅使用前12个字符,因此在地理哈希中指定超过12个字符不会增加精度。这12个字符提供60位精度,可以将潜在的误差降至不到2厘米。

地理点字段的参数:

  • ignore_malformed:如果设置为true,将忽略格式错误的地理点。如果设置为false(默认值),则格式错误的地理点将引发异常并拒绝整个文档。地理点被视为格式错误,如果其纬度范围超出-90 <= 纬度 <= 90,或者经度范围超出-180 <= 经度 <= 180。请注意,如果使用了脚本参数,无法设置此选项。

  • ignore_z_value:如果设置为true(默认值),将接受三维点(存储在源中),但仅索引纬度和经度值;第三维度将被忽略。如果设置为false,包含除纬度和经度以外的任何值的地理点将引发异常并拒绝整个文档。请注意,如果使用了脚本参数,无法设置此选项。

  • index:是否应将字段设置为可搜索?接受true(默认值)和false

  • null_value:接受用于替换任何显式空值的地理点值。默认值为null,表示该字段将被视为缺失。请注意,如果使用了脚本参数,无法设置此选项。

  • on_script_error:定义如果脚本参数定义的脚本在索引时引发错误应该执行的操作。接受fail(默认值),它将导致整个文档被拒绝,和continue,它将将字段注册在文档的_ignored元数据字段中并继续索引。此参数只能在同时设置脚本字段时才能设置。

  • script:如果设置了此参数,字段将索引由该脚本生成的值,而不是直接从源中读取值。如果在输入文档上为此字段设置了值,文档将被拒绝并显示错误。脚本的格式与其运行时等效项相同,应以(纬度、经度)双精度值对的形式发出点。

在脚本中使用地理点:

在脚本中访问地理点的值时,该值以GeoPoint对象的形式返回,允许访问.lat.lon值。

示例:

def geopoint = doc['location

'].value;
def lat = geopoint.lat;
def lon = geopoint.lon;

出于性能原因,最好直接访问lat/lon值,如下所示:

def lat = doc['location'].lat;
def lon = doc['location'].lon;

地理形状字段类型 Geo Shape Field Type

地理形状字段类型(Geo Shape Field Type)是 Elasticsearch 中用于索引和搜索任意地理形状(如矩形和多边形)的数据类型。它应该在要索引的数据或要执行的查询中包含不仅仅是点的形状时使用。你可以使用地理形状查询(geo_shape query)来查询使用此类型的文档。

地理形状字段可以使用不同的地理树编码方法(BKD编码或前缀树编码)来进行编码。默认情况下,Elasticsearch将地理形状的值编码为BKD树。如果不指定以下映射选项,则将使用BKD编码:

  • distance_error_pct
  • points_only
  • precision
  • strategy
  • tree_levels
  • tree

如果指定了上述选项之一,字段将使用前缀树编码。前缀树编码已被弃用。

以下是地理形状字段的映射选项:

  • tree:已弃用,不再使用。前缀树的名称,可选值为 “geohash”(用于Geohash前缀树)和 “quadtree”(用于Quad前缀树)。

  • precision:已弃用,不再使用。此参数可用来设置tree_levels参数的合适值,该值指定所需的精度,Elasticsearch将计算出tree_levels参数的最佳值以满足所需精度。此参数应该是一个数字,后面可以跟一个可选的距离单位,如 “m”(米)、“km”(千米)、“mi”(英里)等。

  • tree_levels:已弃用,不再使用。前缀树要使用的最大层级数,可用来控制形状表示的精度以及索引的术语数。默认值取决于所选的前缀树实现。如果用户了解底层实现的工作原理,可以使用此参数。然而,Elasticsearch在内部仅使用tree_levels参数,并且即使使用precision参数,也是通过映射API返回tree_levels参数。

  • strategy:已弃用,不再使用。该参数定义了索引和搜索时如何表示形状的方法。建议让Elasticsearch自动设置此参数。有两种可用的策略:recursive 和 term。Recursive 和 Term策略已弃用,将在将来的版本中移除。虽然它们仍然可用,Term策略仅支持点类型(points_only参数将自动设置为true),而Recursive策略支持所有形状类型。同时,对于这两种策略,Elasticsearch建议使用向量编码的方法(Vector Indexing)。

  • distance_error_pct:已弃用,不再使用。用于指示前缀树在精度方面应具有多大程度的准确性。默认值为0.025(2.5%),最大支持值为0.5。注意:如果显式定义了精度或tree_level,则此值将默认为0。这可以导致对具有低误差的高分辨率形状(例如,具有小于0.001误差的1米大的形状)而言,内存使用量显著增加。为了提高索引性能(以牺牲查询准确性为代价),可以显式定义tree_level或precision以及合理的distance_error_pct,但要注意,大形状会有更多的误报。

  • orientation:可选。字段的WKT多边形的默认方向。此参数设置并返回RIGHT(顺时针)或LEFT(逆时针)值。但可以以多种方式指定任一值。

    • 要设置RIGHT,请使用以下参数或其大写变体:
      • right
      • counterclockwise
      • ccw
    • 要设置LEFT,请使用以下参数或其大写变体:
      • left
      • clockwise
      • cw
  • points_only:已弃用,不再使用。将此选项设置为true(默认值为false)会配置地理形状字段类型仅用于点形状(注意:不支持多点)。这会优化当只有点会被索引时的索引和搜索性能,例如在已知只有点将被索引时,可以提高geohash和quadtree的性能

。目前,不能对geo_point字段类型执行geo_shape查询。此选项弥合了这一差距,通过改善点字段上的性能,使得在只有点的字段上执行geo_shape查询变得最佳。

  • ignore_malformed:如果为true,则忽略格式错误的GeoJSON或WKT形状。如果为false(默认值),格式错误的GeoJSON和WKT形状将引发异常并拒绝整个文档。

  • ignore_z_value:如果为true(默认值),将接受包含三维点(存储在源中),但仅索引纬度和经度值;忽略第三维度。如果为false,包含多于纬度和经度(两个维度)值的地理点会引发异常并拒绝整个文档。

  • coerce:如果为true,将自动封闭多边形中的未封闭线性环(linear rings)。

地理形状字段类型的索引方法:地理形状类型通过将形状分解为三角形网格并将每个三角形作为BKD树中的7维点进行索引。这提供了接近完美的空间分辨率(精度高达1e-7小数度),因为所有空间关系都是使用原始形状的编码向量表示来计算的,而不是使用前缀树编码使用的栅格格网表示。网格化器的性能主要取决于定义多边形/多多边形的顶点数。虽然这是默认的索引技术,但仍然可以通过根据适当的映射选项来设置tree或strategy参数来使用前缀树编码。注意,这些参数现在已被弃用,将在将来的版本中移除。

重要说明:

  • 包含关系查询 - 当使用新的默认向量索引策略时,定义为包含关系的地理形状查询(contains)仅支持在ElasticSearch 7.5.0或更高版本创建的索引。

前缀树:为了有效地在倒排索引中表示形状,使用前缀树的实现将形状转换为表示网格方格的一系列哈希值(通常称为“栅格”)。前缀树使用多个网格层,每个层的精度逐渐增加,以表示地球。这可以被视为在更高的缩放级别提高地图或图像的详细程度。由于这种方法导致了带有索引形状的精度问题,因此已经弃用,而向量编码方法(见索引方法)已取而代之。

提供多个前缀树实现:

  • GeohashPrefixTree:使用Geohash表示网格方格。Geohash是基于经度和纬度位交错的base32编码字符串。因此,哈希值越长,精度越高。每个添加到Geohash的字符代表另一个树层,并增加5位精度。Geohash表示一个矩形区域,有32个子矩形。Elasticsearch中Geohash的最大层级数是24;默认值是9。

  • QuadPrefixTree:使用Quadtree表示网格方格。与Geohash类似,Quadtree将纬度和经度的位数交错,生成一个位集。Quadtree中的一个树层表示该位集中的2位,分别用于每个坐标。Elasticsearch中Quadtree的最大层级数是29;默认值是21。

空间策略:已弃用,不再使用。所选的索引实现依赖于SpatialStrategy,以选择如何分解形状(作为网格方格或网格方格三角网格)。每个策略回答以下问题:

  • 可以索引哪种形状?
  • 可以使用哪些查询操作和形状?
  • 是否支持一个字段中的多个形状?

提供以下策略实现(以及相应的功能):

  • Recursive策略:支持所有形状,支持的查询操作包括INTERSECTS、DISJOINT、WITHIN、CONTAINS。允许一个字段中包含多个形状。

  • Term策略:支持点,支持的查询操作包括INTERSECTS。允许一个字段中包含多个形状。在使用Term策略时,points_only参数会自动设置为true。

精度:Recursive策略和Term策略不能提供100%的准确性,具体取决于如何配置它们,可能会返回INTERSECTS、WITHIN和CONTAINS查询的一些误报,以及DISJOINT查询的一些漏报。为了减轻这一问题,重要的是选择tree_levels参数的适当值并相应地调整期望。例如,一个点可能接近特定网格单元的边界,因此可能与仅与其相邻的网格单元匹配的查询不匹配,尽管该形状非常接近该点。

示例:

以下是一个示例,演示如何映射一个具有地理形状字段的索引:

PUT /example
{
  "mappings": {
    "properties": {
      "location": {
        "type": "geo_shape"
      }
    }
  }
}

在上述映射中,将location字段映射为geo_shape类型,使用默认的向量实现,提供了大约1e-7小数度的精度。

性能注意事项:

使用前缀树的性能考虑:使用前缀树时,Elasticsearch使用树中的路径作为倒排索引中的术语。层级越高(因此精度越高

),生成的术语就越多。当然,计算这些术语,将它们保存在内存中并将它们存储在磁盘上都是有代价的。特别是对于更高级别的树,即使有适度的数据量,索引可能会变得非常大。此外,特征的大小也很重要。大型复杂多边形在更高的树层级时会占用大量空间。哪种设置适合取决于用例。一般来说,需要在准确性、索引大小和查询性能之间进行权衡。

Elasticsearch中两种实现的默认值都是在赤道上约50米的适度精度的折衷方案。这允许索引数千万个形状,而相对于输入大小来说,不会过分膨胀索引的大小。

在使用前缀树实现的地理形状上执行geo_shape查询,如果search.allow_expensive_queries设置为false,查询将不会被执行。

输入结构:

形状可以使用GeoJSON或Well-Known Text(WKT)格式表示。以下表格提供了GeoJSON和WKT到Elasticsearch类型的映射:

GeoJSON类型 WKT类型 Elasticsearch类型 描述
Point POINT point 单个地理坐标。注意:Elasticsearch仅使用WGS-84坐标。
LineString LINESTRING linestring 由两个或多个点给定的任意线。
Polygon POLYGON polygon 由点的列表列表定义的多边形。每个(外部)列表中的第一个点和最后一个点必须相同(多边形必须封闭),因此需要n + 1个顶点来创建一个n边的多边形,最少需要4个顶点。
MultiPoint MULTIPOINT multipoint 一组不相连但可能相关的点。
MultiLineString MULTILINESTRING multilinestring 一组单独的线串。
MultiPolygon MULTIPOLYGON multipolygon 一组单独的多边形。
GeometryCollection GEOMETRYCOLLECTION geometrycollection 类似于multi*形状的GeoJSON形状,不同之处在于多个类型可以共存(例如,一个点和一个线串)。
N/A BBOX envelope 由指定的仅有左上角和右下角点而没有其他点的矩形(bounding rectangle)。
N/A N/A circle 由中心点和半径(默认为METERS)指定的圆。

对于所有类型,坐标数组中的正确坐标顺序是经度、纬度(X、Y)。这与许多地理空间API(例如,Google Maps)通常使用的纬度、经度(Y、X)不同。

点:

点是单个地理坐标,例如建筑物的位置或智能手机的Geolocation API给出的当前位置。以下是GeoJSON中点的示例:

POST /example/_doc
{
  "location" : {
    "type" : "Point",
    "coordinates" : [-77.03653, 38.897676]
  }
}

以下是WKT中点的示例:

POST /example/_doc
{
  "location" : "POINT (-77.03653 38.897676)"
}

线串:

线串由两个或多个位置的数组定义。通过只指定两个点,线串将表示一条直线。指定两个以上的点会创建一条任意路径。以下是GeoJSON中线串的示例:

POST /example/_doc
{
  "location" : {
    "type" : "LineString",
    "coordinates" : [[-77.03653, 38.897676], [-77.009051, 38.889939]]
  }
}

以下是WKT中线串的示例:

POST /example/_doc
{
  "location" : "LINESTRING (-77.03653 38.897676, -77.009051 38.889939)"
}

上述线串将从白宫出发绘制一条直线到美国国会大厦。

多边形:

多边形由点列表的列表定义。每个(外部)列表中的第一个点和最后一个点必须相同(多边形必须封闭)。以下是GeoJSON中多边形的示例:

POST /example/_doc
{
  "location" : {
    "type" : "Polygon",
    "coordinates" : [
      [ [100.0, 0.0], [101.0, 0.0], [101.0, 1.0], [100.0, 1.0], [100.0, 0.0] ]
    ]
  }
}

以下是WKT中多边形的示例:



POST /example/_doc
{
  "location" : "POLYGON ((100 0, 101 0, 101 1, 100 1, 100 0))"
}

上述多边形定义了一个简单的矩形,坐标范围从经度100.0到101.0,纬度从0.0到1.0。

多点:

多点是由一组不相连但可能相关的点组成的。每个点都有一个坐标。以下是GeoJSON中多点的示例:

POST /example/_doc
{
  "location" : {
    "type" : "MultiPoint",
    "coordinates" : [[100.0, 0.0], [101.0, 1.0]]
  }
}

以下是WKT中多点的示例:

POST /example/_doc
{
  "location" : "MULTIPOINT (100 0, 101 1)"
}

上述示例包含两个点。

多线串:

多线串是一组单独的线串,每个线串都由点的数组定义。以下是GeoJSON中多线串的示例:

POST /example/_doc
{
  "location" : {
    "type" : "MultiLineString",
    "coordinates" : [
      [ [-77.03653, 38.897676], [-77.009051, 38.889939] ],
      [ [-77.095158, 38.864666], [-77.016683, 38.915387] ]
    ]
  }
}

以下是WKT中多线串的示例:

POST /example/_doc
{
  "location" : "MULTILINESTRING ((-77.03653 38.897676, -77.009051 38.889939), (-77.095158 38.864666, -77.016683 38.915387))"
}

上述示例包含两个线串,每个线串都是一条路径。

多多边形:

多多边形是一组单独的多边形,每个多边形都由点列表的列表定义。以下是GeoJSON中多多边形的示例:

POST /example/_doc
{
  "location" : {
    "type" : "MultiPolygon",
    "coordinates" : [
      [
        [ [102.0, 2.0], [103.0, 2.0], [103.0, 3.0], [102.0, 3.0], [102.0, 2.0] ]
      ],
      [
        [ [100.0, 0.0], [101.0, 0.0], [101.0, 1.0], [100.0, 1.0], [100.0, 0.0] ]
      ]
    ]
  }
}

以下是WKT中多多边形的示例:

POST /example/_doc
{
  "location" : "MULTIPOLYGON (((102 2, 103 2, 103 3, 102 3, 102 2)), ((100 0, 101 0, 101 1, 100 1, 100 0))"
}

上述示例包含两个多边形,每个多边形都是一个矩形。

空间策略:

空间策略定义如何在倒排索引中表示形状。每个策略具有特定的特征集和适用性。空间策略不影响查询或存储多边形的形状本身。两个策略的区别是如何分解形状,并如何在倒排索引中存储特征。两个主要策略实现是:

  • Recursive策略:支持所有形状,支持的查询操作包括INTERSECTS、DISJOINT、WITHIN、CONTAINS。允许一个字段中包含多个形状。

  • Term策略:支持点,支持的查询操作包括INTERSECTS。允许一个字段中包含多个形状。

前缀树策略(已弃用):前缀树策略用于使用前缀树作为索引形状表示的策略。前缀树策略已弃用,不再使用。

示例:

以下是使用Recursive策略的示例:

PUT /example
{
  "mappings": {
    "properties": {
      "location": {
        "type": "geo_shape",
        "strategy": "recursive"
      }
    }
  }
}

在上述示例中,location字段使用了Recursive策略。

以下是使用Term策略的示例:

PUT /example
{
  "mappings": {
    "properties": {
      "location": {
        "type": "geo_shape",
        "strategy": "term"
      }
    }
  }
}

在上述示例中,location字段使用了Term策略。

前缀树策略已弃用,不再使用。

查询方法:

地理形状字段可以使用geo_shape查询来执行各种地理查询操作,如INTERSECTS、DISJOINT、WITHIN、CONTAINS等。下面是一些常见的地理查询示例:

  • 使用INTERSECTS查询来查找与指定形状相交的文档:
{
  "query": {
    "geo_shape": {
      "location": {
        "shape": {
          "type": "Polygon",
          "coordinates": [[
            [-77.03653, 38.897676],
            [-77.009051, 38.889939],
            [-77.006438, 38.893371],
            [-77.03653, 38.897676]
          ]]
        },
        "relation": "intersects"
      }
    }
  }
}
  • 使用DISJOINT查询来查找与指定形状不相交的文档:
{
  "query": {
    "geo_shape": {
      "location": {
        "shape": {
          "type": "Polygon",
          "coordinates": [[
            [-77.03653, 38.897676],
            [-77.009051, 38.889939],
            [-77.006438, 38.893371],
            [-

77.03653, 38.897676]
          ]]
        },
        "relation": "disjoint"
      }
    }
  }
}
  • 使用WITHIN查询来查找包含在指定形状内的文档:
{
  "query": {
    "geo_shape": {
      "location": {
        "shape": {
          "type": "Polygon",
          "coordinates": [[
            [-77.03653, 38.897676],
            [-77.009051, 38.889939],
            [-77.006438, 38.893371],
            [-77.03653, 38.897676]
          ]]
        },
        "relation": "within"
      }
    }
  }
}
  • 使用CONTAINS查询来查找包含指定形状的文档:
{
  "query": {
    "geo_shape": {
      "location": {
        "shape": {
          "type": "Point",
          "coordinates": [-77.022731, 38.897705]
        },
        "relation": "contains"
      }
    }
  }
}

这些示例只是一些基本的查询操作。您可以根据需要组合这些操作,以实现更复杂的地理查询。请注意,性能会受到形状的复杂性、索引设置和查询条件的影响,因此需要根据具体情况进行调整。

直方图字段类型 Histogram

Histogram字段是一种用于存储预聚合的数值数据,表示直方图的字段类型。直方图数据由两个成对的数组定义:

  1. values 数组,包含双精度浮点数,表示直方图的桶(buckets)。这些值必须按升序提供。
  2. counts 数组,包含整数,表示落入每个桶中的值的数量。这些数量必须是正数或零。

由于values数组中的元素对应于counts数组中相同位置的元素,这两个数组必须具有相同的长度。

一个直方图字段只能存储文档中的一个值和计数数组对。不支持嵌套数组。直方图字段不支持排序。

参数:

  • time_series_metric(可选,字符串):标记字段是否为时间序列度量。对于直方图字段,此参数接受值 “histogram”。

使用:
直方图字段主要用于聚合。为了更容易进行聚合,直方图字段数据存储为二进制文档值(doc values),而不是索引。其字节大小最多为 13 * numValues,其中 numValues 是提供的数组的长度。

因为数据不被索引,您只能使用直方图字段进行以下聚合和查询:

  • 最小值聚合(min aggregation)
  • 最大值聚合(max aggregation)
  • 求和聚合(sum aggregation)
  • 计数聚合(value_count aggregation)
  • 平均值聚合(avg aggregation)
  • 百分位数聚合(percentiles aggregation)
  • 百分位数排名聚合(percentile ranks aggregation)
  • 箱线图聚合(boxplot aggregation)
  • 直方图聚合(histogram aggregation)
  • 范围聚合(range aggregation)
  • 存在查询(exists query)

构建直方图:
当将直方图作为聚合的一部分时,结果的准确性将取决于构建直方图的方式。重要的是考虑将用于构建直方图的百分位数聚合模式。一些可能性包括:

  • 对于T-Digest模式,values数组表示均值中心位置,counts数组表示归属于每个中心的值的数量。如果算法已经开始近似百分位数,那么这种不准确性会传递到直方图中。
  • 对于高动态范围(HDR)直方图模式,values数组表示每个桶间隔的固定上限,counts数组表示归属于每个间隔的值的数量。这个实现维护了一个固定的最坏情况百分比误差(指定为有效数字的数量),因此在生成直方图时使用的值将是您在聚合时能够实现的最大精度。

直方图字段是“算法不可知”的,不存储特定于T-Digest或HDRHistogram的数据。这意味着字段在技术上可以使用任一算法进行聚合,但实际上用户应该选择一种算法并以该方式索引数据(例如,T-Digest的中心点或HDRHistogram的间隔)以确保最佳准确性。

示例:
以下是一个创建索引和存储直方图字段的示例:

创建一个新索引,包括两个字段映射:

  • my_histogram,用于存储百分位数数据的直方图字段
  • my_text,用于存储直方图标题的关键字字段
PUT my-index-000001
{
  "mappings": {
    "properties": {
      "my_histogram": {
        "type": "histogram"
      },
      "my_text": {
        "type": "keyword"
      }
    }
  }
}

存储两个直方图的预聚合数据,分别为 histogram_1histogram_2

PUT my-index-000001/_doc/1
{
  "my_text": "histogram_1",
  "my_histogram": {
      "values": [0.1, 0.2, 0.3, 0.4, 0.5], 
      "counts": [3, 7, 23, 12, 6] 
   }
}

PUT my-index-000001/_doc/2
{
  "my_text": "histogram_2",
  "my_histogram": {
      "values": [0.1, 0.25, 0.35, 0.4, 0.45, 0.5], 
      "counts": [8, 17, 8, 7, 6, 2] 
   }
}

ip字段类型 IP

ip字段类型是用于索引和存储IPv4或IPv6地址的字段类型。

示例创建一个包含ip字段的索引:

PUT my-index-000001
{
  "mappings": {
    "properties": {
      "ip_addr": {
        "type": "ip"
      }
    }
  }
}

然后,存储一个IPv4地址的文档:

PUT my-index-000001/_doc/1
{
  "ip_addr": "192.168.1.1"
}

要查询ip字段,您可以使用CIDR表示法,例如:

GET my-index-000001/_search
{
  "query": {
    "term": {
      "ip_addr": "192.168.0.0/16"
    }
  }
}

或者,如果您要查询IPv6地址,需要注意冒号是查询字符串查询的特殊字符,因此IPv6地址需要转义。最简单的方法是在搜索的值周围加上引号:

GET my-index-000001/_search
{
  "query": {
    "query_string": {
      "query": "ip_addr:\"2001:db8::/48\""
    }
  }
}

ip字段类型接受以下参数:

  • boost:映射字段级别的查询时间加权。接受浮点数,默认为1.0。
  • doc_values:指定字段是否以列式存储方式存储在磁盘上,以便以后用于排序、聚合或脚本。接受true(默认值)或false。
  • ignore_malformed:如果为true,则忽略格式错误的IP地址。如果为false(默认值),则格式错误的IP地址会引发异常并拒绝整个文档。注意,如果使用script参数,则无法设置此参数。
  • index:指定字段是否可搜索。接受true(默认值)和false。
  • null_value:接受用于替代任何明确的空值的IPv4或IPv6值。默认为null,这意味着该字段将被视为丢失。请注意,如果使用script参数,则无法设置此参数。
  • on_script_error:定义如果script参数定义的脚本在索引时引发错误时应执行的操作。接受reject(默认值),这将导致整个文档被拒绝,和ignore,这将在文档的_ignored元数据字段中注册字段,并继续索引。只能在设置script字段时才能设置此参数。
  • script:如果设置了此参数,字段将索引由该脚本生成的值,而不是直接从源中读取值。如果在输入文档中设置了此字段的值,那么将拒绝文档并生成错误。脚本采用与其运行时等效的格式,应生成包含IPv4或IPv6格式地址的字符串。
  • store:指定字段值是否应与_source字段分开存储和检索。接受true或false(默认值)。
  • time_series_dimension:(技术预览功能)用于内部用途,由Elastic公司使用。

父子关系字段类型 join

join字段类型是Elasticsearch中的一种特殊字段类型,用于在同一索引的文档之间创建父子关系。关系部分定义了文档内的一组可能的关系,每个关系都包括一个父名称和一个子名称。

在Elasticsearch中,不建议使用多级关系来复制关系模型。每个关系级别在查询时会增加内存和计算方面的开销。为了获得更好的搜索性能,建议对数据进行去规范化(denormalize)。

父子关系可以如下所示定义:

PUT my-index-000001
{
  "mappings": {
    "properties": {
      "my_id": {
        "type": "keyword"
      },
      "my_join_field": { 
        "type": "join",
        "relations": {
          "question": "answer" 
        }
      }
    }
  }
}

在上述示例中,创建了一个名为my_join_fieldjoin字段,定义了一个父关系 “question” 和一个子关系 “answer”。

要索引具有关系的文档,必须在文档的源中提供关系的名称以及可选的父文档。例如,以下示例在 “question” 上下文中创建了两个父文档:

PUT my-index-000001/_doc/1?refresh
{
  "my_id": "1",
  "text": "This is a question",
  "my_join_field": {
    "name": "question" 
  }
}

PUT my-index-000001/_doc/2?refresh
{
  "my_id": "2",
  "text": "This is another question",
  "my_join_field": {
    "name": "question"
  }
}

父文档的索引时,您可以选择仅指定关系的名称作为一种快捷方式,而无需将其封装在正常的对象表示法中:

PUT my-index-000001/_doc/1?refresh
{
  "my_id": "1",
  "text": "This is a question",
  "my_join_field": "question" 
}

PUT my-index-000001/_doc/2?refresh
{
  "my_id": "2",
  "text": "This is another question",
  "my_join_field": "question"
}

当索引子文档时,必须在_source中添加关系的名称以及文档的父ID。为了正确建立父子关系,子文档必须路由到其更大的父文档上,因此必须使用正确的路由值。以下示例展示了如何索引两个子文档:

PUT my-index-000001/_doc/3?routing=1&refresh 
{
  "my_id": "3",
  "text": "This is an answer",
  "my_join_field": {
    "name": "answer", 
    "parent": "1" 
  }
}

PUT my-index-000001/_doc/4?routing=1&refresh
{
  "my_id": "4",
  "text": "This is another answer",
  "my_join_field": {
    "name": "answer",
    "parent": "1"
  }
}

要建立父子关系,子文档必须与其更大的父文档在同一个分片上。因此,在获取、删除或更新子文档时,必须提供相同的路由值。

join字段使用全局序数来加速关联操作。全局序数需要在分片发生任何更改后进行重建。全局序数默认会急切地进行构建:如果索引发生更改,join字段的全局序数将作为刷新的一部分重建。然而,在大多数情况下,这是正确的权衡,否则,当首次使用父子关联查询或聚合时,将重建join字段的全局序数。这可能会为用户引入显著的延迟,通常会更糟,因为在多个写操作发生时,可能会尝试在单个刷新间隔内重建多个join字段的全局序数。

如果join字段很少使用,并且写操作频繁发生,可能有意义禁用急切加载:

PUT my-index-000001
{
  "mappings": {
    "properties": {
      "my_join_field": {
        "type": "join",
        "relations": {
           "question": "answer"
        },
        "eager_global_ordinals": false
      }
    }
  }
}

上述设置允许禁用急切加载,从而在索引变化时不重建join字段的全局序数。

join字段允许定义多个子文档作为单个父文档的子文档。它还允许定义多个父文档关系,但需要确保它们都在相同的分片上。

多级父子关系存在,但不建议使用多级关系来复制关系数据库模型。每个关系级别会增加查询时的内存和计算开销。为了获得更好的搜索性能,建议对数据进行去规范化。

关键字类型 Keyword type family

关键字类型家族包括以下字段类型:

  1. keyword:用于结构化内容,如ID、电子邮件地址、主机名、状态码、邮政编码或标签。
  2. constant_keyword:针对始终包含相同值的关键字字段。
  3. wildcard:用于非结构化的机器生成内容,该通配符类型经过优化,适用于值较大或基数较高的字段。

关键字字段通常用于排序、聚合和术语级别查询,如term查询。避免将关键字字段用于全文搜索,请使用text字段类型代替。

关键字字段类型示例

以下是一个基本关键字字段的映射示例:

PUT my-index-000001
{
  "mappings": {
    "properties": {
      "tags": {
        "type":  "keyword"
      }
    }
  }
}

映射数字标识符

并非所有数字数据都应映射为数字字段数据类型。Elasticsearch会为数字字段(如整数或长整数)进行范围查询进行优化。但是,关键字字段更适用于术语和其他术语级别查询。

例如,ISBN或产品ID等标识符很少用于范围查询,但通常使用术语级别查询来检索它们。如果符合以下条件,可以考虑将数字标识符映射为关键字:

  • 没有计划使用范围查询搜索标识符数据。
  • 快速检索很重要。关键字字段上的术语查询通常比数字字段上的术语查询更快。

如果不确定要使用哪种字段类型,可以使用多字段将数据同时映射为关键字和数字数据类型。

基本关键字字段的参数

关键字字段接受以下参数:

  • boost:字段级别的查询时间加权。接受浮点数,默认为1.0。
  • doc_values:字段是否以列存储方式存储在磁盘上,以便以后用于排序、聚合或脚本。接受true(默认)或false。
  • eager_global_ordinals:是否在刷新时急切加载全局序数。接受true或false(默认)。在经常用于术语聚合的字段上启用此选项是个好主意。
  • fields:多字段允许相同的字符串值以不同方式进行多次索引,以用于不同目的,例如一个字段用于搜索,多字段用于排序和聚合。
  • ignore_above:不要索引任何长于此值的字符串。默认为2147483647,以便接受所有值。但请注意,默认的动态映射规则通过设置ignore_above: 256创建一个子关键字字段,以覆盖此默认值。
  • index:字段是否可搜索?接受true(默认)或false。
  • index_options:为了评分目的,应存储在索引中的信息是什么。默认为docs,但还可以设置为freqs,以在计算分数时考虑词项频率。
  • meta:关于字段的元数据。
  • norms:在评分查询时是否应考虑字段长度。接受true或false(默认)。
  • null_value:接受字符串值,用于替代任何显式的null值。默认为null,这意味着将字段视为缺失。请注意,如果使用了脚本值,则无法设置此值。
  • on_script_error:在索引时由脚本参数定义的脚本引发错误时,应采取的措施。接受fail(默认值),它将导致整个文档被拒绝,以及continue,它将在文档的_ignored元数据字段中注册该字段并继续索引。只有在设置了脚本字段时才能设置此参数。
  • script:如果设置了此参数,字段将索引由此脚本生成的值,而不是直接从源中读取值。如果在输入文档上设置了此字段的值,那么该文档将被拒绝并显示错误。脚本采用与运行时等效项相同的格式。脚本生成的值将按通常方式进行规范化,并且如果它们的长度超过了ignore_above上设置的值,将被忽略。
  • store:字段值是否应单独存储和检索,而不与_source字段分开。接受true或false(默认)。
  • similarity:应使用哪种评分算法或相似度。默认为BM25。
  • normalizer:在索引之前如何预处理关键字。默认为null,表示将保留关键字不变。
  • split_queries_on_whitespace:在构建针对此字段的查询时,全文查询是否应在空格上拆分输入。接受true或false(默认)。
  • time_series_dimension:[预览]此功能处于技术预览状态,可能会在将来的版本中更改或删除。Elastic将尽最大努力解决任何问题,但技术预览中的功能不适用于正式发布功能的支持SLA。

常量关键字字段类型

常量关键字是用于索引中的所有文档都具有相同值的关键字字段的专用类型。它支持与关键字字段相同的查询和聚合,但利用了所有文档在索引中都具有相同值的事实来更有效地执行查询。

您可以提交既不包含该字段值也不具有与映射中配置的值相同的值的文档。以下两个索引请求是等效的:

POST logs-debug/_doc


{
  "date": "2019-12-12",
  "message": "Starting up Elasticsearch",
  "level": "debug"
}
POST logs-debug/_doc
{
  "date": "2019-12-12",
  "message": "Starting up Elasticsearch"
}

然而,提供与映射中配置的值不同的值是不允许的。如果映射中未提供值,则字段将根据第一个索引的文档中包含的值自动配置自身。尽管此行为可能很方便,但请注意,这意味着如果有一个错误的值,则单个有毒文档可能导致拒绝所有其他文档。

在提供值之前(无论是通过映射还是从文档中),字段上的查询将不匹配任何文档,包括exists查询。一旦设置了该值,字段的值将无法更改。

常量关键字字段的参数

常量关键字字段接受以下映射参数:

  • meta:有关字段的元数据。
  • value:要与索引中的所有文档关联的值。如果未提供此参数,则将根据首个索引的文档设置值。

通配符字段类型

通配符字段类型是用于非结构化的机器生成内容的专用关键字字段,您计划使用类似grep的通配符和正则表达式查询进行搜索。通配符类型经过优化,适用于具有大值或高基数的字段。

如果您选择通配符字段类型,您可以根据字段值的基数和大小将该字段映射为关键字或通配符字段。如果符合以下条件之一,可以使用通配符类型:

  • 该字段包含超过一百万个唯一值。
    AND
  • 您计划经常使用具有前导通配符的模式进行字段搜索,例如foo或baz。
  • 该字段包含大于32KB的值。
    AND
  • 您计划经常使用任何通配符模式搜索该字段。

否则,使用关键字字段类型进行更快的搜索、更快的索引和更低的存储成本。如果以前使用文本字段索引非结构化的机器生成内容,您可以重新索引以更新映射为关键字或通配符字段。此外,我们建议您更新应用程序或工作流程,以替换字段上的基于单词的全文查询为等效的术语级别查询。

通配符字段内部索引整个字段值使用ngrams并存储完整字符串。索引用作粗略过滤器,用于减少检索和检查完整值的数量。此字段特别适用于在日志行上运行类似grep的查询。通常情况下,存储成本低于关键字字段,但在完全术语匹配上搜索速度较慢。如果字段值共享许多前缀,例如同一网站的URL,通配符字段的存储成本可能高于等效的关键字字段。

在通配符字段上执行索引和搜索的示例:

PUT my-index-000001
{
  "mappings": {
    "properties": {
      "my_wildcard": {
        "type": "wildcard"
      }
    }
  }
}
PUT my-index-000001/_doc/1
{
  "my_wildcard" : "This string can be quite lengthy"
}
GET my-index-000001/_search
{
  "query": {
    "wildcard": {
      "my_wildcard": {
        "value": "*quite*lengthy"
      }
    }
  }
}

通配符字段的参数

通配符字段接受以下参数:

  • null_value:接受字符串值,用于替代任何显式的null值。默认为null,这意味着将字段视为缺失。
  • ignore_above:不要索引任何长于此值的字符串。默认为2147483647,以便接受所有值。
  • 限制:通配符字段类似于关键字字段,因此不支持依赖于单词位置的查询,如短语查询。运行通配符查询时,任何重写参数都将被忽略。评分始终是常数分数。

嵌套字段类型 Nested field type

嵌套字段类型是对象数据类型的专用版本,允许数组中的对象以一种可以独立查询的方式进行索引。

当在索引中包含大型、具有大量任意键的键值对时,您可以考虑将每个键值对建模为具有键和值字段的嵌套文档。然而,可以考虑使用平铺数据类型,它将整个对象映射为单个字段,并允许对其内容进行简单搜索。嵌套文档和查询通常开销较大,因此对于这种用例使用平铺数据类型是更好的选择。

如何展平对象数组

Elasticsearch没有内部对象的概念。因此,它将对象层次结构展平为字段名称和值的简单字段列表。例如,考虑以下文档:

PUT my-index-000001/_doc/1
{
  "group" : "fans",
  "user" : [ 
    {
      "first" : "John",
      "last" :  "Smith"
    },
    {
      "first" : "Alice",
      "last" :  "White"
    }
  ]
}

"user"字段被动态添加为对象类型字段。前面的文档在内部将被转换为一个更像这样的文档:

{
  "group" :        "fans",
  "user.first" : [ "alice", "john" ],
  "user.last" :  [ "smith", "white" ]
}

"user.first"和"user.last"字段被展平为多值字段,并且丢失了"alice"和"white"之间的关联。这个文档将错误地匹配包含"alice"和"smith"的查询:

GET my-index-000001/_search
{
  "query": {
    "bool": {
      "must": [
        { "match": { "user.first": "Alice" }},
        { "match": { "user.last":  "Smith" }}
      ]
    }
  }
}

使用嵌套字段处理对象数组

如果需要索引对象数组并保持数组中每个对象的独立性,请使用嵌套数据类型,而不是对象数据类型。

在内部,嵌套对象将数组中的每个对象作为单独的隐藏文档进行索引,这意味着可以使用嵌套查询独立查询每个嵌套对象:

PUT my-index-000001
{
  "mappings": {
    "properties": {
      "user": {
        "type": "nested" 
      }
    }
  }
}

PUT my-index-000001/_doc/1
{
  "group" : "fans",
  "user" : [
    {
      "first" : "John",
      "last" :  "Smith"
    },
    {
      "first" : "Alice",
      "last" :  "White"
    }
  ]
}

GET my-index-000001/_search
{
  "query": {
    "nested": {
      "path": "user",
      "query": {
        "bool": {
          "must": [
            { "match": { "user.first": "Alice" }},
            { "match": { "user.last":  "Smith" }} 
          ]
        }
      }
    }
  }
}

嵌套字段的参数

嵌套字段接受以下参数:

  • dynamic(可选,字符串):是否应动态添加新属性到现有的嵌套对象。接受true(默认)、false和strict。
  • properties(可选,对象):嵌套对象内部的字段,可以是任何数据类型,包括嵌套。可以向现有的嵌套对象添加新属性。
  • include_in_parent(可选,布尔值):如果为true,则嵌套对象中的所有字段也会作为标准(平铺)字段添加到父文档中。默认为false。
  • include_in_root(可选,布尔值):如果为true,则嵌套对象中的所有字段也会作为标准(平铺)字段添加到根文档中。默认为false。

嵌套映射和对象的限制

如前所述,每个嵌套对象都作为单独的Lucene文档进行索引。继续以前的示例,如果我们索引了包含100个用户对象的单个文档,那么将创建101个Lucene文档:一个用于父文档,一个用于每个嵌套对象。由于嵌套映射开销较大,Elasticsearch采取了措施来防止性能问题:

  • index.mapping.nested_fields.limit:索引中唯一嵌套映射的最大数量。嵌套类型应仅在特殊情况下使用,当需要查询独立的对象数组时。为了防止不良设计的映射,此设置限制了每个索引中唯一嵌套类型的数量。默认值为50。

  • index.mapping.nested_objects.limit:单个文档中跨所有嵌套类型包含的最大嵌套JSON对象数。此限制有助于防止在文档包含太多嵌套对象时出现内存不足错误。默认值为10000。

例如,如果在前面的示例映射中添加了另一个名为"comments"的嵌套类型,那么每个文档的用户和评论对象的组合数量必须在限制之下。

数值字段类型 Numeric

Elasticsearch支持以下数值类型:

  • long:带符号的64位整数,最小值为-263,最大值为263-1。
  • integer:带符号的32位整数,最小值为-231,最大值为231-1。
  • short:带符号的16位整数,最小值为-32,768,最大值为32,767。
  • byte:带符号的8位整数,最小值为-128,最大值为127。
  • double:双精度64位IEEE 754浮点数,限制为有限值。
  • float:单精度32位IEEE 754浮点数,限制为有限值。
  • half_float:半精度16位IEEE 754浮点数,限制为有限值。
  • scaled_float:由长整数支持的浮点数,乘以一个固定的双精度缩放因子。
  • unsigned_long:无符号的64位整数,最小值为0,最大值为264-1。

以下是配置具有数值字段的映射的示例:

PUT my-index-000001
{
  "mappings": {
    "properties": {
      "number_of_bytes": {
        "type": "integer"
      },
      "time_in_seconds": {
        "type": "float"
      },
      "price": {
        "type": "scaled_float",
        "scaling_factor": 100
      }
    }
  }
}

doublefloathalf_float类型认为-0.0和+0.0是不同的值。因此,在-0.0上执行term查询不会匹配+0.0,反之亦然。同样,对于范围查询也是如此:如果上限是-0.0,那么+0.0不会匹配,如果下限是+0.0,那么-0.0不会匹配。

应该使用哪种类型?

对于整数类型(byte、short、integer和long),应选择足够满足用例的最小类型。这将有助于更高效地进行索引和搜索。但请注意,存储是根据实际存储的值进行优化的,因此选择一个类型而不是另一个不会影响存储要求。

对于浮点数类型,通常更有效的方法是使用缩放因子将浮点数据存储为整数,这正是scaled_float类型在底层执行的操作。例如,价格字段可以存储在具有缩放因子100的scaled_float中。所有API都将按照字段存储为双精度浮点数进行操作,但在底层,Elasticsearch将使用以分为单位的数值,即price*100,这是一个整数。这在大多数情况下有助于节省磁盘空间,因为整数比浮点数更容易压缩。scaled_float也可用于在精度和磁盘空间之间进行权衡。例如,假设您正在跟踪CPU利用率,其值在0到1之间。通常,CPU利用率是12.7%或13%之间没有太大差异,因此可以使用缩放因子为100的scaled_float,以将CPU利用率四舍五入到最接近的百分比,以节省空间。

如果scaled_float不合适,那么在浮点数类型(double、float和half_float)中,应选择满足用例的最小类型。以下是一个比较这些类型以帮助做出决策的表格:

类型最小值最大值有效位数/位数
double2-1074(2-2-52)·2102353 / 15.95
float2-149(2-2-23)·212724 / 7.22
half_float2-246550411 / 3.31

映射数值标识符

并非所有数值数据都应该映射为数值字段数据类型。Elasticsearch会优化数值字段,例如整数或长整数,以进行范围查询。但是,关键字字段更适用于术语和其他术语级查询。

例如,ISBN或产品ID等标识符很少用于范围查询。但通常会使用术语级查询来检索它们。

如果您不打算使用范围查询来搜索标识符数据,同时需要快速检索,则可以将数值标识符映射为关键字字段。关键字字段上的术语查询通常比数值字段上的术语查询更快。

如果您不确定使用哪种类型,可以使用多字段来将数据同时映射为关键字和数值数据类型。

数值字段的参数

数值类型字段接受以下参数:

  • boost:映射字段级别的查询时间加权。接受浮点数,默认值为1.0。
  • coerce

尝试将字符串转换为数字,并对整数截断小数部分。接受true(默认)和false。不适用于unsigned_long。请注意,如果使用script参数,则不能设置此参数。

  • doc_values:字段是否以列分隔方式存储在磁盘上,以便以后用于排序、聚合或脚本?接受true(默认)或false。
  • ignore_malformed:如果为true,则会忽略格式不正确的数字。如果为false(默认),则格式不正确的数字会引发异常,并拒绝整个文档。请注意,如果使用script参数,则不能设置此参数。
  • index:字段是否可搜索?接受true(默认)或false。
  • meta:关于字段的元数据。
  • null_value:接受与字段类型相同的数值值,用于替代任何显式的null值。默认为null,表示字段视为丢失。请注意,如果使用script参数,则不能设置此参数。
  • on_script_error:定义了在索引时由script参数定义的脚本引发错误时要执行的操作。接受fail(默认,将导致整个文档被拒绝)和continue(将在文档的_ignored元数据字段中注册字段并继续索引)。只有在设置了script字段时才能设置此参数。
  • script:如果设置了此参数,字段将索引由此脚本生成的值,而不是直接从源中读取值。如果在输入文档中为此字段设置了值,则将拒绝文档并显示错误。脚本的格式与其运行时等效项相同。只能在长整数和双精度字段类型上配置脚本。
  • store:字段值是否应与_source字段分开存储和检索?接受true或false(默认)。
  • time_series_dimension:(预览功能)仅供Elastic内部使用。
  • time_series_metric:(预览功能)仅供Elastic内部使用。

scaled_float参数

scaled_float接受一个额外参数:

  • scaling_factor:在编码值时要使用的缩放因子。值将在索引时乘以此因子,并四舍五入到最接近的长整数值。例如,具有缩放因子10的scaled_float将在内部以23的形式存储2.34,而所有搜索时操作(查询、聚合、排序)都将按照文档具有值2.3的方式运行。较高的缩放因子值会提高精度,但也会增加空间要求。此参数是必需的。

对象字段类型 objecdt

JSON文档具有层次结构:文档可能包含内部对象,而这些内部对象本身也可以包含内部对象:

PUT my-index-000001/_doc/1
{ 
  "region": "US",
  "manager": { 
    "age":     30,
    "name": { 
      "first": "John",
      "last":  "Smith"
    }
  }
}

外部文档本身也是一个JSON对象。它包含一个名为manager的内部对象,而manager内部又包含一个名为name的内部对象。

在内部,此文档被索引为一组简单的键值对,类似于以下结构:

{
  "region":             "US",
  "manager.age":        30,
  "manager.name.first": "John",
  "manager.name.last":  "Smith"
}

上述文档的显式映射可能如下所示:

PUT my-index-000001
{
  "mappings": {
    "properties": { 
      "region": {
        "type": "keyword"
      },
      "manager": { 
        "properties": {
          "age":  { "type": "integer" },
          "name": { 
            "properties": {
              "first": { "type": "text" },
              "last":  { "type": "text" }
            }
          }
        }
      }
    }
  }
}

上述映射示例中:

  • properties位于顶级映射定义中。
  • manager字段是一个内部对象字段。
  • manager.name字段是manager字段内部的内部对象字段。

您不必显式将字段类型设置为object,因为这是默认值。

对象字段的参数

对象字段接受以下参数:

  • dynamic:是否应动态添加新属性到现有对象中。接受true(默认)、runtime、false和strict。
  • enabled:对象字段的JSON值是否应被解析和索引(true,默认),或者完全忽略(false)。
  • properties:对象内的字段,可以是任何数据类型,包括对象。可以向现有对象添加新属性。

如果您需要索引对象数组而不是单个对象,请首先阅读关于嵌套字段的部分。

Percolator字段类型

Percolator字段类型是Elasticsearch的一种字段类型,它将JSON结构解析为本机查询并存储该查询,以便于后续的Percolate查询可以使用它来匹配提供的文档。

任何包含JSON对象的字段都可以配置为Percolator字段。Percolator字段类型没有特定的设置。只需配置Percolator字段类型就足以告诉Elasticsearch将某个字段视为查询。

如果按照以下映射配置Percolator字段类型:

PUT my-index-000001
{
  "mappings": {
    "properties": {
      "query": {
        "type": "percolator"
      },
      "field": {
        "type": "text"
      }
    }
  }
}

然后,您可以索引一个查询:

PUT my-index-000001/_doc/match_value
{
  "query": {
    "match": {
      "field": "value"
    }
  }
}

在Percolator查询中引用的字段必须已经存在于与Percolation相关的索引的映射中。为了确保这些字段存在,可以通过"create index"或"update mapping" API来添加或更新映射。

重新索引Percolator查询有时是必需的,以便从新版本中改进的Percolator字段类型中受益。

可以使用reindex API来重新索引Percolator查询。让我们看看以下具有Percolator字段类型的索引:

PUT index
{
  "mappings": {
    "properties": {
      "query" : {
        "type" : "percolator"
      },
      "body" : {
        "type": "text"
      }
    }
  }
}

POST _aliases
{
  "actions": [
    {
      "add": {
        "index": "index",
        "alias": "queries" 
      }
    }
  ]
}

PUT queries/_doc/1?refresh
{
  "query" : {
    "match" : {
      "body" : "quick brown fox"
    }
  }
}

建议为索引定义一个别名,这样在重新索引时,系统/应用程序无需更改,即可知道Percolator查询现在位于不同的索引中。

假设您要升级到新的主要版本,并且为了让新的Elasticsearch版本仍能读取您的查询,您需要将查询重新索引到当前Elasticsearch版本的新索引中:

PUT new_index
{
  "mappings": {
    "properties": {
      "query" : {
        "type" : "percolator"
      },
      "body" : {
        "type": "text"
      }
    }
  }
}

POST /_reindex?refresh
{
  "source": {
    "index": "index"
  },
  "dest": {
    "index": "new_index"
  }
}

POST _aliases
{
  "actions": [ 
    {
      "remove": {
        "index" : "index",
        "alias": "queries"
      }
    },
    {
      "add": {
        "index": "new_index",
        "alias": "queries"
      }
    }
  ]
}

如果存在别名,请不要忘记将其指向新的索引。

通过别名"queries"执行Percolate查询:

GET /queries/_search
{
  "query": {
    "percolate" : {
      "field" : "query",
      "document" : {
        "body" : "fox jumps over the lazy dog"
      }
    }
  }
}

现在,匹配将从新索引中返回。

优化查询时间文本分析:

当Percolator验证Percolator候选匹配时,它将解析、执行查询时间文本分析,并实际运行Percolator查询以匹配正在Percolating的文档。这将为每个候选匹配执行,每次执行Percolate查询时都会发生。如果您的查询时间文本分析是查询解析的相对昂贵部分,那么文本分析可能成为Percolating时花费时间的支配因素。当Percolator最终验证了许多Percolator查询匹配时,这种查询解析开销可能变得明显。

为了避免在Percolate时间最昂贵的文本分析部分,可以选择在索引Percolator查询时执行昂贵的文本分析部分。这需要使用两种不同的分析器。第一个分析器实际上执行需要执行的文本分析(昂贵部分)。第二个分析器(通常是whitespace)只是拆分第一个分析器生成的标记。然后,在索引Percolator查询之前,应使用分析API使用较昂贵的分析器来分析查询文本。分析API的结果,即标记,应替换Percolator查询中的原始查询文本。重要的是,查询现在应该配置为覆盖映射中的分析器,并仅使用第二个分析器。大多数基于文本的查询支持分析器选项(如match、query_string、simple_query_string)。使用这种方法,昂贵的文本分析只会执行一次,而不是多次。

让我们通过一个简化的示例演示这个工作流程。

假设我们要索引以下Percolator查询:

{
  "query" : {
    "match" : {
      "body" : {
        "query" : "missing bicycles"
      }
    }
  }
}

以及以下的设置和映射:

PUT /test_index
{
  "settings": {
    "analysis": {
      "analyzer": {
        "my_analyzer" : {
          "tokenizer": "standard",
          "filter" : ["lowercase", "porter_stem"]
        }
      }
    },
    "mappings": {
      "properties": {
        "query" : {
          "type": "percolator"
        },
        "body" : {
          "type": "text",
          "analyzer": "my_analyzer" 
        }
      }
    }
  }
}

出于本示例的目的,这个分析器被认为是昂贵的。

首先,我们需要使用分析API在索引之前执行文本分析:

POST /test_index/_analyze
{
  "analyzer" : "my_analyzer",
  "text" : "missing bicycles"
}

这将产生以下响应:

{
  "tokens": [
    {
      "token": "miss",
      "start_offset": 0,
      "end_offset": 7,
      "type": "<ALPHANUM>",
      "position": 0
    },
    {
      "token": "bicycl",
      "start_offset": 8,
      "end_offset": 16,
      "type": "<ALPHANUM>",
      "position": 1
    }
  ]
}

返回的标记按照生成的顺序需要替换Percolator查询中的查询文本:

PUT /test_index/_doc/1?refresh
{
  "query" : {
    "match" : {
      "body" : {
        "query" : "miss bicycl",
        "analyzer" : "whitespace" 
      }
    }
  }
}

重要的是选择whitespace分析器,否则将使用映射中定义的分析器,这将破坏使用这个工作流的目的。请注意,whitespace是内置分析器,如果需要使用不同的分析器,必须首先在索引的设置中进行配置。

在Percolate时间,什么都不会改变,Percolate查询可以正常定义:

GET /test_index/_search
{
  "query": {
    "percolate" : {
      "field" : "query",
      "document" : {
        "body" : "Bycicles are missing"
      }
    }
  }
}

这将产生类似以下的响应:

{
  "took": 6,
  "timed_out": false,
  "_shards": {
    "total": 1,
    "successful": 1,
    "skipped" : 0,
    "failed": 0
  },
  "hits": {
    "total" : {
        "value": 1,
        "relation": "eq"
    },
    "max_score": 0.13076457,
    "hits": [
      {
        "_index": "test_index",
        "_type": "_doc",
        "_id": "1",
        "_score": 0.13076457,
        "_source": {
          "query": {
            "match": {
              "body": {
                "query": "miss bicycl",
                "analyzer": "whitespace"
              }
            }
          }
        },
        "fields" : {
          "_percolator_document_slot" : [0]
        }
      }
    ]
  }
}

这个Percolate查询现在来自新索引。

优化通配符查询:

通配符查询比其他查询更昂贵,特别是如果通配符表达式很大的话。

对于具有前缀通配符表达式或仅前缀查询的通配符查询,可以使用edge_ngram标记过滤器将这些查询替换为针对配置了edge_ngram标记过滤器的字段的常规词项查询。

创建具有自定义分析设置的索引:

PUT my_queries1
{
  "settings": {
    "analysis": {
      "analyzer": {
        "wildcard_prefix": { 
          "type": "custom",
          "tokenizer": "standard",
          "filter" : [
            "lowercase",
            "wildcard_edge_ngram"
          ]
        }
      },
      "filter": {
        "wildcard_edge_ngram": { 
          "type": "edge_ngram",
          "min_gram": 1,
          "max_gram": 32
        }
      }
    }
  },
  "mappings": {
    "properties": {
      "query" : {
        "type" : "percolator"
      },
      "my_field": {
        "type": "text",
        "fields": {
          "prefix": { 
            "type": "text",
            "analyzer": "wildcard_prefix",
            "search_analyzer": "standard"
          }
        }
      }
    }
  }
}

分析器在索引时间只需要生成前缀标记。

根据前缀搜索需求,增加min_gram并减小max_gram设置。

此多字段应用于使用term或match查询而不是prefix或通配符查询进行前缀搜索。

然后,不是索引以下查询:

{
  "query": {
    "wildcard": {
      "my_field": "abc*"
    }
}

应该索引以下查询:

PUT /my_queries1/_doc/1?refresh
{
  "query" : {
    "term": {
      "my_field.prefix": "abc"
    }
  }
}

这种方式可以更有效地处理第二个查询,而不是第一个查询。

以下搜索请求将与先前索引的Percolator查询匹配:

GET /my_queries1/_search
{
  "query": {
    "percolate": {
      "field": "query",
      "document": {
        "my_field": "abcd"
      }
    }
  }
}

同样,这是一种优化前缀通配符搜索的技术。通过使用reverse标记过滤器和edge_ngram标记过滤器来处理后缀通配符搜索,可以采用相同的技术。这使得后缀通配符搜索更加高效。对于后缀通配符搜索,需要在索引之前配置reverse标记过滤器。

PUT my_queries2
{
  "settings": {
    "analysis": {
      "analyzer": {
        "wildcard_suffix": {
          "type": "custom",
          "tokenizer": "standard",
          "filter": [
            "lowercase",
            "reverse",
            "wildcard_edge_ngram"
          ]
        },
        "wildcard_suffix_search_time": {
          "type": "custom",
          "tokenizer": "standard",
          "filter": [
            "lowercase",
            "reverse"
          ]
        }
      },
      "filter": {
        "wildcard_edge_ngram": {
          "type": "edge_ngram",
          "min_gram": 1,
          "max_gram": 32
        }
      }
    }
  },
  "mappings": {
    "properties": {
      "query" : {
        "type" : "percolator"
      },
      "my_field": {
        "type

": "text",
        "fields": {
          "suffix": {
            "type": "text",
            "analyzer": "wildcard_suffix",
            "search_analyzer": "wildcard_suffix_search_time" 
          }
        }
      }
    }
  }
}

搜索时需要使用自定义分析器,因为否则查询项不会被反转,否则将不会与反转后缀标记匹配。

然后,不是索引以下查询:

{
  "query": {
    "wildcard": {
      "my_field": "*xyz"
    }
}

应该索引以下查询:

PUT /my_queries2/_doc/2?refresh
{
  "query": {
    "match": { 
      "my_field.suffix": "xyz"
    }
  }
}

应该使用match查询而不是term查询,因为文本分析需要反转查询项。

以下搜索请求将与先前索引的Percolator查询匹配:

GET /my_queries2/_search
{
  "query": {
    "percolate": {
      "field": "query",
      "document": {
        "my_field": "wxyz"
      }
    }
  }
}

专用的Percolator索引:

Percolate查询可以添加到任何索引中。与将Percolate查询添加到数据所在的索引不同,这些查询也可以添加到专用索引中。这样做的好处是,这个专用的Percolator索引可以有自己的索引设置(例如主分片和副本分片的数量)。如果选择使用专用的Percolate索引,需要确保普通索引上的映射也可用于Percolate索引。否则,Percolate查询可能会被错误解析。

强制将未映射的字段处理为字符串:

在某些情况下,不清楚要注册哪种Percolator查询,如果没有字段映射的字段在Percolator查询中引用,那么添加Percolator查询将失败。这意味着需要更新映射以具有具有适当设置的字段,然后才能添加Percolator查询。但有时,只需将所有未映射的字段处理为默认的文本字段就足够了。在这种情况下,可以将index.percolator.map_unmapped_fields_as_text设置为true(默认为false),然后如果Percolator查询中引用的字段不存在,它将被处理为默认的文本字段,以便添加Percolator查询不会失败。

限制:

父/子关系:由于Percolate查询一次处理一个文档,因此不支持针对子文档运行的查询和过滤器,例如has_child和has_parent。

获取查询:有一些查询通过查询解析期间的get调用来获取数据。例如,当使用terms查找时,模板查询使用索引脚本,geo_shape在使用预先索引的形状时。当这些查询由Percolator字段类型索引时,get调用将执行一次。因此,每次Percolator查询评估这些查询时,都将使用它们在索引时的获取的术语、形状等数据。需要注意的是,这些查询的获取术语等操作发生在每次在主分片和副本分片上进行索引时,因此实际索引的术语可能在不同的分片副本之间不同,如果源索引在索引时发生更改。

脚本查询:脚本查询中的脚本只能访问文档值字段。Percolator查询将提供的文档索引到内存中的索引中。这个内存中的索引不支持存储字段,因此不会存储_source字段和其他存储字段。这是脚本查询中无法使用_source和其他存储字段的原因。

字段别名:包含字段别名的Percolator查询可能不会始终按预期行为。特别是,如果注册了一个包含字段别名的Percolator查询,然后在映

射中更改字段别名的目标,Percolator查询将无法找到它要查找的字段。

point数据类型

point数据类型用于索引和搜索二维平面坐标系中的任意x、y对。您可以使用shape Query来查询此类型的文档。

下面演示了指定点的四种方式:

  1. 将点表示为对象:
PUT my-index-000001
{
  "mappings": {
    "properties": {
      "location": {
        "type": "point"
      }
    }
  }
}

PUT my-index-000001/_doc/1
{
  "text": "Point as an object",
  "location": { 
    "x": 41.12,
    "y": -71.34
  }
}
  1. 将点表示为字符串:
PUT my-index-000001/_doc/2
{
  "text": "Point as a string",
  "location": "41.12,-71.34" 
}
  1. 将点表示为数组:
PUT my-index-000001/_doc/4
{
  "text": "Point as an array",
  "location": [41.12, -71.34] 
}
  1. 将点表示为Well-Known Text (WKT) POINT原语:
PUT my-index-000001/_doc/5
{
  "text": "Point as a WKT POINT primitive",
  "location" : "POINT (41.12 -71.34)" 
}

这些方式分别表示了点,可以使用不同的数据结构来表示点坐标。

  • 以对象方式表示,使用 “x” 和 “y” 键。
  • 以字符串方式表示,使用格式 “x,y”。
  • 以数组方式表示,使用格式 [x, y]。
  • 以Well-Known Text (WKT) POINT原语方式表示,使用格式 “POINT(x y)”。

索引器提供的坐标是单精度浮点数值,因此字段保证与Java虚拟机提供的相同的精度(通常为1E-38)。

point字段的参数如下:

  • ignore_malformed:如果设置为true,则会忽略格式错误的点。如果设置为false(默认值),格式错误的点会引发异常并拒绝整个文档。

  • ignore_z_value:如果设置为true(默认值),将接受三维点(存储在_source字段中),但只会索引x和y值;第三维度将被忽略。如果设置为false,包含超过x和y(二维)值的点将引发异常并拒绝整个文档。

  • null_value:接受一个点值,用于替代任何显式的null值。默认为null,这意味着该字段将被视为缺失。

排序和检索点:

目前无法直接对点进行排序或检索其字段。点值仅通过_source字段检索。
range字段类型表示介于上限和下限之间的连续数值范围。例如,一个范围可以表示十月份的任何日期或从0到9的任何整数。它们使用下限(gtgte)和上限(ltlte)运算符进行定义。可以用于查询,并对聚合提供有限支持。唯一支持的聚合是直方图(histogram)和基数(cardinality)。

范围字段类型 range

支持以下range类型:

  • integer_range:带有最小值-231和最大值231-1的有符号32位整数的范围。

  • float_range:带有单精度32位IEEE 754浮点数值的范围。

  • long_range:带有最小值-263和最大值263-1的有符号64位整数的范围。

  • double_range:带有双精度64位IEEE 754浮点数值的范围。

  • date_range:日期值的范围。日期范围支持通过format映射参数的各种日期格式。无论使用哪种格式,日期值都会被解析为自Unix纪元以来的UTC毫秒数的无符号64位整数。不支持包含now日期数学表达式的值。

  • ip_range:支持IPv4或IPv6(或混合)地址的IP值范围。

以下是配置具有不同range字段的映射的示例,以及索引多种range类型的示例:

PUT range_index
{
  "settings": {
    "number_of_shards": 2
  },
  "mappings": {
    "properties": {
      "expected_attendees": {
        "type": "integer_range"
      },
      "time_frame": {
        "type": "date_range", 
        "format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis"
      }
    }
  }
}

PUT range_index/_doc/1?refresh
{
  "expected_attendees" : { 
    "gte" : 10,
    "lt" : 20
  },
  "time_frame" : {
    "gte" : "2015-10-31 12:00:00", 
    "lte" : "2015-11-01"
  }
}

date_range类型接受由日期类型定义的相同字段参数。

示例中,索引了一个具有10到20名与会者(不包括20名)的会议。

以下是对名为"expected_attendees"的integer_range字段的term查询示例。12是范围内的值,因此会匹配。

GET range_index/_search
{
  "query" : {
    "term" : {
      "expected_attendees" : {
        "value": 12
      }
    }
  }
}

上述查询的结果。

{
  "took": 13,
  "timed_out": false,
  "_shards" : {
    "total": 2,
    "successful": 2,
    "skipped" : 0,
    "failed": 0
  },
  "hits" : {
    "total" : {
        "value": 1,
        "relation": "eq"
    },
    "max_score" : 1.0,
    "hits" : [
      {
        "_index" : "range_index",
        "_type" : "_doc",
        "_id" : "1",
        "_score" : 1.0,
        "_source" : {
          "expected_attendees" : {
            "gte" : 10, "lt" : 20
          },
          "time_frame" : {
            "gte" : "2015-10-31 12:00:00", "lte" : "2015-11-01"
          }
        }
      }
    ]
  }
}

下面是针对名为"time_frame"的date_range字段的date_range查询示例。

GET range_index/_search
{
  "query" : {
    "range" : {
      "time_frame" : { 
        "gte" : "2015-10-31",
        "lte" : "2015-11-01",
        "relation" : "within" 
      }
    }
  }
}

range查询的工作方式与上面描述的range查询相同。

range字段的range查询支持relation参数,可以是WITHIN、CONTAINS、INTERSECTS(默认值)之一。

这个查询会产生类似的结果。

{
  "took": 13,
  "timed_out": false,
  "_shards" : {
    "total": 2,
    "successful": 2,
    "skipped" : 0,
    "failed": 0
  },
  "hits" : {
    "total" : {
        "value": 1,
        "relation": "eq"
    },
    "max_score" : 1.0,
    "hits" : [
      {
        "_index" : "range_index",
        "_type" : "_doc",
        "_id" : "1",
        "_score" : 1.0,
        "_source" : {
          "expected_attendees" : {
            "gte" : 10, "lt" : 20
          },
          "time_frame" : {
            "gte" : "2015-10-31 12:00:00", "lte" : "2015-11-01"
          }
        }
      }
    ]
  }
}

IP范围:
除了上面的范围格式,IP范围可以使用CIDR表示法:
PUT range_index/_mapping
{
“properties”: {
“ip_allowlist”: {
“type”: “ip_range”
}
}
}

PUT range_index/_doc/2
{
“ip_allowlist” : “192.168.0.0/16”
}
range字段的参数如下:
coerce:尝试将字符串转换为数字并截断整数的小数部分。接受true(默认值)和false。
boost:映射字段级别的查询时间加权。接受浮点数,默认为1.0。
index:字段是否可搜索?接受true(默认值)和false。
store:字段值是否应存储在_source字段之外,以便检索?接受true或false(默认值)。

特征排名字段类型 rank_feature

rank_feature字段类型用于索引数字,以便在后续的查询中使用rank_feature查询来提升文档的相关性。

下面是一个示例,首先创建一个名为my-index-000001的索引,其中包含两个rank_feature字段,然后向该索引中添加一个文档,并执行一个rank_feature查询:

PUT my-index-000001
{
  "mappings": {
    "properties": {
      "pagerank": {
        "type": "rank_feature" 
      },
      "url_length": {
        "type": "rank_feature",
        "positive_score_impact": false 
      }
    }
  }
}

PUT my-index-000001/_doc/1
{
  "pagerank": 8,
  "url_length": 22
}

GET my-index-000001/_search
{
  "query": {
    "rank_feature": {
      "field": "pagerank"
    }
  }
}

上述示例中,首先创建了my-index-000001索引,其中包含两个rank_feature字段:pagerankurl_lengthpagerank字段的默认行为是正相关,而url_length字段设置为负相关。

然后,向该索引中添加了一个文档,该文档包含了这两个字段的值。

最后,执行了一个rank_feature查询,以使用pagerank字段来影响文档的相关性得分。

需要注意的是:

  • rank_feature字段类型仅支持单值字段和严格正值。多值字段和负值将被拒绝。
  • rank_feature字段不支持查询、排序或聚合。它们只能在rank_feature查询中使用。
  • rank_feature字段只保留了9个有效位数的精度,这对应着大约0.4%的相对误差。
  • 与得分负相关的rank_feature字段应将positive_score_impact设置为false(默认为true)。这将由rank_feature查询来使用,以修改评分公式,使得分随特征值的增加而减少。

排名特征字段类型 rank_features

"rank_features"字段是一种用于索引数值特征向量的字段,以便在后续的查询中使用"rank_feature"查询来增强文档的排名。

它类似于"rank_feature"数据类型,但更适用于特征列表稀疏的情况,以便不必为每个特征添加一个字段到映射中。

以下是一个示例:

PUT my-index-000001
{
  "mappings": {
    "properties": {
      "topics": {
        "type": "rank_features" 
      },
      "negative_reviews" : {
        "type": "rank_features",
        "positive_score_impact": false 
      }
    }
  }
}

PUT my-index-000001/_doc/1
{
  "topics": { 
    "politics": 20,
    "economics": 50.8
  },
  "negative_reviews": {
    "1star": 10,
    "2star": 100
  }
}

PUT my-index-000001/_doc/2
{
  "topics": {
    "politics": 5.2,
    "sports": 80.1
  },
  "negative_reviews": {
    "1star": 1,
    "2star": 10
  }
}

上述示例中,我们创建了一个名为"my-index-000001"的索引,其中包含两个"rank_features"字段,一个名为"topics",另一个名为"negative_reviews"。"negative_reviews"字段还设置了"positive_score_impact"为false,表示与分数负相关的特征。

"rank_features"字段必须使用"rank_features"字段类型。它们只支持单值特征和严格正数值。多值字段和零值或负值将被拒绝。

"rank_features"字段不支持排序或聚合,只能使用"rank_feature"查询进行查询。

"rank_features"字段只保留9个有效位数的精度,这对应大约0.4%的相对误差。

与分数负相关的"rank_features"应将"positive_score_impact"设置为false(默认为true)。这将由"rank_feature"查询用于修改评分公式,使得分数随着特征值的增加而降低,而不是增加。

键入时搜索字段类型 search_as_you_type

"search_as_you_type"字段类型是一种文本字段,经过优化,提供了对满足"as-you-type"自动完成用例的查询的开箱即用支持。它创建一系列子字段,这些子字段经过分析以索引可以有效匹配部分匹配整个索引文本值的查询的术语。支持前缀完成(即匹配从输入的开始处开始的术语)和中缀完成(即匹配输入中的任何位置的术语)。

当将此类型的字段添加到映射中时,可以使用以下示例:

PUT my-index-000001
{
  "mappings": {
    "properties": {
      "my_field": {
        "type": "search_as_you_type"
      }
    }
  }
}

这将创建以下字段:

  • my_field:根据映射配置进行分析。如果没有配置分析器,则使用索引的默认分析器。
  • my_field._2gram:使用大小为2的shingle令牌过滤器包装my_field的分析器。
  • my_field._3gram:使用大小为3的shingle令牌过滤器包装my_field的分析器。
  • my_field._index_prefix:使用边缘ngram令牌过滤器包装my_field._3gram的分析器。

shingle子字段的大小可以使用max_shingle_size映射参数进行配置,默认为3,此参数的有效值是2至4之间的整数值。将为每个shingle大小从2到max_shingle_size创建子字段。my_field._index_prefix子字段将始终使用具有max_shingle_size的shingle子字段的分析器来构建自己的分析器。

增加max_shingle_size将提高具有更多连续术语的查询的匹配性,但会增加索引大小。默认的max_shingle_size通常足够了。

通常,为服务搜索即输入时完成用例而查询的最有效方法是使用类型为bool_prefix的multi_match查询,该查询针对根search_as_you_type字段及其shingle子字段。这可以匹配查询术语的任何顺序,但如果文档包含shingle子字段中的顺序术语,则会为其打分更高。

GET my-index-000001/_search
{
  "query": {
    "multi_match": {
      "query": "brown f",
      "type": "bool_prefix",
      "fields": [
        "my_field",
        "my_field._2gram",
        "my_field._3gram"
      ]
    }
  }
}

要搜索严格按顺序匹配查询术语的文档,或者使用短语查询的其他属性,请使用根字段上的match_phrase_prefix查询。如果最后一个术语应该精确匹配而不是作为前缀匹配,则还可以使用match_phrase查询。使用短语查询可能比使用match_bool_prefix查询效率较低。

GET my-index-000001/_search
{
  "query": {
    "match_phrase_prefix": {
      "my_field": "brown f"
    }
  }
}

"search_as_you_type"字段类型的特定参数包括:

  • max_shingle_size(可选,整数):要创建的最大shingle大小。有效值为2(包括)到4(包括)。默认为3。
  • 为文本字段配置的analyzer、index、index_options、norms、store、search_analyzer、search_quote_analyzer、similarity和term_vector参数等与文本字段的配置类似。这些参数配置了根字段的子字段的行为。

优化前缀查询:

当对根字段或任何其子字段进行前缀查询时,查询将被重写为对._index_prefix子字段的项查询。这比通常对文本字段进行的前缀查询更有效,因为每个shingle的前缀长度都直接作为项在._index_prefix子字段中索引。

._index_prefix子字段的分析器稍微修改了shingle生成行为,以便还索引了通常不会作为shingle产生的术语末尾的前缀。例如,如果将值"quick brown

fox"索引到search_as_you_type字段,并设置max_shingle_size为3,则还将索引术语"brown fox"和"fox"的前缀到._index_prefix子字段,尽管它们不会出现在._3gram子字段中。这允许完成字段输入中的所有术语。

形状 shape

"shape"数据类型用于索引和搜索任意x、y平面形状,如矩形和多边形。它可用于索引和查询其坐标位于二维平面坐标系中的几何形状。

您可以使用"shape"查询来查询此类型的文档。

映射选项:
与geo_shape字段类型一样,shape字段映射将GeoJSON或Well-Known Text(WKT)几何对象映射到shape类型。要启用它,用户必须明确将字段映射到shape类型。以下是shape字段映射的选项:

  • orientation:可选地定义如何解释多边形/多多边形的顶点顺序。此参数定义了两个坐标系规则之一(右手或左手),每种规则可以以三种不同的方式指定。1. 右手规则:right、ccw、counterclockwise;2. 左手规则:left、cw、clockwise。默认方向(逆时针)符合OGC标准,该标准规定外部环的顶点按逆时针顺序排列,内部环(洞)的顶点按顺时针顺序排列。在geo_shape映射中设置此参数会明确设置geo_shape字段的坐标列表的顶点顺序,但可以在每个单独的GeoJSON或WKT文档中进行覆盖。
  • ignore_malformed:如果为true,则忽略格式错误的GeoJSON或WKT形状。如果为false(默认),格式错误的GeoJSON和WKT形状将引发异常并拒绝整个文档。
  • ignore_z_value:如果为true(默认),将接受三维点(存储在源中),但仅索引纬度和经度值;将忽略第三维。如果为false,包含除纬度和经度(两个维度)值之外的geopoints将引发异常并拒绝整个文档。
  • coerce:如果为true,将自动关闭多边形中的未闭合线环。

索引方法:
与geo_shape一样,shape字段类型通过将几何形状分解为三角网格,并将每个三角形索引为BKD树中的7维点来进行索引。索引器提供的坐标是单精度浮点值,因此该字段保证与Java虚拟机提供的相同精度(通常为1E-38)。对于多边形/多多边形,三角剖分器的性能主要取决于定义几何形状的顶点数量。

重要说明:
包含关系查询 - 支持将关系定义为包含的shape查询适用于使用ElasticSearch 7.5.0或更高版本创建的索引。

示例:
以下是将geometry字段映射为shape类型的映射定义示例:

PUT /example
{
  "mappings": {
    "properties": {
      "geometry": {
        "type": "shape"
      }
    }
  }
}

该映射将geometry字段映射为shape类型。索引器使用单精度浮点值表示顶点值,因此准确性保证与Java虚拟机提供的float值大致相同(通常为1E-38)。

输入结构:
几何形状可以使用GeoJSON或Well-Known Text(WKT)格式表示。以下表提供了GeoJSON和WKT到Elasticsearch类型的映射:

  • Point -> POINT -> point:单个x、y坐标。
  • LineString -> LINESTRING -> linestring:由两个或多个点给定的任意线。
  • Polygon -> POLYGON -> polygon:首尾点必须匹配的封闭多边形,因此创建一个n边多边形需要n + 1个顶点,至少4个顶点。
  • MultiPoint -> MULTIPOINT -> multipoint:一组未连接但可能相关的点。
  • MultiLineString -> MULTILINESTRING -> multilinestring:一组单独的线串。
  • MultiPolygon -> MULTIPOLYGON -> multipolygon:一组单独的多边形。
  • GeometryCollection -> GEOMETRYCOLLECTION -> geometrycollection:类似于multi*多边形的形状集合,但多种类型可以共存(例如,一个点和一个线串)。
  • N/A -> BBOX -> envelope:由指

定左上角和右下角点表示的边界矩形。

对于所有类型,内部类型和坐标字段都是必需的。

GeoJSON和WKT以及Elasticsearch中的正确坐标顺序是(X、Y)在坐标数组内。这与许多地理空间API(例如geo_shape)通常使用的俚语纬度、经度(Y、X)顺序不同。

以下是GeoJSON和WKT中点的示例:

POST /example/_doc
{
  "location": {
    "type": "point",
    "coordinates": [-377.03653, 389.897676]
  }
}
POST /example/_doc
{
  "location": "POINT (-377.03653 389.897676)"
}

以下是GeoJSON和WKT中的LineString示例:

POST /example/_doc
{
  "location": {
    "type": "linestring",
    "coordinates": [[-377.03653, 389.897676], [-377.009051, 389.889939]]
  }
}
POST /example/_doc
{
  "location": "LINESTRING (-377.03653 389.897676, -377.009051 389.889939)"
}

以下是GeoJSON和WKT中的Polygon示例:

POST /example/_doc
{
  "location": {
    "type": "polygon",
    "coordinates": [
      [ [1000.0, -1001.0], [1001.0, -1001.0], [1001.0, -1000.0], [1000.0, -1000.0], [1000.0, -1001.0] ]
    ]
  }
}
POST /example/_doc
{
  "location": "POLYGON ((1000.0 -1001.0, 1001.0 -1001.0, 1001.0 -1000.0, 1000.0 -1000.0, 1000.0 -1001.0))"
}

请注意,第一个数组表示多边形的外边界,其他数组表示内部形状(“洞”)。以下是带有洞的多边形的GeoJSON示例:

POST /example/_doc
{
  "location": {
    "type": "polygon",
    "coordinates": [
      [ [1000.0, -1001.0], [1001.0, -1001.0], [1001.0, -1000.0], [1000.0, -1000.0], [1000.0, -1001.0] ],
      [ [1000.2, -1001.2], [1000.8, -1001.2], [1000.8, -1001.8], [1000.2, -1001.8], [1000.2, -1001.2] ]
    ]
  }
}
POST /example/_doc
{
  "location": "POLYGON ((1000.0 1000.0, 1001.0 1000.0, 1001.0 1001.0, 1000.0 1001.0, 1000.0 1000.0), (1000.2 1000.2, 1000.8 1000.2, 1000.8 1000.8, 1000.2 1000.8, 1000.2 1000.2))"
}

重要说明:WKT不强制顶点的特定顺序。GeoJSON要求外多边形必须是逆时针的,内部形状必须是顺时针的,这与Open Geospatial Consortium(OGC)的Simple Feature Access规范的顶点顺序一致。

默认情况下,Elasticsearch期望顶点按逆时针(右手规则)顺序排列。如果数据按顺时针(左手规则)顺序提供,用户可以在字段映射中或作为文档的参数中更改orientation参数。以下是在文档中覆盖orientation参数的示例:

POST /example/_doc
{
  "location": {
    "type": "polygon",
    "orientation": "clockwise",
    "coordinates": [
      [

 [1000.0, 1000.0], [1000.0, 1001.0], [1001.0, 1001.0], [1001.0, 1000.0], [1000.0, 1000.0] ]
    ]
  }
}

MultiPoint、MultiLineString、MultiPolygon和Geometry Collection的示例也类似。

文本 Text

文本字段是一种用于索引全文本值的字段类型,比如电子邮件的正文或产品的描述。这些字段会进行分析,也就是在被索引之前会通过分析器将字符串转换为独立的词汇项列表。分析过程允许Elasticsearch在每个全文本字段中搜索单独的词汇项。文本字段不用于排序,很少用于聚合(尽管“significant text”聚合是一个显著的例外)。

文本字段最适用于非结构化但可读的内容。如果您需要索引非结构化的机器生成内容,请参见"Mapping unstructured content"。如果您需要索引结构化内容,如电子邮件地址、主机名、状态代码或标签,那么您更可能应该使用关键字字段。

以下是文本字段的映射示例:

PUT my-index-000001
{
  "mappings": {
    "properties": {
      "full_name": {
        "type": "text"
      }
    }
  }
}

有时,有必要在同一个字段上既拥有全文本(text)又拥有关键字(keyword)版本,一个用于全文本搜索,另一个用于聚合和排序。这可以通过多字段(multi-fields)来实现。

文本字段接受以下参数

  • analyzer:文本字段在索引时和搜索时应使用的分析器。默认为默认的索引分析器或标准分析器。
  • boost:映射字段级别的查询时间加权。接受浮点数,默认为1.0。
  • eager_global_ordinals:是否在刷新时急切加载全局序数?接受true或false(默认)。启用这个选项对那些经常用于(显著的)词项聚合的字段是一个好主意。
  • fielddata:字段是否可以使用内存中的fielddata进行排序、聚合或脚本处理?接受true或false(默认)。
  • fielddata_frequency_filter:专家设置,允许决定在启用fielddata时要加载哪些值到内存中。默认情况下,所有值都会被加载。
  • fields:多字段允许相同的字符串值以不同方式进行多次索引,以满足不同的目的,比如一个字段用于搜索,一个多字段用于排序和聚合,或者使用不同的分析器分析相同的字符串值。
  • index:字段是否可以被搜索?接受true(默认)或false。
  • index_options:为了搜索和高亮显示的目的,索引中应存储什么信息。默认为positions。
  • index_prefixes:如果启用,将长度在2到5个字符之间的词项前缀索引到一个单独的字段中。这允许前缀搜索更高效,但会增加索引的大小。
  • index_phrases:如果启用,将两个词项的词组(shingles)索引到一个单独的字段中。这允许精确短语查询(无偏移)更高效,但会增加索引的大小。
  • norms:是否在评分查询中考虑字段长度?接受true(默认)或false。
  • position_increment_gap:应在字符串数组的每个元素之间插入的虚假词位置数。默认值为分析器上配置的position_increment_gap,默认为100。100是之所以选择的数字,是因为它可以防止有相当大偏移(小于100)的短语查询匹配跨字段值的词项。
  • store:字段值是否应存储并能够单独从_source字段中检索。接受true或false(默认)。
  • search_analyzer:搜索时应使用的分析器。默认为分析器设置。
  • search_quote_analyzer:遇到短语时在搜索时应使用的分析器。默认为search_analyzer设置。
  • similarity:应使用的评分算法或相似性。默认为BM25。
  • term_vector:是否应为字段存储词项向量。默认为no。
  • meta:关于字段的元数据。

文本字段通常默认是可搜索的,但默认情况下不可用于聚合、排序或脚本。如果您尝试在文本字段上进行排序、聚合或从脚本中访问值,将看到如下异常信息:

“默认情况下,文本字段上已禁用fielddata。请在your_field_name上设置fielddata=true,以通过反向索引加载fielddata到内存中。需要注意的是,fielddata会使用大量内存。”

要在文本字段上启用fielddata,您可以使用更新映射API,如下所示:

PUT my-index-000001/_mapping
{
  "properties": {
    "my_field": {
      "type": "text",
      "fielddata": true
    }
  }
}

fielddata_frequency_filter映射参数可以用于减少加载到内存中的词项数量,从而降低内存使用。词项可以根据频率进行过滤。可以指定最小值和最大值,以表示绝对数值(当数值大于1.0时)或百分比(例如0.01表示1%而1.0表示100%)。频率是根据段(segment)计算的。百分比是基于具有字段值的文档数来计算的,而不是段中的所有文档数。

对小段(segment)可以使用min_segment_size完全排除,方法是指定段应包含的最小文档数。

"fielddata"通常不适用于文本字段。fielddata存储在堆中,因为它的计算成本很高。计算fielddata可能会导致延迟峰值,并且增加堆使用是集群性能问题的原因之一。

大多数希望在文本字段上执行更多操作的用户使用多字段映射,方法是同时拥有用于全文本搜索的文本字段和用于聚合的未分析的关键字字段,如下所示:

PUT my-index-000001
{
  "mappings": {
    "properties": {
      "my_field": {
        "type": "text",
        "fields": {
          "keyword": {
            "type": "keyword"
          }
        }
      }
    }
  }
}
  • 使用my_field字段进行搜索。
  • 使用my_field.keyword字段进行聚合、排序或在脚本中使用。

要在文本字段上启用fielddata,您可以使用更新映射API,如下所示:

PUT my-index-000001/_mapping
{
  "properties": {
    "my_field": {
      "type": "text",
      "fielddata": true
    }
  }
}

"match_only_text"是文本的变体,它在消耗空间效率时交换了位置查询的评分和效率。这个字段有效地以只索引文档(index_options: docs)的方式存储数据,并禁用了标准化(norms: false)。术语查询的执行速度可能比文本字段更快,不过需要查看_source文档来验证短语是否匹配,因此需要查看位置的查询(如match_phrase查询)执行较慢。所有查询返回的分数恒定,都等于1.0。

分析不可配置:match_only_text始终使用默认分析器(默认为标准分析器)进行分析。

不支持span查询,如果绝对需要span查询,可以使用interval查询或text字段类型。

除此之外,match_only_text支持与text相同的查询。与text一样,它不支持排序,并且只支持有限的聚合。

以下是"match_only_text"字段类型的映射示例:

PUT logs
{
  "mappings": {
    "properties": {
      "@timestamp": {
        "type": "date"
      },
      "message": {
        "type": "match_only_text"
      }
    }
  }
}

"match_only_text"字段类型支持以下映射参数:

  • fields:多字段允许相同的字符串值以不同方式多次索引,以满足不同目的,例如一个字段用于搜索,多字段用于排序和聚合,或者使用不同的分析器分析相同的字符串值。
  • meta:关于字段的元数据。

Token count字段类型

Token count字段类型实际上是一个整数字段,它接受字符串值,对其进行分析,然后索引字符串中的标记数。

例如:

PUT my-index-000001
{
  "mappings": {
    "properties": {
      "name": { 
        "type": "text",
        "fields": {
          "length": { 
            "type":     "token_count",
            "analyzer": "standard"
          }
        }
      }
    }
  }
}

PUT my-index-000001/_doc/1
{ "name": "John Smith" }

PUT my-index-000001/_doc/2
{ "name": "Rachel Alice Williams" }

GET my-index-000001/_search
{
  "query": {
    "term": {
      "name.length": 3 
    }
  }
}

在这个示例中,name字段是一个text字段,它使用默认的标准分析器。

name.length字段是一个token_count多字段,它将索引name字段中的标记数量。

这个查询只匹配包含"Rachel Alice Williams"的文档,因为它包含三个标记。

token_count字段的参数包括:

  • analyzer:用于分析字符串值的分析器。
  • enable_position_increments:指示是否应计算位置增量。
  • boost:字段级别的查询时间加权。
  • doc_values:指示字段是否应以列存储方式存储在磁盘上。
  • index:指示字段是否可搜索。
  • null_value:用于替代任何显式空值的数值。
  • store:指示字段值是否应分开存储和可检索。

这个字段类型通常用于索引和查询文本中标记的数量,而不是文本内容本身。

无符号长整数 Unsigned long

Unsigned long是一种数值字段类型,表示无符号64位整数,其最小值为0,最大值为264-1(从0到18446744073709551615,包括边界值)。

以下是一个示例:

PUT my_index
{
  "mappings": {
    "properties": {
      "my_counter": {
        "type": "unsigned_long"
      }
    }
  }
}

Unsigned long可以以数值或字符串形式进行索引,表示范围在[0, 18446744073709551615]的整数值。它们不能具有小数部分。

以下是一个示例:

POST /my_index/_bulk?refresh
{"index":{"_id":1}}
{"my_counter": 0}
{"index":{"_id":2}}
{"my_counter": 9223372036854775808}
{"index":{"_id":3}}
{"my_counter": 18446744073709551614}
{"index":{"_id":4}}
{"my_counter": 18446744073709551615}

Term查询接受数值或字符串形式的任何数字。

以下是一个示例:

GET /my_index/_search
{
    "query": {
        "term" : {
            "my_counter" : 18446744073709551615
        }
    }
}

范围查询的条件可以包含带有小数部分的值。在这种情况下,Elasticsearch将其转换为整数值:gte和gt条件被转换为最接近的整数上限,包括在内,而lt和lte范围被转换为最接近的整数下限,包括在内。建议将范围作为字符串传递,以确保它们被解析而没有丢失精度。

以下是一个示例:

GET /my_index/_search
{
    "query": {
        "range" : {
            "my_counter" : {
                "gte" : "9223372036854775808.5",
                "lte" : "18446744073709551615"
            }
        }
    }
}

对于具有sort的unsigned_long字段的查询,对于特定文档,如果该文档的值在long值范围内,则Elasticsearch返回long类型的排序值,如果值超出此范围,则返回BigInteger类型的排序值。REST客户端需要能够处理JSON中的大整数值,以正确支持此字段类型。

以下是一个示例:

GET /my_index/_search
{
    "query": {
        "match_all" : {}
    },
    "sort" : {"my_counter" : "desc"}
}

目前,在脚本中不支持unsigned_long。

对于unsigned_long的存储字段,以字符串形式存储和返回。

对于terms聚合,与排序值类似,将使用Long或BigInteger值。对于其他聚合,将值转换为double类型。

支持具有混合数值类型的搜索,其中之一是unsigned_long,除了排序查询。因此,在两个索引上执行排序查询,其中一个索引中具有unsigned_long类型的字段,另一个索引中具有long类型的字段,不会产生正确的结果,应避免使用。如果需要此类排序,可以改用基于脚本的排序。

支持具有多个数值类型的聚合,其中之一是unsigned_long。在这种情况下,将值转换为double类型。

版本字段类型 Version

版本字段类型是关键字字段的一个特例,用于处理软件版本值并支持它们的专用优先规则。优先规则遵循语义化版本控制规则的规定,例如主要、次要和修补版本部分按数字顺序排序(即"2.1.0" < “2.4.1” < “2.11.2”),并且预发行版本在正式发布版本之前排序(即"1.0.0-alpha" < “1.0.0”)。

您可以将版本字段索引如下:

PUT my-index-000001
{
  "mappings": {
    "properties": {
      "my_version": {
        "type": "version"
      }
    }
  }
}

该字段提供了与常规关键字字段相同的搜索功能。例如,可以使用match或term查询进行精确匹配,并支持前缀和通配符搜索。主要优点是范围查询将遵循Semver排序规则,因此在"1.0.0"和"1.5.0"之间的范围查询将包括"1.2.3"版本,但不包括"1.11.2"版本,例如。请注意,如果使用常规关键字字段进行索引,那么排序是按字母顺序排列,结果会不同。

软件版本应遵循语义化版本控制规则模式和优先规则,有一个明显的例外,即允许有三个或三个以上的主要版本标识符(即"1.2"或"1.2.3.4"都被视为有效版本,而在严格的Semver规则下它们不合格)。不符合Semver定义的版本字符串(例如"1.2.alpha.4")仍然可以进行索引和检索以进行精确匹配,但它们将按照字母顺序排序后出现在任何具有有效版本的版本之后。空字符串""被视为无效,并在所有有效版本之后但在其他无效版本之前排序。

版本字段的参数包括:

  • meta:字段的元数据。

限制:

该字段类型不针对重度使用通配符、正则表达式或模糊搜索进行了优化。虽然这些类型的查询在此字段中可行,但如果您严重依赖于这些查询,应考虑使用常规关键字字段。

元数据字段 Metadata fields

元数据字段是与每个文档相关的信息,例如 _index(索引)、_type(映射类型)和 _id(文档标识)等。在创建映射类型时,有些元数据字段的行为可以进行自定义。

身份元数据字段:

  • _index(索引):文档所属的索引。
  • _type(映射类型):文档的映射类型。
  • _id(文档标识):文档的唯一标识符。

文档源元数据字段:

  • _source(_源):包含文档主体的原始 JSON 数据。
  • _size(_大小):由 mapper-size 插件提供的 _source 字段的大小,以字节为单位。

文档计数元数据字段:

  • _doc_count(_文档计数):用于存储文档代表的预聚合数据的自定义字段。

索引元数据字段:

  • _field_names(_字段名称):文档中包含非空值的所有字段。
  • _ignored(_已忽略):由于 ignore_malformed 而在索引时被忽略的文档中的所有字段。

路由元数据字段:

  • _routing(_路由):自定义路由值,用于将文档路由到特定的分片。

其他元数据字段:

  • _meta(_元数据):应用程序特定的元数据。
  • _tier(_层级):文档所属索引的当前数据层级首选项。

_doc_count field 文档计数字段
_bucket aggregations(桶聚合)总是返回一个名为 doc_count 的字段,显示了每个桶中聚合和分区的文档数量。计算 doc_count 的值非常简单,对于每个收集到的文档,doc_count 会递增 1。

虽然这种简单的方法在计算针对单个文档的聚合时有效,但它无法准确表示存储了预聚合数据(例如直方图或 aggregate_metric_double 字段)的文档,因为一个汇总字段可能代表多个文档。

为了正确计算处理预聚合数据时的文档数量,我们引入了一个名为 _doc_count 的元数据字段类型。_doc_count 必须始终是表示单个汇总字段中聚合的文档数量的正整数。

当将 _doc_count 字段添加到文档中时,所有桶聚合将尊重其值,并将桶的 doc_count 递增字段的值。如果文档不包含任何 _doc_count 字段,默认情况下 _doc_count = 1。

一个 _doc_count 字段只能在一个文档中存储一个正整数。不允许嵌套数组。

如果文档不包含 _doc_count 字段,聚合器将递增 1,这是默认行为。

示例:

以下的创建索引 API 请求创建了一个具有以下字段映射的新索引:

my_histogram,用于存储百分位数数据的直方图字段
my_text,用于存储直方图标题的关键字字段

PUT my_index
{
  "mappings" : {
    "properties" : {
      "my_histogram" : {
        "type" : "histogram"
      },
      "my_text" : {
        "type" : "keyword"
      }
    }
}

以下的索引 API 请求存储了两个直方图的预聚合数据:histogram_1 和 histogram_2。

PUT my_index/_doc/1
{
  "my_text" : "histogram_1",
  "my_histogram" : {
      "values" : [0.1, 0.2, 0.3, 0.4, 0.5],
      "counts" : [3, 7, 23, 12, 6]
   },
  "_doc_count": 45 
}

PUT my_index/_doc/2
{
  "my_text" : "histogram_2",
  "my_histogram" : {
      "values" : [0.1, 0.25, 0.35, 0.4, 0.45, 0.5],
      "counts" : [8, 17, 8, 7, 6, 2]
   },
  "_doc_count": 62 
}

_doc_count 字段必须是存储用于生成每个直方图的文档数量的正整数。

如果我们在 my_index 上运行以下的 terms 聚合:

GET /_search
{
    "aggs" : {
        "histogram_titles" : {
            "terms" : { "field" : "my_text" }
        }
    }
}

我们将得到以下响应:

{
    ...
    "aggregations" : {
        "histogram_titles" : {
            "doc_count_error_upper_bound": 0,
            "sum_other_doc_count": 0,
            "buckets" : [
                {
                    "key" : "histogram_2",
                    "doc_count" : 62
                },
                {
                    "key" : "histogram_1",
                    "doc_count" : 45
                }
            ]
        }
    }
}

这个响应显示了每个直方图标题的文档数量,正确地反映了存储了 _doc_count 字段的文档的预聚合数据。
_field_names 字段名称
_field_names 字段用于索引包含任何非空值的字段的名称,这些字段包含在文档中。此字段曾用于 exists 查询,以查找具有或不具有特定字段的非空值的文档。

现在,_field_names 字段仅索引已禁用 doc_values 和 norms 的字段的名称。对于启用了 doc_values 或 norm 的字段,exists 查询仍然可用,但不会使用 _field_names 字段。

禁用 _field_names:
禁用 _field_names 已被弃用,并将在未来的主要版本中移除。

通常情况下,禁用 _field_names 不是必要的,因为它不再像以前那样带有索引开销。如果您有许多已禁用 doc_values 和 norms 的字段,且不需要使用这些字段执行 exists 查询,您可以通过将以下内容添加到映射中来禁用 _field_names:

PUT tweets
{
  "mappings": {
    "_field_names": {
      "enabled": false
    }
  }
}

这将禁用 _field_names 字段。
_ignored 字段
_ignored 字段用于索引和存储文档中在索引文档时被忽略的每个字段的名称。这可以出现在字段格式不正确且已启用 ignore_malformed 时,或者当关键字字段的值超出其可选的 ignore_above 设置时。

此字段可通过 term、terms 和 exists 查询进行搜索,并作为搜索命中的一部分返回。

例如,以下查询将匹配所有包含一个或多个被忽略字段的文档:

GET _search
{
  "query": {
    "exists": {
      "field": "_ignored"
    }
}

类似地,以下查询将查找所有在索引时被忽略的文档的 @timestamp 字段:

GET _search
{
  "query": {
    "term": {
      "_ignored": "@timestamp"
    }
}

这将找到所有在索引时被忽略的 @timestamp 字段的文档。
_id field
每个文档都有一个唯一标识符 _id,用于唯一标识文档,并且已被索引,以便可以使用 GET API 或 ids 查询来查找文档。_id 可以在索引时分配,或者可以由Elasticsearch生成一个唯一的 _id。此字段在映射中不可配置。

_id 字段的值可以在诸如 term、terms、match 和 query_string 之类的查询中访问。

示例文档:

PUT my-index-000001/_doc/1
{
  "text": "带有 ID 1 的文档"
}

PUT my-index-000001/_doc/2?refresh=true
{
  "text": "带有 ID 2 的文档"
}

GET my-index-000001/_search
{
  "query": {
    "terms": {
      "_id": [ "1", "2" ] 
    }
  }
}

通过 _id 字段进行查询(也可参见 ids 查询)

_id 字段受限于不可用于聚合、排序和脚本的限制。如果需要在 _id 字段上进行排序或聚合,建议将 _id 字段的内容复制到启用了 doc_values 的另一个字段中。

_id 字段的大小限制为 512 字节,更大的值将被拒绝。
_index field
在跨多个索引执行查询时,有时希望添加与特定索引的文档相关的查询子句。_index 字段允许匹配文档被索引的索引。其值可以在某些查询和聚合以及在排序或脚本中访问:

PUT index_1/_doc/1
{
  "text": "在索引 1 中的文档"
}

PUT index_2/_doc/2?refresh=true
{
  "text": "在索引 2 中的文档"
}

GET index_1,index_2/_search
{
  "query": {
    "terms": {
      "_index": ["index_1", "index_2"] 
    }
  },
  "aggs": {
    "indices": {
      "terms": {
        "field": "_index", 
        "size": 10
      }
    }
  },
  "sort": [
    {
      "_index": { 
        "order": "asc"
      }
    }
  ],
  "script_fields": {
    "index_name": {
      "script": {
        "lang": "painless",
        "source": "doc['_index']" 
      }
    }
  }
}

使用 _index 字段进行查询

在 _index 字段上进行聚合

在 _index 字段上进行排序

在脚本中访问 _index 字段

_index 字段在虚拟上是可用的,它不会被添加到 Lucene 索引作为实际字段。这意味着您可以在术语或术语查询中使用 _index 字段(或任何重写为术语查询的查询,如 match、query_string 或 simple_query_string 查询),以及前缀和通配符查询。但是,它不支持正则表达式和模糊查询。

对 _index 字段的查询除了具体索引名称外,还可以接受索引别名。

在指定远程索引名称时,如 cluster_1:index_3,查询必须包含分隔字符 :。例如,对 cluster_:index_3 的通配符查询将匹配来自远程索引的文档。然而,对 clusterindex_1 的查询仅匹配本地索引,因为没有分隔符。此行为与远程索引名称的通常解析规则相一致。

_meta field
映射类型可以关联自定义元数据。Elasticsearch 并不直接使用这些元数据,但可以用来存储特定于应用程序的元数据,例如文档所属的类别:

PUT my-index-000001
{
  "mappings": {
    "_meta": { 
      "class": "MyApp::User",
      "version": {
        "min": "1.0",
        "max": "1.3"
      }
    }
  }
}

可以使用 GET mapping API 检索此 _meta 信息。

可以使用 update mapping API 在现有类型上更新 _meta 字段:

PUT my-index-000001/_mapping
{
  "_meta": {
    "class": "MyApp2::User3",
    "version": {
      "min": "1.3",
      "max": "1.5"
    }
  }
}

这使您可以随时更新映射类型的 _meta 元数据。这对于存储关于文档的附加信息或自定义应用程序数据非常有用。

_routing field

文档通过以下公式被路由到索引中的特定分片:

routing_factor = num_routing_shards / num_primary_shards
shard_num = (hash(_routing) % num_routing_shards) / routing_factor
其中,num_routing_shards 是索引设置 index.number_of_routing_shards 的值,num_primary_shards 是索引设置 index.number_of_shards 的值。

默认情况下,_routing 的值是文档的 _id。可以通过为每个文档指定自定义的 _routing 值来实现自定义路由模式。例如:

PUT my-index-000001/_doc/1?routing=user1&refresh=true 
{
  "title": "这是一个文档"
}

此文档使用 user1 作为其 _routing 值,而不是其 ID。

在获取、删除或更新文档时,需要提供相同的 _routing 值。

可以在查询中访问 _routing 字段的值:

GET my-index-000001/_search
{
  "query": {
    "terms": {
      "_routing": [ "user1" ] 
    }
  }
}

使用 _routing 字段进行查询

数据流不支持自定义路由。取而代之的是,将查询发送到数据流的适当后备索引。

使用自定义路由进行搜索

自定义路由可以降低搜索的影响。与其将搜索请求广播到索引中的所有分片,不如将请求发送到与特定路由值(或值)匹配的分片:

GET my-index-000001/_search?routing=user1,user2 
{
  "query": {
    "match": {
      "title": "document"
    }
  }
}

此搜索请求仅在与 user1 和 user2 路由值关联的分片上执行。

使路由值为必需项

在使用自定义路由时,重要的是在索引、获取、删除或更新文档时提供路由值。忘记路由值可能导致文档被索引到多个分片上。作为一种保障措施,可以配置 _routing 字段,使自定义路由值对所有 CRUD 操作都是必需的:

PUT my-index-000002
{
  "mappings": {
    "_routing": {
      "required": true 
    }
  }
}

在此示例中,对于所有文档都要求路由。

唯一的 ID 与自定义路由

当索引文档时指定自定义 _routing 时,不能保证 _id 在索引中的所有分片上是唯一的。实际上,如果使用不同的 _routing 值索引,具有相同 _id 的文档可能会出现在不同的分片上。

由用户负责确保 _id 在索引中是唯一的。

路由到索引分区

可以配置索引,以使自定义路由值进入分片的子集,而不是单个分片。这有助于减轻出现不平衡集群的风险,同时仍然减小了搜索的影响。

要启用此功能,需要在索引创建时提供 index.routing_partition_size 的索引级设置。随着分区大小的增加,数据将更均匀地分布,但每个请求将不得不搜索更多的分片。

启用此设置后,计算分片的公式如下:

routing_value = hash(_routing) + hash(_id) % routing_partition_size
shard_num = (routing_value % num_routing_shards) / routing_factor
即,_routing 字段用于计算索引中的一组分片,然后使用 _id 来选择该组内的分片。

为启用此功能,index.routing_partition_size 的值应大于 1 且小于 index.number_of_shards。

一旦启用分区索引,将具有以下限制:

不能在其中创建具有关联字段关系的映射。
索引内的所有映射必须将 _routing 字段标记为必需的。

_source field
_source 字段包含在索引时传递的原始 JSON 文档正文。_source 字段本身不被索引(因此不可搜索),但它被存储,以便在执行获取或搜索等提取请求时可以返回。

禁用 _source 字段:
虽然_source字段非常有用,但它在索引中会产生存储开销。因此,可以通过以下方式禁用它:

PUT my-index-000001
{
  "mappings": {
    "_source": {
      "enabled": false
    }
  }
}

在禁用 _source 字段之前,请考虑一下后果。如果_source字段不可用,将不支持许多功能,包括:

  • update、update_by_query 和 reindex API。
  • 即时高亮显示。
  • 从一个 Elasticsearch 索引重新索引到另一个索引的能力,无论是更改映射或分析,还是将索引升级到新的主要版本。
  • 通过查看索引时使用的原始文档来调试查询或聚合的能力。
  • 可能在未来自动修复索引损坏的能力。

如果担心磁盘空间,可以增加压缩级别,而不是禁用 _source。

包括/排除字段从 _source 中:
一个专家级的功能是在文档被索引后、但在 _source 字段存储之前,可以修剪 _source 字段的内容。

从 _source 中删除字段与禁用 _source 具有类似的缺点,特别是不能将文档从一个 Elasticsearch 索引重新索引到另一个索引。考虑使用源字段过滤而不是删除字段。

可以使用 includes/excludes 参数(还接受通配符)来进行如下设置:

PUT logs
{
  "mappings": {
    "_source": {
      "includes": [
        "*.count",
        "meta.*"
      ],
      "excludes": [
        "meta.description",
        "meta.other.*"
      ]
    }
  }
}

这些字段将从存储的 _source 字段中删除。

即使这些字段不在存储的 _source 中,我们仍然可以在该字段上进行搜索。
_tier field

在跨多个索引执行查询时,有时希望定位存储在给定数据层节点上的索引(data_hot、data_warm、data_cold 或 data_frozen)。_tier 字段允许匹配文档被索引到的索引的 tier_preference 设置。首选值可以在某些查询中访问:

PUT index_1/_doc/1
{
  "text": "在索引 1 中的文档"
}

PUT index_2/_doc/2?refresh=true
{
  "text": "在索引 2 中的文档"
}

GET index_1,index_2/_search
{
  "query": {
    "terms": {
      "_tier": ["data_hot", "data_warm"] 
    }
  }
}

使用 _tier 字段进行查询

通常,查询将使用 terms 查询列出感兴趣的层级,但您可以在任何被重写为术语查询的查询中使用 _tier 字段,如 match、query_string、term、terms 或 simple_query_string 查询,以及前缀和通配符查询。然而,它不支持正则表达式和模糊查询。

索引的 tier_preference 设置是首选层级的逗号分隔列表,按照首选层级主机索引的顺序排列,首选层级在列表中首先列出,然后是可能的多个备选选项。查询匹配只考虑首选项(列表的第一个值)。
映射参数 Mapping parameters
Mapping parameters | Elasticsearch Guide [7.17] | Elastic
以下是常用的一些映射参数:

  1. analyzer: 用于指定分析器,用于文本字段的文本分析。

  2. boost: 用于调整字段的相关性得分,可以提高或降低字段的权重。

  3. doc_values: 指示是否在字段上启用 doc_values,以便进行聚合和排序。

  4. dynamic: 控制字段是否动态映射,允许 Elasticsearch 自动检测字段类型。

  5. enabled: 控制字段是否被启用或禁用,可以用于临时禁用字段而不需要删除映射。

  6. index: 控制字段是否被索引,可以设置为 truefalsenull(使用默认设置)。

  7. store: 控制字段是否被存储,以便在检索文档时获取原始值。

  8. norms: 控制字段的归一化设置,用于评分计算。

  9. properties: 用于定义对象字段的子字段。

  10. search_analyzer: 用于指定搜索时使用的分析器,不同于索引时的分析器。

  11. similarity: 用于指定字段的相似性设置,影响相关性得分。

映射限制
以下是用于限制字段映射数量以防止映射爆炸的设置:

  1. index.mapping.total_fields.limit:这是索引中字段的最大数量。字段和对象映射以及字段别名都计入这一限制。默认值为1000。此限制旨在防止映射和搜索变得过大。较高的值可能导致性能下降和内存问题,尤其是在负载较高或资源较少的集群中。如果您增加此设置,建议同时增加indices.query.bool.max_clause_count设置,该设置限制了查询中的布尔子句的最大数量。

  2. index.mapping.depth.limit:这是字段的最大深度,以内部对象数量来衡量。例如,如果所有字段都在根对象级别定义,那么深度为1。如果存在一个对象映射,则深度为2,依此类推。默认值为20。

  3. index.mapping.nested_fields.limit:这是索引中不同嵌套映射的最大数量。嵌套类型应仅在特殊情况下使用,当需要独立查询对象数组时。为防止不良设计的映射,此设置限制了每个索引中唯一嵌套类型的数量。默认值为50。

  4. index.mapping.nested_objects.limit:这是单个文档中包含的所有嵌套 JSON 对象的最大数量,跨所有嵌套类型。此限制有助于防止文档包含过多嵌套对象时发生内存不足错误。默认值为10000。

  5. index.mapping.field_name_length.limit:此设置用于限制字段名称的最大长度。通常情况下,不需要设置此设置,因为默认设置是足够的,除非用户开始添加具有非常长名称的大量字段。默认值是Long.MAX_VALUE(没有限制)。

  6. index.mapping.dimension_fields.limit(技术预览):此功能处于技术预览阶段,可能在将来的版本中更改或删除。它是 Elastic 内部使用的设置。

删除映射类型
从Elasticsearch 7.0.0开始,不再支持默认映射类型(_default_ mapping type)。在6.x中创建的索引将在Elasticsearch 6.x中继续像以前一样运行。在7.0版本的API中,映射类型被弃用,并在索引创建、映射添加、映射获取、模板添加、模板获取以及字段映射获取等API中进行了破坏性更改。

什么是映射类型?

自Elasticsearch首次发布以来,每个文档都存储在单个索引中,并分配给一个映射类型(mapping type)。映射类型用于表示要索引的文档或实体的类型,例如,Twitter索引可能包括用户类型(user type)和推文类型(tweet type)。

每个映射类型可以拥有自己的字段。例如,用户类型可能包括full_name字段、user_name字段和email字段,而推文类型可以包括content字段、tweeted_at字段和与用户类型相同的user_name字段。

每个文档都有一个名为_type的元数据字段,其中包含类型名称。通过在URL中指定类型名称,可以限制搜索仅在一个或多个类型中进行:

GET twitter/user,tweet/_search
{
  "query": {
    "match": {
      "user_name": "kimchy"
    }
  }
}

_type字段与文档的_id组合生成_uid字段,因此具有相同_id但不同类型的文档可以存在于单个索引中。

映射类型还用于建立文档之间的父子关系,因此问题类型(question)的文档可以是答案类型(answer)的父文档。

为什么要移除映射类型?

最初,我们提到“索引”类似于SQL数据库中的“数据库”,而“类型”等同于“表”。

这是一个不准确的类比,它导致了错误的假设。在SQL数据库中,各个表是相互独立的。一个表中的列与另一个表中具有相同名称的列之间没有关联。但在映射类型中的字段却不同。

在Elasticsearch索引中,具有相同名称的字段在内部由相同的Lucene字段支持。换句话说,在上面的示例中,用户类型中的user_name字段与推文类型中的user_name字段实际上存储在相同的字段中,并且两个user_name字段必须在两个类型中具有相同的映射(定义)。

这可能会导致困扰,例如,当您希望在同一索引中的一个类型中将deleted字段设为日期字段,而在另一个类型中将其设为布尔字段时。

此外,将不具备共同字段或仅有少数共同字段的不同实体存储在同一个索引中会导致数据稀疏,干扰Lucene有效压缩文档的能力。

出于这些原因,我们决定从Elasticsearch中移除映射类型的概念。

映射类型的替代方案:

  1. 按文档类型创建索引(Index per document type): 第一种替代方案是为每个文档类型创建一个单独的索引。而不是将推文和用户存储在单个twitter索引中,您可以在tweets索引中存储推文,在users索引中存储用户。这些索引是完全独立的,因此不会在不同索引之间发生字段类型冲突。此方法有两个好处:数据更有可能是密集的,因此可以受益于Lucene中使用的压缩技术;全文搜索中用于评分的术语统计更可能准确,因为同一索引中的所有文档代表单个实体。每个索引可以根据其包含的文档数量适当设置主分片的数量。

  2. 自定义类型字段(Custom type field): 当然,每个群集中的主分片数量存在限制,因此您可能不希望为只包含少数几千个文档的集合浪费整个主分片。在这种情况下,您可以实现自定义类型字段,它将类似于旧的_type。例如,上面的用户/推文示例原本的工作流如下:

    PUT twitter
    {
      "mappings": {
        "user": {
          "properties": {
            "name": { "type": "text" },
            "user_name": { "type": "keyword" },
            "email": { "type": "keyword" }
          }
        },
        "tweet": {
          "properties": {
            "content": { "type": "text" },
            "user_name": { "type": "keyword" },
            "tweeted_at": { "type": "date" }
          }
        }
      }
    }
    
    PUT twitter/user/kimchy
    {
      "name": "Shay Banon",
      "user_name": "kimchy",
      "email": "shay@kimchy.com"
    }
    
    PUT twitter/tweet/1
    {
      "user_name": "kimchy",
      "tweeted_at": "2017-10-24T09:00:00Z",
      "content": "Types are going away"
    }
    
    GET twitter/tweet/_search
    {
      "query": {
        "match": {
          "user_name": "kimchy"
        }
      }
    }
    

    您可以通过添加自定义类型字段来实现相同的目标,如下所示:

    PUT twitter
    {
      "mappings": {
        "_doc": {
          "properties": {
            "type": { "type": "keyword"
    
    

},
“name”: { “type”: “text” },
“user_name”: { “type”: “keyword” },
“email”: { “type”: “keyword” },
“content”: { “type”: “text” },
“tweeted_at”: { “type”: “date” }
}
}
}
}

PUT twitter/_doc/user-kimchy
{
  "type": "user", 
  "name": "Shay Banon",
  "user_name": "kimchy",
  "email": "shay@kimchy.com"
}

PUT twitter/_doc/tweet-1
{
  "type": "tweet", 
  "user_name": "kimchy",
  "tweeted_at": "2017-10-24T09:00:00Z",
  "content": "Types are going away"
}

GET twitter/_search
{
  "query": {
    "bool": {
      "must": {
        "match": {
          "user_name": "kimchy"
        }
      },
      "filter": {
        "match": {
          "type": "tweet" 
        }
      }
    }
  }
}
```

显式的类型字段取代了隐式的`_type`字段。
  1. 无映射类型的父子关系(Parent/Child without mapping types): 以前,父子关系是通过将一个映射类型指定为父类型,将一个或多个其他映射类型指定为子类型来表示的。没有映射类型,我们不能再使用这种语法。父子关系特性将继续像以前一样运行,只是表示文档之间关系的方式已更改为使用新的join字段。

映射类型移除的时间表:

  • Elasticsearch 5.6.0: 在索引上设置index.mapping.single_type: true将启用单索引中的单一类型行为,该行为将在6.0版本中强制执行。作为Elasticsearch 5.6的一部分,用于父子关系的新字段在新建的索引中可用。
  • Elasticsearch 6.x: 在5.x中创建的索引将在6.x中继续像5.x中一样工作。在6.x中创建的索引仅允许每个索引一个类型,可以使用任何名称作为类型,但每个索引只能有一个类型。推荐的类型名称是_doc,以便在7.0中索引API具有与其在路径中的路径相同的路径:PUT {index}/_doc/{id}POST {index}/_doc_type名称不再与_id组合以生成_uid字段。_uid字段已成为_id字段的别名。新索引不再支持旧样式的父子关系,应使用新的join字段。_default_映射类型已被弃用。在6.8版本中,索引创建、索引模板和映射API支持查询字符串参数(include_type_name),该参数指示请求和响应是否应包含类型名称。默认值为true,应显式设置为准备升级到7.0版本。不设置include_type_name会导致弃用警告。没有明确类型的索引将使用虚拟类型名称_doc
  • Elasticsearch 7.x: 在请求中指定类型已被弃用。例如,在7.0中,索引文档不再需要文档类型。新的索引API是PUT {index}/_doc/{id}(对于显式id)和POST {index}/_doc(对于自动生成的id)。请注意,在7.0版本中,_doc是路径的永久部分,代表端点名称而不是文档类型。索引创建、索引模板和映射API中的include_type_name参数将默认为false。设置该参数将导致弃用警告。_default_映射类型已被移除。
  • **

Elasticsearch 8.x:** 不再支持在请求中指定类型。include_type_name参数已被移除。

将多类型索引迁移到单类型的方法:

Reindex API可以用于将多类型索引转换为单类型索引。以下示例适用于Elasticsearch 5.6或Elasticsearch 6.x。在6.x中,无需指定index.mapping.single_type,因为它是默认设置。

按文档类型创建索引:

首先将twitter索引分成tweets索引和users索引:

PUT users
{
  "settings": {
    "index.mapping.single_type": true
  },
  "mappings": {
    "_doc": {
      "properties": {
        "name": {
          "type": "text"
        },
        "user_name": {
          "type": "keyword"
        },
        "email": {
          "type": "keyword"
        }
      }
    }
  }
}

PUT tweets
{
  "settings": {
    "index.mapping.single_type": true
  },
  "mappings": {
    "_doc": {
      "properties": {
        "content": {
          "type": "text"
        },
        "user_name": {
          "type": "keyword"
        },
        "tweeted_at": {
          "type": "date"
        }
      }
    }
  }
}

POST _reindex
{
  "source": {
    "index": "twitter",
    "type": "user"
  },
  "dest": {
    "index": "users",
    "type": "_doc"
  }
}

POST _reindex
{
  "source": {
    "index": "twitter",
    "type": "tweet"
  },
  "dest": {
    "index": "tweets",
    "type": "_doc"
  }
}

自定义类型字段:

下一个示例添加了自定义类型字段并将其设置为原始_type的值。还将类型添加到_id中,以防有冲突的ID具有不同的类型的文档:

PUT new_twitter
{
  "mappings": {
    "_doc": {
      "properties": {
        "type": {
          "type": "keyword"
        },
        "name": {
          "type": "text"
        },
        "user_name": {
          "type": "keyword"
        },
        "email": {
          "type": "keyword"
        },
        "content": {
          "type": "text"
        },
        "tweeted_at": {
          "type": "date"
        }
      }
    }
  }
}

POST _reindex
{
  "source": {
    "index": "twitter"
  },
  "dest": {
    "index": "new_twitter"
  },
  "script": {
    "source": """
      ctx._source.type = ctx._type;
      ctx._id = ctx._type + '-' + ctx._id;
      ctx._type = '_doc';
    """
  }
}

7.0版本的无类型API:

在Elasticsearch 7.0中,每个API都支持无类型请求,指定类型将产生弃用警告。

无类型API即使目标索引包含自定义类型也能正常工作。例如,如果索引具有自定义类型名称my_type,我们可以使用无类型的索引调用添加文档,并使用无类型的获取调用加载文档。

索引API:

索引创建、索引模板和映射API支持一个新的include_type_name URL参数,该参数指定请求和响应中的映射定义是否包含类型名称。该参数在6.8版本中默认为true,以匹配使用映射中的类型名称的7.0之前的行为。在7.0版本中,默认为false,并将在8.0版本中移除。为了避免在6.8中出现弃用警告,可以在6.8版本中显式设置该参数为truefalse。在7.0中,设置include_type_name参数将产生弃用警告。

文档API:

在7.0中,文档API必须使用{index}/_doc路径进行调用,以实现自动生成的_id,以及{index}/_doc/{id}以指定显式的id

搜索API:

在调用搜索API(如_search_msearch_explain)时,不应在URL中包含类型。此外,在查询、聚合或脚本中不应再使用_type字段。

响应中的类型:

文档和搜索API将继续在响应中返回_type键,以避免破坏响应解析。但该键被视为弃用,不再应引用。在8.0版本中,将完全从响应中删除类型。

请注意,当使用弃用的类型API时,索引的映射类型将正常返回,但无类型API将在响应中始终返回虚拟类型_doc,即使映

射中的类型不是_doc。如果您需要直接访问索引的映射类型,请避免使用无类型API,而是使用_default_ API,或从节点的_mappings endpoint检索索引的映射。

总之,Elasticsearch在7.0版本中已经不再支持映射类型。原来使用类型的功能现在可以通过单索引多类型、自定义类型字段或无类型的API请求来实现。这些变化使Elasticsearch的数据建模更加灵活和清晰,同时减少了数据稀疏性和字段类型冲突的问题。

元数据字段 Metadata fields

元数据字段是与每个文档相关的信息,例如 _index(索引)、_type(映射类型)和 _id(文档标识)等。在创建映射类型时,有些元数据字段的行为可以进行自定义。

身份元数据字段:

  • _index(索引):文档所属的索引。
  • _type(映射类型):文档的映射类型。
  • _id(文档标识):文档的唯一标识符。

文档源元数据字段:

  • _source(_源):包含文档主体的原始 JSON 数据。
  • _size(_大小):由 mapper-size 插件提供的 _source 字段的大小,以字节为单位。

文档计数元数据字段:

  • _doc_count(_文档计数):用于存储文档代表的预聚合数据的自定义字段。

索引元数据字段:

  • _field_names(_字段名称):文档中包含非空值的所有字段。
  • _ignored(_已忽略):由于 ignore_malformed 而在索引时被忽略的文档中的所有字段。

路由元数据字段:

  • _routing(_路由):自定义路由值,用于将文档路由到特定的分片。

其他元数据字段:

  • _meta(_元数据):应用程序特定的元数据。
  • _tier(_层级):文档所属索引的当前数据层级首选项。

_doc_count field 文档计数字段
_bucket aggregations(桶聚合)总是返回一个名为 doc_count 的字段,显示了每个桶中聚合和分区的文档数量。计算 doc_count 的值非常简单,对于每个收集到的文档,doc_count 会递增 1。

虽然这种简单的方法在计算针对单个文档的聚合时有效,但它无法准确表示存储了预聚合数据(例如直方图或 aggregate_metric_double 字段)的文档,因为一个汇总字段可能代表多个文档。

为了正确计算处理预聚合数据时的文档数量,我们引入了一个名为 _doc_count 的元数据字段类型。_doc_count 必须始终是表示单个汇总字段中聚合的文档数量的正整数。

当将 _doc_count 字段添加到文档中时,所有桶聚合将尊重其值,并将桶的 doc_count 递增字段的值。如果文档不包含任何 _doc_count 字段,默认情况下 _doc_count = 1。

一个 _doc_count 字段只能在一个文档中存储一个正整数。不允许嵌套数组。

如果文档不包含 _doc_count 字段,聚合器将递增 1,这是默认行为。

示例:

以下的创建索引 API 请求创建了一个具有以下字段映射的新索引:

my_histogram,用于存储百分位数数据的直方图字段
my_text,用于存储直方图标题的关键字字段

PUT my_index
{
  "mappings" : {
    "properties" : {
      "my_histogram" : {
        "type" : "histogram"
      },
      "my_text" : {
        "type" : "keyword"
      }
    }
}

以下的索引 API 请求存储了两个直方图的预聚合数据:histogram_1 和 histogram_2。

PUT my_index/_doc/1
{
  "my_text" : "histogram_1",
  "my_histogram" : {
      "values" : [0.1, 0.2, 0.3, 0.4, 0.5],
      "counts" : [3, 7, 23, 12, 6]
   },
  "_doc_count": 45 
}

PUT my_index/_doc/2
{
  "my_text" : "histogram_2",
  "my_histogram" : {
      "values" : [0.1, 0.25, 0.35, 0.4, 0.45, 0.5],
      "counts" : [8, 17, 8, 7, 6, 2]
   },
  "_doc_count": 62 
}

_doc_count 字段必须是存储用于生成每个直方图的文档数量的正整数。

如果我们在 my_index 上运行以下的 terms 聚合:

GET /_search
{
    "aggs" : {
        "histogram_titles" : {
            "terms" : { "field" : "my_text" }
        }
    }
}

我们将得到以下响应:

{
    ...
    "aggregations" : {
        "histogram_titles" : {
            "doc_count_error_upper_bound": 0,
            "sum_other_doc_count": 0,
            "buckets" : [
                {
                    "key" : "histogram_2",
                    "doc_count" : 62
                },
                {
                    "key" : "histogram_1",
                    "doc_count" : 45
                }
            ]
        }
    }
}

这个响应显示了每个直方图标题的文档数量,正确地反映了存储了 _doc_count 字段的文档的预聚合数据。
_field_names 字段名称
_field_names 字段用于索引包含任何非空值的字段的名称,这些字段包含在文档中。此字段曾用于 exists 查询,以查找具有或不具有特定字段的非空值的文档。

现在,_field_names 字段仅索引已禁用 doc_values 和 norms 的字段的名称。对于启用了 doc_values 或 norm 的字段,exists 查询仍然可用,但不会使用 _field_names 字段。

禁用 _field_names:
禁用 _field_names 已被弃用,并将在未来的主要版本中移除。

通常情况下,禁用 _field_names 不是必要的,因为它不再像以前那样带有索引开销。如果您有许多已禁用 doc_values 和 norms 的字段,且不需要使用这些字段执行 exists 查询,您可以通过将以下内容添加到映射中来禁用 _field_names:

PUT tweets
{
  "mappings": {
    "_field_names": {
      "enabled": false
    }
  }
}

这将禁用 _field_names 字段。
_ignored 字段
_ignored 字段用于索引和存储文档中在索引文档时被忽略的每个字段的名称。这可以出现在字段格式不正确且已启用 ignore_malformed 时,或者当关键字字段的值超出其可选的 ignore_above 设置时。

此字段可通过 term、terms 和 exists 查询进行搜索,并作为搜索命中的一部分返回。

例如,以下查询将匹配所有包含一个或多个被忽略字段的文档:

GET _search
{
  "query": {
    "exists": {
      "field": "_ignored"
    }
}

类似地,以下查询将查找所有在索引时被忽略的文档的 @timestamp 字段:

GET _search
{
  "query": {
    "term": {
      "_ignored": "@timestamp"
    }
}

这将找到所有在索引时被忽略的 @timestamp 字段的文档。
_id field
每个文档都有一个唯一标识符 _id,用于唯一标识文档,并且已被索引,以便可以使用 GET API 或 ids 查询来查找文档。_id 可以在索引时分配,或者可以由Elasticsearch生成一个唯一的 _id。此字段在映射中不可配置。

_id 字段的值可以在诸如 term、terms、match 和 query_string 之类的查询中访问。

示例文档:

PUT my-index-000001/_doc/1
{
  "text": "带有 ID 1 的文档"
}

PUT my-index-000001/_doc/2?refresh=true
{
  "text": "带有 ID 2 的文档"
}

GET my-index-000001/_search
{
  "query": {
    "terms": {
      "_id": [ "1", "2" ] 
    }
  }
}

通过 _id 字段进行查询(也可参见 ids 查询)

_id 字段受限于不可用于聚合、排序和脚本的限制。如果需要在 _id 字段上进行排序或聚合,建议将 _id 字段的内容复制到启用了 doc_values 的另一个字段中。

_id 字段的大小限制为 512 字节,更大的值将被拒绝。
_index field
在跨多个索引执行查询时,有时希望添加与特定索引的文档相关的查询子句。_index 字段允许匹配文档被索引的索引。其值可以在某些查询和聚合以及在排序或脚本中访问:

PUT index_1/_doc/1
{
  "text": "在索引 1 中的文档"
}

PUT index_2/_doc/2?refresh=true
{
  "text": "在索引 2 中的文档"
}

GET index_1,index_2/_search
{
  "query": {
    "terms": {
      "_index": ["index_1", "index_2"] 
    }
  },
  "aggs": {
    "indices": {
      "terms": {
        "field": "_index", 
        "size": 10
      }
    }
  },
  "sort": [
    {
      "_index": { 
        "order": "asc"
      }
    }
  ],
  "script_fields": {
    "index_name": {
      "script": {
        "lang": "painless",
        "source": "doc['_index']" 
      }
    }
  }
}

使用 _index 字段进行查询

在 _index 字段上进行聚合

在 _index 字段上进行排序

在脚本中访问 _index 字段

_index 字段在虚拟上是可用的,它不会被添加到 Lucene 索引作为实际字段。这意味着您可以在术语或术语查询中使用 _index 字段(或任何重写为术语查询的查询,如 match、query_string 或 simple_query_string 查询),以及前缀和通配符查询。但是,它不支持正则表达式和模糊查询。

对 _index 字段的查询除了具体索引名称外,还可以接受索引别名。

在指定远程索引名称时,如 cluster_1:index_3,查询必须包含分隔字符 :。例如,对 cluster_:index_3 的通配符查询将匹配来自远程索引的文档。然而,对 clusterindex_1 的查询仅匹配本地索引,因为没有分隔符。此行为与远程索引名称的通常解析规则相一致。

_meta field
映射类型可以关联自定义元数据。Elasticsearch 并不直接使用这些元数据,但可以用来存储特定于应用程序的元数据,例如文档所属的类别:

PUT my-index-000001
{
  "mappings": {
    "_meta": { 
      "class": "MyApp::User",
      "version": {
        "min": "1.0",
        "max": "1.3"
      }
    }
  }
}

可以使用 GET mapping API 检索此 _meta 信息。

可以使用 update mapping API 在现有类型上更新 _meta 字段:

PUT my-index-000001/_mapping
{
  "_meta": {
    "class": "MyApp2::User3",
    "version": {
      "min": "1.3",
      "max": "1.5"
    }
  }
}

这使您可以随时更新映射类型的 _meta 元数据。这对于存储关于文档的附加信息或自定义应用程序数据非常有用。

_routing field

文档通过以下公式被路由到索引中的特定分片:

routing_factor = num_routing_shards / num_primary_shards
shard_num = (hash(_routing) % num_routing_shards) / routing_factor
其中,num_routing_shards 是索引设置 index.number_of_routing_shards 的值,num_primary_shards 是索引设置 index.number_of_shards 的值。

默认情况下,_routing 的值是文档的 _id。可以通过为每个文档指定自定义的 _routing 值来实现自定义路由模式。例如:

PUT my-index-000001/_doc/1?routing=user1&refresh=true 
{
  "title": "这是一个文档"
}

此文档使用 user1 作为其 _routing 值,而不是其 ID。

在获取、删除或更新文档时,需要提供相同的 _routing 值。

可以在查询中访问 _routing 字段的值:

GET my-index-000001/_search
{
  "query": {
    "terms": {
      "_routing": [ "user1" ] 
    }
  }
}

使用 _routing 字段进行查询

数据流不支持自定义路由。取而代之的是,将查询发送到数据流的适当后备索引。

使用自定义路由进行搜索

自定义路由可以降低搜索的影响。与其将搜索请求广播到索引中的所有分片,不如将请求发送到与特定路由值(或值)匹配的分片:

GET my-index-000001/_search?routing=user1,user2 
{
  "query": {
    "match": {
      "title": "document"
    }
  }
}

此搜索请求仅在与 user1 和 user2 路由值关联的分片上执行。

使路由值为必需项

在使用自定义路由时,重要的是在索引、获取、删除或更新文档时提供路由值。忘记路由值可能导致文档被索引到多个分片上。作为一种保障措施,可以配置 _routing 字段,使自定义路由值对所有 CRUD 操作都是必需的:

PUT my-index-000002
{
  "mappings": {
    "_routing": {
      "required": true 
    }
  }
}

在此示例中,对于所有文档都要求路由。

唯一的 ID 与自定义路由

当索引文档时指定自定义 _routing 时,不能保证 _id 在索引中的所有分片上是唯一的。实际上,如果使用不同的 _routing 值索引,具有相同 _id 的文档可能会出现在不同的分片上。

由用户负责确保 _id 在索引中是唯一的。

路由到索引分区

可以配置索引,以使自定义路由值进入分片的子集,而不是单个分片。这有助于减轻出现不平衡集群的风险,同时仍然减小了搜索的影响。

要启用此功能,需要在索引创建时提供 index.routing_partition_size 的索引级设置。随着分区大小的增加,数据将更均匀地分布,但每个请求将不得不搜索更多的分片。

启用此设置后,计算分片的公式如下:

routing_value = hash(_routing) + hash(_id) % routing_partition_size
shard_num = (routing_value % num_routing_shards) / routing_factor
即,_routing 字段用于计算索引中的一组分片,然后使用 _id 来选择该组内的分片。

为启用此功能,index.routing_partition_size 的值应大于 1 且小于 index.number_of_shards。

一旦启用分区索引,将具有以下限制:

不能在其中创建具有关联字段关系的映射。
索引内的所有映射必须将 _routing 字段标记为必需的。

_source field
_source 字段包含在索引时传递的原始 JSON 文档正文。_source 字段本身不被索引(因此不可搜索),但它被存储,以便在执行获取或搜索等提取请求时可以返回。

禁用 _source 字段:
虽然_source字段非常有用,但它在索引中会产生存储开销。因此,可以通过以下方式禁用它:

PUT my-index-000001
{
  "mappings": {
    "_source": {
      "enabled": false
    }
  }
}

在禁用 _source 字段之前,请考虑一下后果。如果_source字段不可用,将不支持许多功能,包括:

  • update、update_by_query 和 reindex API。
  • 即时高亮显示。
  • 从一个 Elasticsearch 索引重新索引到另一个索引的能力,无论是更改映射或分析,还是将索引升级到新的主要版本。
  • 通过查看索引时使用的原始文档来调试查询或聚合的能力。
  • 可能在未来自动修复索引损坏的能力。

如果担心磁盘空间,可以增加压缩级别,而不是禁用 _source。

包括/排除字段从 _source 中:
一个专家级的功能是在文档被索引后、但在 _source 字段存储之前,可以修剪 _source 字段的内容。

从 _source 中删除字段与禁用 _source 具有类似的缺点,特别是不能将文档从一个 Elasticsearch 索引重新索引到另一个索引。考虑使用源字段过滤而不是删除字段。

可以使用 includes/excludes 参数(还接受通配符)来进行如下设置:

PUT logs
{
  "mappings": {
    "_source": {
      "includes": [
        "*.count",
        "meta.*"
      ],
      "excludes": [
        "meta.description",
        "meta.other.*"
      ]
    }
  }
}

这些字段将从存储的 _source 字段中删除。

即使这些字段不在存储的 _source 中,我们仍然可以在该字段上进行搜索。
_tier field

在跨多个索引执行查询时,有时希望定位存储在给定数据层节点上的索引(data_hot、data_warm、data_cold 或 data_frozen)。_tier 字段允许匹配文档被索引到的索引的 tier_preference 设置。首选值可以在某些查询中访问:

PUT index_1/_doc/1
{
  "text": "在索引 1 中的文档"
}

PUT index_2/_doc/2?refresh=true
{
  "text": "在索引 2 中的文档"
}

GET index_1,index_2/_search
{
  "query": {
    "terms": {
      "_tier": ["data_hot", "data_warm"] 
    }
  }
}

使用 _tier 字段进行查询

通常,查询将使用 terms 查询列出感兴趣的层级,但您可以在任何被重写为术语查询的查询中使用 _tier 字段,如 match、query_string、term、terms 或 simple_query_string 查询,以及前缀和通配符查询。然而,它不支持正则表达式和模糊查询。

索引的 tier_preference 设置是首选层级的逗号分隔列表,按照首选层级主机索引的顺序排列,首选层级在列表中首先列出,然后是可能的多个备选选项。查询匹配只考虑首选项(列表的第一个值)。

映射参数 Mapping parameters

Mapping parameters | Elasticsearch Guide [7.17] | Elastic
以下是常用的一些映射参数:

  1. analyzer: 用于指定分析器,用于文本字段的文本分析。

  2. boost: 用于调整字段的相关性得分,可以提高或降低字段的权重。

  3. doc_values: 指示是否在字段上启用 doc_values,以便进行聚合和排序。

  4. dynamic: 控制字段是否动态映射,允许 Elasticsearch 自动检测字段类型。

  5. enabled: 控制字段是否被启用或禁用,可以用于临时禁用字段而不需要删除映射。

  6. index: 控制字段是否被索引,可以设置为 truefalsenull(使用默认设置)。

  7. store: 控制字段是否被存储,以便在检索文档时获取原始值。

  8. norms: 控制字段的归一化设置,用于评分计算。

  9. properties: 用于定义对象字段的子字段。

  10. search_analyzer: 用于指定搜索时使用的分析器,不同于索引时的分析器。

  11. similarity: 用于指定字段的相似性设置,影响相关性得分。

映射限制

以下是用于限制字段映射数量以防止映射爆炸的设置:

  1. index.mapping.total_fields.limit:这是索引中字段的最大数量。字段和对象映射以及字段别名都计入这一限制。默认值为1000。此限制旨在防止映射和搜索变得过大。较高的值可能导致性能下降和内存问题,尤其是在负载较高或资源较少的集群中。如果您增加此设置,建议同时增加indices.query.bool.max_clause_count设置,该设置限制了查询中的布尔子句的最大数量。

  2. index.mapping.depth.limit:这是字段的最大深度,以内部对象数量来衡量。例如,如果所有字段都在根对象级别定义,那么深度为1。如果存在一个对象映射,则深度为2,依此类推。默认值为20。

  3. index.mapping.nested_fields.limit:这是索引中不同嵌套映射的最大数量。嵌套类型应仅在特殊情况下使用,当需要独立查询对象数组时。为防止不良设计的映射,此设置限制了每个索引中唯一嵌套类型的数量。默认值为50。

  4. index.mapping.nested_objects.limit:这是单个文档中包含的所有嵌套 JSON 对象的最大数量,跨所有嵌套类型。此限制有助于防止文档包含过多嵌套对象时发生内存不足错误。默认值为10000。

  5. index.mapping.field_name_length.limit:此设置用于限制字段名称的最大长度。通常情况下,不需要设置此设置,因为默认设置是足够的,除非用户开始添加具有非常长名称的大量字段。默认值是Long.MAX_VALUE(没有限制)。

  6. index.mapping.dimension_fields.limit(技术预览):此功能处于技术预览阶段,可能在将来的版本中更改或删除。它是 Elastic 内部使用的设置。

删除映射类型

从Elasticsearch 7.0.0开始,不再支持默认映射类型(_default_ mapping type)。在6.x中创建的索引将在Elasticsearch 6.x中继续像以前一样运行。在7.0版本的API中,映射类型被弃用,并在索引创建、映射添加、映射获取、模板添加、模板获取以及字段映射获取等API中进行了破坏性更改。

什么是映射类型?

自Elasticsearch首次发布以来,每个文档都存储在单个索引中,并分配给一个映射类型(mapping type)。映射类型用于表示要索引的文档或实体的类型,例如,Twitter索引可能包括用户类型(user type)和推文类型(tweet type)。

每个映射类型可以拥有自己的字段。例如,用户类型可能包括full_name字段、user_name字段和email字段,而推文类型可以包括content字段、tweeted_at字段和与用户类型相同的user_name字段。

每个文档都有一个名为_type的元数据字段,其中包含类型名称。通过在URL中指定类型名称,可以限制搜索仅在一个或多个类型中进行:

GET twitter/user,tweet/_search
{
  "query": {
    "match": {
      "user_name": "kimchy"
    }
  }
}

_type字段与文档的_id组合生成_uid字段,因此具有相同_id但不同类型的文档可以存在于单个索引中。

映射类型还用于建立文档之间的父子关系,因此问题类型(question)的文档可以是答案类型(answer)的父文档。

为什么要移除映射类型?

最初,我们提到“索引”类似于SQL数据库中的“数据库”,而“类型”等同于“表”。

这是一个不准确的类比,它导致了错误的假设。在SQL数据库中,各个表是相互独立的。一个表中的列与另一个表中具有相同名称的列之间没有关联。但在映射类型中的字段却不同。

在Elasticsearch索引中,具有相同名称的字段在内部由相同的Lucene字段支持。换句话说,在上面的示例中,用户类型中的user_name字段与推文类型中的user_name字段实际上存储在相同的字段中,并且两个user_name字段必须在两个类型中具有相同的映射(定义)。

这可能会导致困扰,例如,当您希望在同一索引中的一个类型中将deleted字段设为日期字段,而在另一个类型中将其设为布尔字段时。

此外,将不具备共同字段或仅有少数共同字段的不同实体存储在同一个索引中会导致数据稀疏,干扰Lucene有效压缩文档的能力。

出于这些原因,我们决定从Elasticsearch中移除映射类型的概念。

映射类型的替代方案:

  1. 按文档类型创建索引(Index per document type): 第一种替代方案是为每个文档类型创建一个单独的索引。而不是将推文和用户存储在单个twitter索引中,您可以在tweets索引中存储推文,在users索引中存储用户。这些索引是完全独立的,因此不会在不同索引之间发生字段类型冲突。此方法有两个好处:数据更有可能是密集的,因此可以受益于Lucene中使用的压缩技术;全文搜索中用于评分的术语统计更可能准确,因为同一索引中的所有文档代表单个实体。每个索引可以根据其包含的文档数量适当设置主分片的数量。

  2. 自定义类型字段(Custom type field): 当然,每个群集中的主分片数量存在限制,因此您可能不希望为只包含少数几千个文档的集合浪费整个主分片。在这种情况下,您可以实现自定义类型字段,它将类似于旧的_type。例如,上面的用户/推文示例原本的工作流如下:

    PUT twitter
    {
      "mappings": {
        "user": {
          "properties": {
            "name": { "type": "text" },
            "user_name": { "type": "keyword" },
            "email": { "type": "keyword" }
          }
        },
        "tweet": {
          "properties": {
            "content": { "type": "text" },
            "user_name": { "type": "keyword" },
            "tweeted_at": { "type": "date" }
          }
        }
      }
    }
    
    PUT twitter/user/kimchy
    {
      "name": "Shay Banon",
      "user_name": "kimchy",
      "email": "shay@kimchy.com"
    }
    
    PUT twitter/tweet/1
    {
      "user_name": "kimchy",
      "tweeted_at": "2017-10-24T09:00:00Z",
      "content": "Types are going away"
    }
    
    GET twitter/tweet/_search
    {
      "query": {
        "match": {
          "user_name": "kimchy"
        }
      }
    }
    

    您可以通过添加自定义类型字段来实现相同的目标,如下所示:

    PUT twitter
    {
      "mappings": {
        "_doc": {
          "properties": {
            "type": { "type": "keyword"
    
    

},
“name”: { “type”: “text” },
“user_name”: { “type”: “keyword” },
“email”: { “type”: “keyword” },
“content”: { “type”: “text” },
“tweeted_at”: { “type”: “date” }
}
}
}
}

PUT twitter/_doc/user-kimchy
{
  "type": "user", 
  "name": "Shay Banon",
  "user_name": "kimchy",
  "email": "shay@kimchy.com"
}

PUT twitter/_doc/tweet-1
{
  "type": "tweet", 
  "user_name": "kimchy",
  "tweeted_at": "2017-10-24T09:00:00Z",
  "content": "Types are going away"
}

GET twitter/_search
{
  "query": {
    "bool": {
      "must": {
        "match": {
          "user_name": "kimchy"
        }
      },
      "filter": {
        "match": {
          "type": "tweet" 
        }
      }
    }
  }
}
```

显式的类型字段取代了隐式的`_type`字段。
  1. 无映射类型的父子关系(Parent/Child without mapping types): 以前,父子关系是通过将一个映射类型指定为父类型,将一个或多个其他映射类型指定为子类型来表示的。没有映射类型,我们不能再使用这种语法。父子关系特性将继续像以前一样运行,只是表示文档之间关系的方式已更改为使用新的join字段。

映射类型移除的时间表:

  • Elasticsearch 5.6.0: 在索引上设置index.mapping.single_type: true将启用单索引中的单一类型行为,该行为将在6.0版本中强制执行。作为Elasticsearch 5.6的一部分,用于父子关系的新字段在新建的索引中可用。
  • Elasticsearch 6.x: 在5.x中创建的索引将在6.x中继续像5.x中一样工作。在6.x中创建的索引仅允许每个索引一个类型,可以使用任何名称作为类型,但每个索引只能有一个类型。推荐的类型名称是_doc,以便在7.0中索引API具有与其在路径中的路径相同的路径:PUT {index}/_doc/{id}POST {index}/_doc_type名称不再与_id组合以生成_uid字段。_uid字段已成为_id字段的别名。新索引不再支持旧样式的父子关系,应使用新的join字段。_default_映射类型已被弃用。在6.8版本中,索引创建、索引模板和映射API支持查询字符串参数(include_type_name),该参数指示请求和响应是否应包含类型名称。默认值为true,应显式设置为准备升级到7.0版本。不设置include_type_name会导致弃用警告。没有明确类型的索引将使用虚拟类型名称_doc
  • Elasticsearch 7.x: 在请求中指定类型已被弃用。例如,在7.0中,索引文档不再需要文档类型。新的索引API是PUT {index}/_doc/{id}(对于显式id)和POST {index}/_doc(对于自动生成的id)。请注意,在7.0版本中,_doc是路径的永久部分,代表端点名称而不是文档类型。索引创建、索引模板和映射API中的include_type_name参数将默认为false。设置该参数将导致弃用警告。_default_映射类型已被移除。
  • **

Elasticsearch 8.x:** 不再支持在请求中指定类型。include_type_name参数已被移除。

将多类型索引迁移到单类型的方法:

Reindex API可以用于将多类型索引转换为单类型索引。以下示例适用于Elasticsearch 5.6或Elasticsearch 6.x。在6.x中,无需指定index.mapping.single_type,因为它是默认设置。

按文档类型创建索引:

首先将twitter索引分成tweets索引和users索引:

PUT users
{
  "settings": {
    "index.mapping.single_type": true
  },
  "mappings": {
    "_doc": {
      "properties": {
        "name": {
          "type": "text"
        },
        "user_name": {
          "type": "keyword"
        },
        "email": {
          "type": "keyword"
        }
      }
    }
  }
}

PUT tweets
{
  "settings": {
    "index.mapping.single_type": true
  },
  "mappings": {
    "_doc": {
      "properties": {
        "content": {
          "type": "text"
        },
        "user_name": {
          "type": "keyword"
        },
        "tweeted_at": {
          "type": "date"
        }
      }
    }
  }
}

POST _reindex
{
  "source": {
    "index": "twitter",
    "type": "user"
  },
  "dest": {
    "index": "users",
    "type": "_doc"
  }
}

POST _reindex
{
  "source": {
    "index": "twitter",
    "type": "tweet"
  },
  "dest": {
    "index": "tweets",
    "type": "_doc"
  }
}

自定义类型字段:

下一个示例添加了自定义类型字段并将其设置为原始_type的值。还将类型添加到_id中,以防有冲突的ID具有不同的类型的文档:

PUT new_twitter
{
  "mappings": {
    "_doc": {
      "properties": {
        "type": {
          "type": "keyword"
        },
        "name": {
          "type": "text"
        },
        "user_name": {
          "type": "keyword"
        },
        "email": {
          "type": "keyword"
        },
        "content": {
          "type": "text"
        },
        "tweeted_at": {
          "type": "date"
        }
      }
    }
  }
}

POST _reindex
{
  "source": {
    "index": "twitter"
  },
  "dest": {
    "index": "new_twitter"
  },
  "script": {
    "source": """
      ctx._source.type = ctx._type;
      ctx._id = ctx._type + '-' + ctx._id;
      ctx._type = '_doc';
    """
  }
}

7.0版本的无类型API:

在Elasticsearch 7.0中,每个API都支持无类型请求,指定类型将产生弃用警告。

无类型API即使目标索引包含自定义类型也能正常工作。例如,如果索引具有自定义类型名称my_type,我们可以使用无类型的索引调用添加文档,并使用无类型的获取调用加载文档。

索引API:

索引创建、索引模板和映射API支持一个新的include_type_name URL参数,该参数指定请求和响应中的映射定义是否包含类型名称。该参数在6.8版本中默认为true,以匹配使用映射中的类型名称的7.0之前的行为。在7.0版本中,默认为false,并将在8.0版本中移除。为了避免在6.8中出现弃用警告,可以在6.8版本中显式设置该参数为truefalse。在7.0中,设置include_type_name参数将产生弃用警告。

文档API:

在7.0中,文档API必须使用{index}/_doc路径进行调用,以实现自动生成的_id,以及{index}/_doc/{id}以指定显式的id

搜索API:

在调用搜索API(如_search_msearch_explain)时,不应在URL中包含类型。此外,在查询、聚合或脚本中不应再使用_type字段。

响应中的类型:

文档和搜索API将继续在响应中返回_type键,以避免破坏响应解析。但该键被视为弃用,不再应引用。在8.0版本中,将完全从响应中删除类型。

请注意,当使用弃用的类型API时,索引的映射类型将正常返回,但无类型API将在响应中始终返回虚拟类型_doc,即使映

射中的类型不是_doc。如果您需要直接访问索引的映射类型,请避免使用无类型API,而是使用_default_ API,或从节点的_mappings endpoint检索索引的映射。

总之,Elasticsearch在7.0版本中已经不再支持映射类型。原来使用类型的功能现在可以通过单索引多类型、自定义类型字段或无类型的API请求来实现。这些变化使Elasticsearch的数据建模更加灵活和清晰,同时减少了数据稀疏性和字段类型冲突的问题。