重要文本聚合编辑

一种聚合,用于返回集合中有趣的或不常见的自由文本术语的出现次数。它类似于重要术语聚合,但不同之处在于

  • 它专门设计用于类型为 text 的字段
  • 它不需要字段数据或文档值
  • 它会动态地重新分析文本内容,这意味着它还可以过滤掉嘈杂文本中重复的部分,否则这些部分往往会扭曲统计数据。

重新分析*大型*结果集将需要大量时间和内存。建议将 significant_text 聚合用作采样器多样化采样器聚合的子聚合,以将分析限制在*少量*最匹配的文档中,例如 200 个。这通常会提高速度、内存使用率和结果质量。

示例用例

  • 当用户搜索“禽流感”时,建议使用“H5N1”来帮助扩展查询
  • 建议使用与股票代码 $ATI 相关的关键字,用于自动新闻分类器

在这些情况下,选择的词不仅仅是结果中最流行的词。最流行的词往往非常无聊(*和、的、我们、我、他们*……)。重要的词是在*前景*和*背景*集之间测量的流行度发生了显著变化的词。如果术语“H5N1”在 1000 万个文档的索引中仅存在于 5 个文档中,但在构成用户搜索结果的 100 个文档中却发现了 4 个,那么这将非常重要,并且可能与他们的搜索非常相关。5/10,000,000 与 4/100 的频率差异很大。

基本用法编辑

在典型的用例中,感兴趣的*前景*集是针对查询的最匹配搜索结果的选择,而用于统计比较的*背景*集是从中收集结果的索引。

示例

response = client.search(
  index: 'news',
  body: {
    query: {
      match: {
        content: 'Bird flu'
      }
    },
    aggregations: {
      my_sample: {
        sampler: {
          shard_size: 100
        },
        aggregations: {
          keywords: {
            significant_text: {
              field: 'content'
            }
          }
        }
      }
    }
  }
)
puts response
GET news/_search
{
  "query": {
    "match": { "content": "Bird flu" }
  },
  "aggregations": {
    "my_sample": {
      "sampler": {
        "shard_size": 100
      },
      "aggregations": {
        "keywords": {
          "significant_text": { "field": "content" }
        }
      }
    }
  }
}

响应

{
  "took": 9,
  "timed_out": false,
  "_shards": ...,
  "hits": ...,
    "aggregations" : {
        "my_sample": {
            "doc_count": 100,
            "keywords" : {
                "doc_count": 100,
                "buckets" : [
                    {
                        "key": "h5n1",
                        "doc_count": 4,
                        "score": 4.71235374214817,
                        "bg_count": 5
                    }
                    ...
                ]
            }
        }
    }
}

结果表明,“h5n1”是与禽流感密切相关的几个术语之一。它在我们的整个索引中只出现了 5 次(参见 bg_count),但其中 4 个很幸运地出现在我们 100 个“禽流感”结果样本中。这表明了一个重要的词,用户可以将其添加到他们的搜索中。

使用 filter_duplicate_text 处理噪声数据编辑

自由文本字段通常包含原始内容和机械复制文本的混合(剪切粘贴的传记、电子邮件回复链、转发、样板页眉/页脚、页面导航菜单、侧边栏新闻链接、版权声明、标准免责声明、地址)。

在现实世界的数据中,如果不对这些重复的文本部分进行过滤,它们往往会在 significant_text 结果中占据很大比例。过滤近似重复的文本在索引时是一项艰巨的任务,但我们可以在查询时使用 filter_duplicate_text 设置动态地清理数据。

首先,让我们看一个未经过滤的真实示例,使用Signal 媒体数据集,其中包含一百万篇涵盖各种新闻的新闻文章。以下是搜索提及“elasticsearch”的文章的原始重要文本结果

{
  ...
  "aggregations": {
    "sample": {
      "doc_count": 35,
      "keywords": {
        "doc_count": 35,
        "buckets": [
          {
            "key": "elasticsearch",
            "doc_count": 35,
            "score": 28570.428571428572,
            "bg_count": 35
          },
          ...
          {
            "key": "currensee",
            "doc_count": 8,
            "score": 6530.383673469388,
            "bg_count": 8
          },
          ...
          {
            "key": "pozmantier",
            "doc_count": 4,
            "score": 3265.191836734694,
            "bg_count": 4
          },
          ...

}

未经清理的文档出现了一些看起来很奇怪的术语,从表面上看,这些术语在统计上与我们的搜索词“elasticsearch”的出现相关,例如“pozmantier”。我们可以深入研究这些文档的示例,看看为什么 pozmantier 与使用此查询连接在一起

response = client.search(
  index: 'news',
  body: {
    query: {
      simple_query_string: {
        query: '+elasticsearch  +pozmantier'
      }
    },
    _source: [
      'title',
      'source'
    ],
    highlight: {
      fields: {
        content: {}
      }
    }
  }
)
puts response
GET news/_search
{
  "query": {
    "simple_query_string": {
      "query": "+elasticsearch  +pozmantier"
    }
  },
  "_source": [
    "title",
    "source"
  ],
  "highlight": {
    "fields": {
      "content": {}
    }
  }
}

结果显示了一系列关于多个技术项目的评审小组的非常相似的新闻文章

{
  ...
  "hits": {
    "hits": [
      {
        ...
        "_source": {
          "source": "Presentation Master",
          "title": "T.E.N. Announces Nominees for the 2015 ISE® North America Awards"
        },
        "highlight": {
          "content": [
            "City of San Diego Mike <em>Pozmantier</em>, Program Manager, Cyber Security Division, Department of",
            " Janus, Janus <em>ElasticSearch</em> Security Visualization Engine "
          ]
        }
      },
      {
        ...
        "_source": {
          "source": "RCL Advisors",
          "title": "T.E.N. Announces Nominees for the 2015 ISE(R) North America Awards"
        },
        "highlight": {
          "content": [
            "Mike <em>Pozmantier</em>, Program Manager, Cyber Security Division, Department of Homeland Security S&T",
            "Janus, Janus <em>ElasticSearch</em> Security Visualization Engine"
          ]
        }
      },
      ...

Mike Pozmantier 是评审小组的众多评委之一,elasticsearch 被用于众多评审项目之一。

通常情况下,这篇冗长的新闻稿被各种新闻网站剪切粘贴,因此,它们包含的任何罕见姓名、数字或拼写错误在统计上都与我们的匹配查询相关。

幸运的是,相似的文档往往排名也相似,因此,作为检查最匹配文档流的一部分,significant_text 聚合可以应用过滤器来删除已经看到的任何 6 个或更多标记的序列。现在让我们尝试使用相同的查询,但打开 filter_duplicate_text 设置

response = client.search(
  index: 'news',
  body: {
    query: {
      match: {
        content: 'elasticsearch'
      }
    },
    aggregations: {
      sample: {
        sampler: {
          shard_size: 100
        },
        aggregations: {
          keywords: {
            significant_text: {
              field: 'content',
              filter_duplicate_text: true
            }
          }
        }
      }
    }
  }
)
puts response
GET news/_search
{
  "query": {
    "match": {
      "content": "elasticsearch"
    }
  },
  "aggs": {
    "sample": {
      "sampler": {
        "shard_size": 100
      },
      "aggs": {
        "keywords": {
          "significant_text": {
            "field": "content",
            "filter_duplicate_text": true
          }
        }
      }
    }
  }
}

对于熟悉 Elastic Stack 的人来说,分析去重文本的结果显然质量更高

{
  ...
  "aggregations": {
    "sample": {
      "doc_count": 35,
      "keywords": {
        "doc_count": 35,
        "buckets": [
          {
            "key": "elasticsearch",
            "doc_count": 22,
            "score": 11288.001166180758,
            "bg_count": 35
          },
          {
            "key": "logstash",
            "doc_count": 3,
            "score": 1836.648979591837,
            "bg_count": 4
          },
          {
            "key": "kibana",
            "doc_count": 3,
            "score": 1469.3020408163263,
            "bg_count": 5
          }
        ]
      }
    }
  }
}

由于复制粘贴操作或其他形式的机械重复,Pozmantier 先生和其他与 elasticsearch 的一次性关联不再出现在聚合结果中。

如果可以通过单个值的索引字段(可能是文章 title 文本的哈希值或 original_press_release_url 字段)识别重复或近似重复的内容,那么使用父级多样化采样器聚合根据该单个键从样本集中消除这些文档将更有效。从性能方面来说,提供给 significant_text 聚合的重复内容越少越好。

限制编辑

不支持子聚合编辑

significant_text 聚合有意不支持添加子聚合,因为

  • 这将带来很高的内存成本
  • 这不是一个普遍有用的功能,并且对于需要它的人来说有一个解决方法

候选词的数量通常非常多,在返回最终结果之前会对这些词进行大量修剪。支持子聚合将产生额外的波动,并且效率低下。客户端始终可以从 significant_text 请求中获取经过大量修剪的结果集,并使用带有 include 子句和子聚合的 terms 聚合进行后续查询,以便以更有效的方式对选定的关键字进行进一步分析。

不支持嵌套对象编辑

significant_text 聚合目前也不能与嵌套对象中的文本字段一起使用,因为它使用的是文档 JSON 源。在给定匹配的 Lucene 文档 ID 的情况下,这使得该功能在从存储的 JSON 中匹配嵌套文档时效率低下。

近似计数编辑

结果中提供的包含某个术语的文档数量是基于对每个分片返回的样本求和得出的,因此可能是

  • 如果某些分片在其顶部样本中没有提供给定术语的数字,则该数字较低
  • 在考虑背景频率时,该数字较高,因为它可能会统计已删除文档中出现的次数

与大多数设计决策一样,这是一个权衡的基础,我们选择以牺牲一些(通常很小)的准确性为代价来提供快速的性能。但是,下一节中介绍的 sizeshard size 设置提供了一些工具来帮助控制准确性级别。

参数编辑

显著性启发式编辑

此聚合支持与重要术语聚合相同的评分启发式(JLH、mutual_information、gnd、chi_square 等)

大小和分片大小编辑

size 参数可以设置用来定义从整个词条列表中返回多少个词条桶。默认情况下,协调搜索过程的节点将请求每个分片提供其自己的顶级词条桶,一旦所有分片都响应,它将把结果缩减为最终列表,然后返回给客户端。如果唯一词条的数量大于 size,则返回的列表可能会略有不准确(词条计数可能会略有不准确,甚至可能没有返回应该位于顶级 size 桶中的词条)。

为了确保更高的准确性,最终 size 的倍数将用作从每个分片请求的词条数量 (2 * (size * 1.5 + 10))。要手动控制此设置,可以使用 shard_size 参数来控制每个分片生成的候选词条的数量。

在合并所有结果后,低频词条可能会变成最有趣的词条,因此当 shard_size 参数设置为明显高于 size 设置的值时,significant_terms 聚合可以产生更高质量的结果。这确保了在最终选择之前,归约节点会对更大数量的有希望的候选词条进行合并审查。显然,大型候选词条列表会导致额外的网络流量和 RAM 使用量,因此需要权衡质量/成本。如果 shard_size 设置为 -1(默认值),则将根据分片数量和 size 参数自动估算 shard_size

shard_size 不能小于 size(因为这没有多大意义)。如果是这样,Elasticsearch 将覆盖它并将其重置为等于 size

最小文档计数编辑

可以使用 min_doc_count 选项仅返回匹配次数超过配置次数的词条。默认值为 3。

得分较高的词条将在分片级别收集,并在第二步中与从其他分片收集的词条合并。但是,分片没有关于全局词条频率的可用信息。是否将词条添加到候选列表的决定仅取决于使用本地分片频率(而不是单词的全局频率)在分片上计算的得分。仅在合并所有分片的本地词条统计信息后,才会应用 min_doc_count 标准。在某种程度上,决定将该词条添加为候选词条是在没有非常 *确定* 该词条是否真的会达到所需的 min_doc_count 的情况下做出的。如果低频但得分高的词条填充了候选列表,这可能会导致最终结果中缺少许多(全局)高频词条。为了避免这种情况,可以增加 shard_size 参数以允许分片上有更多候选词条。但是,这会增加内存消耗和网络流量。

shard_min_doc_count编辑

参数 shard_min_doc_count 控制分片在将词条实际添加到候选列表时,相对于 min_doc_count 的 *确定性*。仅当词条在集合中的本地分片频率高于 shard_min_doc_count 时,才会考虑这些词条。如果您的字典包含许多低频词条,并且您对这些词条不感兴趣(例如拼写错误),那么您可以设置 shard_min_doc_count 参数以在分片级别过滤掉即使在合并本地计数后也合理确定不会达到所需 min_doc_count 的候选词条。shard_min_doc_count 默认设置为 0,除非您显式设置它,否则它不起作用。

通常不建议将 min_doc_count 设置为 1,因为它往往会返回拼写错误或其他奇怪的词条。找到一个词条的多个实例有助于强化这样一个事实:虽然仍然很少见,但该词条并非一次性事故的结果。默认值 3 用于提供最低的证据权重。将 shard_min_doc_count 设置得太高会导致在分片级别过滤掉重要的候选词条。此值应设置为远低于 min_doc_count/#shards

自定义背景上下文编辑

背景词条频率的统计信息的默认来源是整个索引,并且可以通过使用 background_filter 来缩小此范围,以关注较窄上下文中的重要词条

response = client.search(
  index: 'news',
  body: {
    query: {
      match: {
        content: 'madrid'
      }
    },
    aggregations: {
      tags: {
        significant_text: {
          field: 'content',
          background_filter: {
            term: {
              content: 'spain'
            }
          }
        }
      }
    }
  }
)
puts response
GET news/_search
{
  "query": {
    "match": {
      "content": "madrid"
    }
  },
  "aggs": {
    "tags": {
      "significant_text": {
        "field": "content",
        "background_filter": {
          "term": { "content": "spain" }
        }
      }
    }
  }
}

上面的过滤器将有助于关注马德里市特有的词条,而不是像“西班牙语”这样的词条,这些词条在整个索引的全球上下文中并不常见,但在包含“西班牙”一词的文档子集中很常见。

使用背景过滤器会减慢查询速度,因为必须过滤每个词条的 postings 以确定频率

处理源和索引映射编辑

通常,索引字段名称和正在检索的原始 JSON 字段共享相同的名称。但是,对于使用 copy_to 等功能的更复杂的字段映射,源 JSON 字段和正在聚合的索引字段可能会有所不同。在这些情况下,可以使用 source_fields 参数列出将从中分析文本的 JSON _source 字段

response = client.search(
  index: 'news',
  body: {
    query: {
      match: {
        custom_all: 'elasticsearch'
      }
    },
    aggregations: {
      tags: {
        significant_text: {
          field: 'custom_all',
          source_fields: [
            'content',
            'title'
          ]
        }
      }
    }
  }
)
puts response
GET news/_search
{
  "query": {
    "match": {
      "custom_all": "elasticsearch"
    }
  },
  "aggs": {
    "tags": {
      "significant_text": {
        "field": "custom_all",
        "source_fields": [ "content", "title" ]
      }
    }
  }
}

过滤值编辑

可以(尽管很少需要)过滤将为其创建桶的值。这可以使用 includeexclude 参数来完成,这些参数基于正则表达式字符串或精确词条数组。此功能反映了词条聚合文档中描述的功能。