元数据字段

edit

每个文档都有与之关联的元数据,例如 _index_id 元数据字段。在创建映射时,可以自定义其中一些元数据字段的行为。

身份元数据字段

edit

_index

该文档所属的索引。

_id

文档的ID。

文档源元数据字段

edit
_source
表示文档正文的原始JSON。
_size
_source 字段的大小(以字节为单位),由 mapper-size 插件提供。

文档计数元数据字段

edit
_doc_count
用于存储文档计数时,当文档表示预聚合数据的字段。

索引元数据字段

edit
_field_names
文档中包含非空值的所有字段。
_ignored
在索引时由于 ignore_malformed 而被忽略的文档中的所有字段。

路由元数据字段

edit
_routing
自定义路由值,用于将文档路由到特定分片。

其他元数据字段

edit
_meta
应用程序特定的元数据。
_tier
该文档所属索引的当前数据层偏好。

_doc_count 字段

edit

桶聚合总是返回一个名为 doc_count 的字段,显示每个桶中聚合和分区的文档数量。doc_count 的值的计算非常简单。对于每个桶中收集的每个文档,doc_count 都会增加1。

虽然这种简单的方法在计算单个文档的聚合时是有效的,但它无法准确表示存储预聚合数据的文档(例如 histogramaggregate_metric_double 字段),因为一个摘要字段可能代表多个文档。

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

当字段 _doc_count 被添加到文档中时,所有桶聚合将尊重其值并根据该字段的值增加桶 doc_count。如果文档不包含任何 _doc_count 字段,则默认隐含 _doc_count = 1

  • 一个_doc_count字段每个文档只能存储一个正整数。不允许嵌套数组。
  • 如果一个文档不包含_doc_count字段,聚合器将增加1,这是默认行为。

示例

edit

以下创建索引 API 请求创建了一个新的索引,并包含以下字段映射:

  • my_histogram,一个用于存储百分位数据的histogram字段
  • my_text,一个用于存储直方图标题的keyword字段
PUT my_index
{
  "mappings" : {
    "properties" : {
      "my_histogram" : {
        "type" : "histogram"
      },
      "my_text" : {
        "type" : "keyword"
      }
    }
  }
}

以下索引 API 请求存储了两个直方图的预聚合数据:histogram_1histogram_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 aggregation

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

_field_names 字段

edit

_field_names 字段用于索引文档中包含除 null 以外的任何值的每个字段的名称。该字段曾被 exists 查询用来查找具有或不具有特定字段的任何非 null 值的文档。

现在,_field_names 字段仅索引那些已禁用 doc_valuesnorms 的字段的名称。对于启用了 doc_valuesnorm 的字段,exists 查询仍然可用,但不会使用 _field_names 字段。

禁用 _field_names

edit

禁用 _field_names 不再可能。它现在默认启用,因为它不再带来曾经有的索引开销。

已移除对禁用 _field_names 的支持。在新索引上使用它将抛出错误。在8.0之前的索引上使用它仍然允许,但会发出弃用警告。

_ignored 字段

edit

_ignored 字段索引并存储了文档中被忽略的每个字段的名称,当文档被索引时,这些字段被忽略。例如,当字段格式错误并且 ignore_malformed 被启用时,或者当 keyword 字段的值超过了其可选的 ignore_above 设置时,或者当达到 index.mapping.total_fields.limit 并且 index.mapping.total_fields.ignore_dynamic_beyond_limit 设置为 true 时。

此字段可以通过 termtermsexists 查询进行搜索,并且作为搜索结果的一部分返回。

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

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

同样,下面的查询会找到所有在索引时被忽略的@timestamp字段的文档:

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

自8.15.0版本起,_ignored 字段也支持聚合操作。 例如,以下查询可以找到所有被忽略的字段:

GET _search
{
  "aggs": {
    "ignored_fields": {
      "terms": {
         "field": "_ignored"
      }
    }
  }
}

_id 字段

edit

每个文档都有一个唯一的_id,该ID被索引,因此可以使用GET APIids查询来查找文档。_id可以在索引时分配,或者由Elasticsearch生成一个唯一的_id。此字段在映射中不可配置。

在诸如 termtermsmatchquery_string 的查询中,_id 字段的值是可访问的。

# Example documents
PUT my-index-000001/_doc/1
{
  "text": "Document with ID 1"
}

PUT my-index-000001/_doc/2?refresh=true
{
  "text": "Document with ID 2"
}

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

_id 字段上查询(另请参见 ids 查询

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

_id 的大小限制为512字节,超过此大小的值将被拒绝。

_index 字段

edit

在跨多个索引执行查询时,有时希望添加仅与某些索引的文档相关的查询子句。 _index 字段允许匹配文档所索引到的索引。 它的值可以在某些查询和聚合中访问,并且在排序或脚本编写时也可以使用:

PUT index_1/_doc/1
{
  "text": "Document in index 1"
}

PUT index_2/_doc/2?refresh=true
{
  "text": "Document in index 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 索引中。这意味着你可以在 termterms 查询(或任何重写为 term 查询的查询,例如 matchquery_stringsimple_query_string 查询)中使用 _index 字段,以及 prefixwildcard 查询。然而,它不支持 regexpfuzzy 查询。

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

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

_meta 字段

edit

映射类型可以具有与其关联的自定义元数据。这些元数据完全不由Elasticsearch使用,但可以用于存储特定于应用程序的元数据,例如文档所属的类:

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

这个 _meta 信息可以通过 GET mapping API 获取。

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

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

_routing 字段

edit

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

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": "This is a document"
}

GET my-index-000001/_doc/1?routing=user1 

本文档使用 user1 作为其路由值,而不是其ID。

获取删除更新文档时,需要提供相同的routing值。

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

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

_routing字段上进行查询(另请参阅ids查询

数据流不支持自定义路由,除非它们是在模板中启用了allow_custom_routing设置的情况下创建的。

使用自定义路由进行搜索

edit

自定义路由可以减少搜索的影响。与其将搜索请求分发到索引中的所有分片,请求可以仅发送到与特定路由值(或多个值)匹配的分片:

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

此搜索请求将仅在关联于 user1user2 路由值的分片上执行。

使路由值成为必需

edit

在使用自定义路由时,在索引获取删除更新文档时,提供路由值非常重要。

忘记路由值可能会导致文档被索引到多个分片上。作为一种安全措施,可以配置_routing字段,使自定义的routing值成为所有CRUD操作的必需项:

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

PUT my-index-000002/_doc/1 
{
  "text": "No routing value provided"
}

所有文档都需要路由。

此索引请求抛出一个 routing_missing_exception

使用自定义路由的唯一ID

edit

当索引文档指定自定义的_routing时,_id在整个索引的所有分片中并不保证唯一性。实际上,如果使用不同的_routing值进行索引,具有相同_id的文档可能会最终位于不同的分片上。

确保索引中的ID唯一性是用户的责任。

路由到索引分区

edit

可以配置索引,使得自定义路由值将指向分片的一个子集,而不是单个分片。这有助于在减少搜索影响的同时,降低集群不平衡的风险。

这是通过在索引创建时提供索引级别设置 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

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

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

_source 字段

edit

The _source 字段包含在索引时传递的原始 JSON 文档主体。_source 字段本身未被索引(因此不可搜索),但它被存储以便在执行 fetch 请求时返回,例如 getsearch

如果磁盘使用对你很重要,那么请考虑以下选项:

  • 使用合成_source,它在检索时重建源内容,而不是将其存储在磁盘上。这减少了磁盘使用,但代价是GetSearch查询中访问_source的速度较慢。
  • 完全禁用_source字段。这减少了磁盘使用,但禁用了依赖于_source的功能。

合成 _source

edit

合成 _source 仅对 TSDB 索引(将 index.mode 设置为 time_series 的索引)正式发布。对于其他索引,合成 _source 处于技术预览阶段。技术预览中的功能可能会在未来的版本中更改或移除。Elastic 将努力修复任何问题,但技术预览中的功能不受官方 GA 功能支持 SLA 的约束。

尽管非常方便,但源字段占用了磁盘上相当大的空间。与其完全按照发送的方式将源文档存储在磁盘上,Elasticsearch可以在检索时即时重建源内容。通过将索引设置index.mapping.source.mode的值设置为synthetic来启用此功能:

PUT idx
{
  "settings": {
    "index": {
      "mapping": {
        "source": {
          "mode": "synthetic"
        }
      }
    }
  }
}

虽然这种即时重建通常比直接保存源文档并在查询时加载它们要慢,但它节省了大量的存储空间。当不需要时,可以通过不在查询中加载_source字段来避免额外的延迟。

支持的字段

edit

所有字段类型都支持合成 _source。根据实现细节,字段类型在使用合成 _source 时具有不同的属性。

大多数字段类型使用现有数据构建合成的_source,最常见的是doc_values存储字段。对于这些字段类型,不需要额外的空间来存储_source字段的内容。由于doc_values的存储布局,生成的_source字段与原始文档相比会经历修改

对于所有其他字段类型,字段的原始值按原样存储,与非合成模式下的_source字段存储方式相同。在这种情况下,没有进行任何修改,_source中的字段数据与原始文档中的数据相同。同样,使用ignore_malformedignore_above的错误格式字段值也需要按原样存储。这种方法的存储效率较低,因为除了用于索引字段所需的其他数据(如doc_values)外,还需要存储用于_source重建的数据。

合成 _source 限制

edit

某些字段类型有额外的限制。这些限制在字段类型的文档合成 _source部分中有详细说明。

合成 _source 修改

edit

当启用了合成 _source 时,检索到的文档与原始 JSON 相比会经历一些修改。

数组移动到叶子字段
edit

合成的 _source 数组被移动到叶子节点。例如:

PUT idx/_doc/1
{
  "foo": [
    {
      "bar": 1
    },
    {
      "bar": 2
    }
  ]
}

将变为:

{
  "foo": {
    "bar": [1, 2]
  }
}

这可能导致某些数组消失:

PUT idx/_doc/1
{
  "foo": [
    {
      "bar": 1
    },
    {
      "baz": 2
    }
  ]
}

将变为:

{
  "foo": {
    "bar": 1,
    "baz": 2
  }
}
字段按映射命名
edit

合成源名称字段,如它们在映射中的命名。当与动态映射一起使用时,默认情况下,名称中带有点 (.) 的字段会被解释为多个对象,而字段名称中的点在禁用子对象的对象中会被保留。例如:

PUT idx/_doc/1
{
  "foo.bar.baz": 1
}

将变为:

{
  "foo": {
    "bar": {
      "baz": 1
    }
  }
}

这影响了在脚本中如何引用源内容。例如,以原始源形式引用脚本将返回null:

"script": { "source": """  emit(params._source['foo.bar.baz'])  """ }

相反,源引用需要与映射结构保持一致:

"script": { "source": """  emit(params._source['foo']['bar']['baz'])  """ }

或简单地

"script": { "source": """  emit(params._source.foo.bar.baz)  """ }

以下字段API是首选的,因为除了对映射结构不敏感外,它们还会在可用时使用doc值,并且仅在需要时才回退到合成源。这减少了源合成,这是一个缓慢且代价高昂的操作。

"script": { "source": """  emit(field('foo.bar.baz').get(null))   """ }
"script": { "source": """  emit($('foo.bar.baz', null))   """ }
按字母顺序排序
edit

合成的 _source 字段按字母顺序排序。 JSON RFC 将对象定义为 “一个由零个或多个名称/值对组成的无序集合”,因此应用程序不应该关心顺序,但在没有合成 _source 的情况下,原始顺序被保留,一些应用程序可能会违反规范,根据顺序执行某些操作。

范围的表示
edit

范围字段值(例如 long_range)始终在两侧都包含边界,并相应地调整边界。请参阅示例

减少的geo_point值的精度
edit

字段 geo_point 的值在合成的 _source 中以降低的精度表示。请参阅 示例

最小化源代码修改
edit

可以通过额外的存储成本来避免对特定对象或字段进行合成源修改。 这是通过参数 synthetic_source_keep 控制的,具有以下选项:

  • none: 合成源与原始源如上所述不同(默认)。
  • arrays: 相应字段或对象的数组保留原始元素顺序和重复元素。 此类数组的合成源片段不保证与原始源完全匹配,例如数组 [1, 2, [5], [[4, [3]]], 5] 可能按原样显示或以等效格式显示,如 [1, 2, 5, 4, 3, 5]。确切格式 可能会在未来发生变化,以减少此选项的存储开销。
  • all: 记录单例实例和相应字段或对象数组的源。当 应用于对象时,捕获所有子对象和子字段的源。此外,捕获数组的原始源并按原样显示在合成源中。

例如:

PUT idx_keep
{
  "settings": {
    "index": {
      "mapping": {
        "source": {
          "mode": "synthetic"
        }
      }
    }
  },
  "mappings": {
    "properties": {
      "path": {
        "type": "object",
        "synthetic_source_keep": "all"
      },
      "ids": {
        "type": "integer",
        "synthetic_source_keep": "arrays"
      }
    }
  }
}
PUT idx_keep/_doc/1
{
  "path": {
    "to": [
      { "foo": [3, 2, 1] },
      { "foo": [30, 20, 10] }
    ],
    "bar": "baz"
  },
  "ids": [ 200, 100, 300, 100 ]
}

返回原始源,不进行数组去重和排序:

{
  "path": {
    "to": [
      { "foo": [3, 2, 1] },
      { "foo": [30, 20, 10] }
    ],
    "bar": "baz"
  },
  "ids": [ 200, 100, 300, 100 ]
}

捕获数组源的选项可以在索引级别应用,通过将index.mapping.synthetic_source_keep设置为arrays。这适用于索引中的所有对象和字段,除了那些明确将synthetic_source_keep设置为none的对象和字段。在这种情况下,存储开销会随着每个文档源中存在的数组的数量和大小自然增长。

支持无存储开销的合成源的字段类型

edit

以下字段类型支持使用来自doc_values或存储字段的数据合成源,并且不需要额外的存储空间来构建_source字段。

如果您启用了ignore_malformedignore_above设置,那么需要额外的存储空间来存储这些类型中被忽略的字段值。

禁用_source字段

edit

尽管非常方便,但源字段确实会在索引中产生存储开销。出于这个原因,可以按如下方式禁用它:

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

在禁用_source字段之前请三思

用户常常在未考虑后果的情况下禁用_source字段,然后为此感到后悔。如果_source字段不可用,那么许多功能将不受支持:

  • updateupdate_by_queryreindex API。
  • 在 Kibana Discover 应用程序中,字段数据将不会显示。
  • 即时 高亮
  • 能够从一个 Elasticsearch 索引重新索引到另一个索引,无论是为了更改映射或分析,还是为了将索引升级到新的主要版本。
  • 通过查看索引时使用的原始文档来调试查询或聚合的能力。
  • 可能在将来,自动修复索引损坏的能力。

如果磁盘空间是一个问题,可以增加 压缩级别 而不是禁用 _source

_source中包含/排除字段

edit

一个仅限专家使用的功能是能够在文档被索引之后、但在存储_source字段之前,修剪_source字段的内容。

_source 中移除字段与禁用 _source 有类似的缺点,特别是你无法将文档从一个 Elasticsearch 索引重新索引到另一个索引。建议考虑使用 source filtering 替代。

可以使用includes/excludes参数(也接受通配符),如下所示:

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

PUT logs/_doc/1
{
  "requests": {
    "count": 10,
    "foo": "bar" 
  },
  "meta": {
    "name": "Some metric",
    "description": "Some metric description", 
    "other": {
      "foo": "one", 
      "baz": "two" 
    }
  }
}

GET logs/_search
{
  "query": {
    "match": {
      "meta.other.foo": "one" 
    }
  }
}

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

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

_tier 字段

edit

在跨多个索引执行查询时,有时希望针对位于给定数据层节点上的索引(data_hotdata_warmdata_colddata_frozen)。 _tier 字段允许匹配文档所索引到的索引的 tier_preference 设置。 首选值在某些查询中是可访问的:

PUT index_1/_doc/1
{
  "text": "Document in index 1"
}

PUT index_2/_doc/2?refresh=true
{
  "text": "Document in index 2"
}

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

_tier 字段上进行查询

通常,查询会使用terms查询来列出感兴趣的层级,但您可以在任何重写为term查询的查询中使用_tier字段,例如matchquery_stringtermtermssimple_query_string查询,以及prefixwildcard查询。然而,它不支持regexpfuzzy查询。

索引的 tier_preference 设置是一个以逗号分隔的层级名称列表,按优先顺序排列,即托管索引的首选层级首先列出,后面可能跟随许多备用选项。查询匹配仅考虑第一个优先级(列表的第一个值)。