[[_fielddata_filtering]] === Fielddata 的过滤

设想我们正在运行一个网站允许用户收听他们喜欢的歌曲。(((“fielddata”, “filtering”)))(((“aggregations”, “fielddata”, “filtering”))) 为了让他们可以更容易的管理自己的音乐库,用户可以为歌曲设置任何他们喜欢的标签,这样我们就会有很多歌曲被附上 rock(摇滚)hiphop(嘻哈)electronica(电音) ,但也会有些歌曲被附上 my_16th_birthday_favorite_anthem 这样的标签。

现在设想我们想要为用户展示每首歌曲最受欢迎的三个标签,很有可能 rock 这样的标签会排在三个中的最前面,而 my_16th_birthday_favorite_anthem 则不太可能得到评级。 尽管如此,为了计算最受欢迎的标签,我们必须强制将这些一次性使用的项加载到内存中。

感谢 fielddata 过滤,我们可以控制这种状况。我们 知道 自己只对最流行的项感兴趣,所以我们可以简单地避免加载那些不太有意思的长尾项:

  1. PUT /music/_mapping/song
  2. {
  3. "properties": {
  4. "tag": {
  5. "type": "string",
  6. "fielddata": { (1)
  7. "filter": {
  8. "frequency": { (2)
  9. "min": 0.01, (3)
  10. "min_segment_size": 500 (4)
  11. }
  12. }
  13. }
  14. }
  15. }
  16. }

<1> fielddata 关键字允许我们配置 fielddata 处理该字段的方式。

<2> frequency 过滤器允许我们基于项频率过滤加载 fielddata。(((“term frequency”, “fielddata filtering based on”)))

<3> 只加载那些至少在本段文档中出现 1% 的项。

<4> 忽略任何文档个数小于 500 的段。

有了这个映射,只有那些至少在 本段 文档中出现超过 1% 的项才会被加载到内存中。我们也可以指定一个 最大 词频,它可以被用来排除 常用 项,比如 stopwords,停用词

这种情况下,词频是按照段来计算的。这是实现的一个限制:fielddata 是按段来加载的,所以可见的词频只是该段内的频率。但是,这个限制也有些有趣的特性:它可以让受欢迎的新项迅速提升到顶部。

比如一个新风格的歌曲在一夜之间受大众欢迎,我们可能想要将这种新风格的歌曲标签包括在最受欢迎列表中,但如果我们倚赖对索引做完整的计算获取词频,我们就必须等到新标签变得像 rockelectronica )一样流行。由于频度过滤的实现方式,新加的标签会很快作为高频标签出现在新段内,也当然会迅速上升到顶部。

min_segment_size 参数要求 Elasticsearch 忽略某个大小以下的段。如果一个段内只有少量文档,它的词频会非常粗略没有任何意义。小的分段会很快被合并到更大的分段中,某一刻超过这个限制,将会被纳入计算。

[TIP]

通过频次来过滤项并不是唯一的选择,我们也可以使用正则式来决定只加载那些匹配的项。例如,我们可以用 regex 过滤器 (((“regex filtering”))) 处理 twitte 上的消息只将以 # 号开始的标签加载到内存中。

这假设我们使用的分析器会保留标点符号,像 whitespace 分析器。

Fielddata 过滤对内存使用有 巨大的 影响,权衡也是显而易见的:我们实际上是在忽略数据。但对于很多应用,这种权衡是合理的,因为这些数据根本就没有被使用到。内存的节省通常要比包括一个大量而无用的长尾项更为重要。