fielddata(字段数据)

所有字段是默认被 indexed(被索引的),这使得它们是可搜索的.可以在脚本中排序,聚合和获取字段值,但是需要不同的搜索模式.

搜索需要回答一个问题 “哪个 document(文档) 包含这个 term(词条)”,然而排序和聚合需要回答一个不同的问题 " 这个字段在这个 document(文档)中的值是多少?".

许多字段可以使用 index-time,在磁盘上的 doc_values 支持这种数据访问模式, 但是 text 字段不支持 doc_values

相反,text 字段使用查询时存在于内存的数据结构 fielddata.这个数据结构是第一次将字段用于聚合,排序,或者脚本时基于需求构建的。它是通过读取磁盘上的每个 segment(片段)的整个反向索引来构建的,将 term(词条)和 document(文档)关系反转,并将结果存储在内存中,在JVM的堆中.

text 字段默认关闭 Fielddata

Fielddata 会消耗很多堆空间,尤其是加载高基数的 text 字段的时候.一旦 fielddata 加载到堆中,它在  segment(片段)中的生命周期还是存在的.此外,加载  fielddata 是一件非常昂贵的过程,会导致用户体验到延迟的感觉.这就是为什么 fielddata 默认关闭.

如果你尝试对文本字段上的脚本进行排序,访问值,你会看到此异常:

  • Field data text 字段上默认是关闭的.在 [your_field_name]上设置 fielddata = true,以便通过反转反向索引来加载内存中的 fielddata。 请注意,这可能会使用显着的内存。

在开启fielddata之前

在你开启 fielddata  之前,考虑一下为什么你要在脚本中使用  text 来聚合,排序.通常这么做是没有意义的.

在索引之前分析 text 字段,以至于像 New York 这样的值可以通过 new 或者  york 来搜索.当你可能想要一个称作  New York  的单一bucket(桶), term(词条)在这个字段上聚合会返回一个 new bucket和一个 york bucket(桶).

相反,你应该使用 text 字段进行全文搜索,以及一个开启 doc_valuesunanalyzed(未分析) keyword  字段用于聚合,如下:

curl -XPUT 'localhost:9200/my_index?pretty' -H 'Content-Type: application/json' -d'
{
  "mappings": {
    "my_type": {
      "properties": {
        "my_field": {  # 1
          "type": "text",
          "fields": {
            "keyword": {  # 2
              "type": "keyword"
            }
          }
        }
      }
    }
  }
}
' 

| 1 | 使用 my_field 用于搜索 | | 2 | 使用 my_field.keyword 用于聚合,排序,或者脚本. |

开启text 字段的fielddata

你可以使用以下的 PUT mapping API  给一个已经存在的 text 字段开启 fielddata.

curl -XPUT 'localhost:9200/my_index/_mapping/my_type?pretty' -H 'Content-Type: application/json' -d'
{
  "properties": {
    "my_field": {  # 1
      "type":     "text",
      "fielddata": true
    }
  }
}
'

| 1 | 你为 my_field 指定的映射应包含该字段已有的映射,再加上 fielddata 参数. |

建议

fielddata.* 参数必须在相同索引的相同名称的字段有相同的设置.可以使用 PUT mapping API  在现有字段上更新其值.

Global ordinals

Global ordinals(全局序数)是一个基于 field datadoc_values 的数据结构,它以字典顺序每户每个唯一 term(词根)的增量编号.每个 term(词根)都有一个唯一的数,term(词根)A是低于 term(词根)B的.Global ordinals (全局序数)仅在 textkeyword 字段中支持.

Fielddatadoc_values 也有 ordinals(序数),它是特定segment(段)和字段中所有term(词根)的唯一编号.Global ordinals(全局序数)只是建立在 fielddatadoc_values之上,通过在 segement ordinalsglobal ordinals 之间提供映射,后者在整个shard(分片)中是唯一的.

Global ordinals 是用于使用 segement ordinals(片段序数)的功能,例如排序和 terms(词根)聚合,以提高执行时间.term(词根)聚合完全依赖于 Global ordinals(全局序数)来执行 shard (分片)级别的聚合,然后将global ordinals (全局序数)转换为真正的 term(词根),term(词根)仅用于最终减少阶段,其结合不同 shard(分片)的结果.

指定字段的 Global ordinals (全局序数)与 shard(分片)所有的字段相关联,而 field datadoc_values 与单个 segment(片段)相关联.其与针对单个segment(段)相关联的特定字段的字段数据不同,一旦新的 segment(片段)变得可见, Global ordinals(全局序数)就需要完全重建.

Global ordinals(全局序数)取决于一个字段上的 terms(词根)数量,但通常它是比较低的.因为源字符安数据已经被加载.Global ordinals(全局序数)的内存开销很小,因为它被有效的压缩.

fielddata_frequency_filter

Fielddata 过滤可以用于减少加载到内存中 term(词根)数量,从而减少内存使用.term(词根)可以按频率来过滤 :

频率过滤器允许你仅加载 document(文档)频率在最小和最大值之间的 term(词根),可以表示为绝对数字(当数字大于1.0时)或百分比(例如0.01为1%,1.0为100%).每个segment计算频率.百分比是基于 docs(文档)的数量,而不是该 segment(片段)的所有 docs(文档).

通过使用 min_segment_size 指定 segment(片段)应包含的文档和最小数量,可以完全排除  small segment (小的片段),如下:

curl -XPUT 'localhost:9200/my_index?pretty' -H 'Content-Type: application/json' -d'
{
  "mappings": {
    "my_type": {
      "properties": {
        "tag": {
          "type": "text",
          "fielddata": true,
          "fielddata_frequency_filter": {
            "min": 0.001,
            "max": 0.1,
            "min_segment_size": 500
          }
        }
      }
    }
  }
}
'