本地英文版地址: ../en/search-aggregations-bucket-significanttext-aggregation.html
一种聚合,返回集合中有趣或不寻常的自定义文本的聚合。 它类似于significant_terms聚合,但不同之处在于:
-
它是专门设计用于
text
类型的字段的 - 它不需要字段数据或文档值
- 它可以动态地重新分析文本内容,这意味着它还可以过滤噪音文本的重复部分,否则这些重复部分会影响统计数据。
重新分析大结果集将需要大量的时间和内存。 建议将 significant_text 聚合用作 sampler 或 diversified_sampler 聚合的子元素,以将分析限制在一小部分最匹配的文档中,例如200个。 这通常会提高速度、内存使用和结果的质量。
用例示例:
- 当用户搜索“禽流感”时,提示“H5N1”以扩展查询
- 在自动新闻分类器中使用的与股票代码 $ATI 相关的建议关键字
在这些情况下,被选择的单词不仅仅是结果中最流行的词项。 最流行的词往往很无聊(and, of, the, we, I, they …)。 重要的单词是在前景(foreground)和背景(background)集之间受欢迎程度发生了显著变化的词项。 如果词项“H5N1”仅存在于1000万个文档索引中的5个文档中,但是在构成用户搜索结果的100个文档中的4个中出现,则这是重要的并且可能与他们的搜索非常相关。 频率 5/10,000,000 与 4/100 相比差别很大。
在典型的用例中,感兴趣的前景集是对查询的最佳匹配搜索结果的选择,而用于统计比较的背景集则是从中收集结果的一个或多个索引。
示例:
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”是与“bird flu”(禽流感)密切相关的几个词项之一。
作为一个整体,它在我们的索引中只出现了5次(见bg_count
),但其中4次幸运地出现在我们的100份“bird flu”结果样本中。
这表明了它是一个重要的词,用户可以潜在地添加到他们的搜索中去。
自定义字段通常包含原始内容和文本的机械副本(剪切和粘贴传记、电子邮件回复链、转发、样板页眉/页脚、页面导航菜单、侧栏新闻链接、版权声明、标准免责声明、地址)。
在真实世界的数据中,如果不过滤掉这些重复的文本部分,它们往往会在significant_text
结果中占据重要位置。
在索引时过滤近似重复的文本是一项困难的任务,但是我们可以使用filter_duplicate_text
设置在查询时即时清理数据。
首先,让我们来看一个未经过滤的真实世界的例子,它使用了涵盖各种新闻的一百万篇新闻文章的Signal media数据集。 以下是搜索提及“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":
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 个或更多个词元(token)的序号。
现在让我们来尝试同样的查询,但是打开filter_duplicate_text
设置:
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
字段)来识别,那么使用父diversified_sampler聚合来从基于该单个关键字的样本集中消除这些文档会更有效。
从性能的角度来看,你可以预先提供给 significant_text 聚合的重复内容越少,性能就越好。
significant_text 聚合故意不支持添加子聚合,因为:
- 这将带来很高的内存成本
- 这不是一个普遍有用的功能,对于那些需要它的人有一个变通的方法
候选词的数量通常非常大,并且在返回最终结果之前会被大量删减。
支持子聚合会产生额外的波动,而且效率低下。
客户端总是可以从一个significant_text
请求中获得大量精简的结果集,并使用带有include
子句的terms
聚合和子聚合进行后续查询,从而以更高效的方式对选定的关键字进行进一步分析。
significant_text 聚合目前还不能用于嵌套对象中的 text 字段,因为它适用于文档 JSON 源。 在给定一个匹配的 Lucene docID(文档ID)的情况下,当从存储的 JSON 中匹配嵌套文档时,这使得这个特性效率很低。
这种聚合支持与significant_terms聚合相同的评分试探法(JLH、交互信息、gnd、卡方等)
可以设置参数size
来定义应该从整个词项列表中返回多少个词项桶。
默认情况下,协调搜索过程的节点将请求每个分片提供各自的前几个词项桶,一旦所有分片做出响应,它将把结果缩减为最终列表,然后返回给客户端。
如果唯一的词项的数量大于size
,则返回的列表可能稍有偏差且不准确(可能是词项计数稍有偏差,甚至可能是本应该是前size
个桶中的词项没有返回)。
为了确保更好的准确性,使用最终size
的倍数作为从每个分片请求的词项数(2 * (size * 1.5 + 10)
)。
为了手动控制这个设置,可以使用参数shard_size
来控制每个分片产生的候选词项的数量。
所有结果合并后,低频词项可能会成为最有趣的词项,因此当参数shard_size
设置为明显高于size
设置的值时,significant_terms聚合可以生成更高质量的结果。
这确保了在最终选择之前,缩减节点会对大量有希望的候选词项进行综合审查。
很明显,大的候选词项列表将导致额外的网络流量和内存使用,因此这是需要平衡的质量/成本权衡。
如果shard_size
设置为-1(默认值),那么shard_size
将根据分片数量和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
设置为1
,除非你显式设置它,否则它不起作用。
通常不建议将min_doc_count
设置为1
,因为它会返回拼写错误或其他奇怪的词。
找到一个词项的不止一个实例有助于强化这一点,尽管这种情况仍然很少见,但这个词项不是一次性事故的结果。
默认值 3 用于提供最小证据权重。
shard_min_doc_count
设置得太高会导致重要的候选词项在分片级别被过滤掉。
该值应设置为远低于 min_doc_count/#shards
。
背景词项频率的统计信息的默认来源是整个索引,并且可以通过使用background_filter
来缩小该范围,以便在更窄的上下文中聚焦于重要的词项:
GET news/_search { "query" : { "match" : { "content" : "madrid" } }, "aggs" : { "tags" : { "significant_text" : { "field" : "content", "background_filter": { "term" : { "content" : "spain"} } } } } }
上述过滤器将有助于集中在 Madrid 市特有的词项上,而不是显示诸如“Spanish”的词项,这些词项在完整索引的全局上下文中是不常见的,但是在包含单词“Spain”的文档子集中是常见的。
使用背景过滤器会降低查询速度,因为必须过滤每个词的帖子来确定频率
通常,索引的字段名和被检索的原始 JSON 字段共享相同的名称。
然而,对于使用copy_to
源 JSON 字段和被聚合的索引字段等特性的更复杂的字段映射,可能会有所不同。
在这些情况下,可以使用参数source_fields
列出从中分析文本的 JSON _source 字段:
GET news/_search { "query" : { "match" : { "custom_all" : "elasticsearch" } }, "aggs" : { "tags" : { "significant_text" : { "field" : "custom_all", "source_fields": ["content" , "title"] } } } }
可以(尽管很少需要)过滤将为其创建桶的值。
这可以使用基于正则表达式字符串或精确项数组的include
和exclude
参数来完成。
此功能反映了terms聚合文档中描述的功能。