如何从大量文档中选择ElasticSearch的唯一值?

3

我有一个包含大约1亿条文档的索引。有时候我想检索所有文档,有时候我想按照特定字段进行分组,并选择每个唯一值的一个文档。类似于:

SELECT * FROM documents GROUP BY my_field;

举个例子,有时我想得到:

|id|my_field|thing  |
|--|--------|-------|
|1 |a       |red    |
|2 |b       |yellow |
|3 |b       |green  |
|4 |c       |brown  |

有时候

|id|my_field|thing  |
|--|--------|-------|
|1 |a       |red    |
|3 |b       |green  |
|4 |c       |brown  |

ElasticSearch文档建议使用Terms Aggregation。然而,有人建议这仅适用于聚合术语的小基数,即“buckets”的数量较少:

有时唯一术语太多,无法在单个请求/响应对中处理,因此将分析分解为多个请求可能很有用。

此外,Top Hits aggregation返回的数据格式似乎不容易进行分页。
例如,如果我有一百万个不同的术语,它看起来与预期设计不符。这是正确的吗?
我的替代方案是在自己的代码中维护索引并标记重复项,但这容易出错,容易受到并发问题等影响。
有没有一种方法可以实现我的需求,并发挥ElasticSearch的优势?
3个回答

1
看起来ElasticSearch索引/聚合并不是最好的使用情形。我用另一种方式解决了我的问题——创建一个单独的类型(或索引),存储我的数据的去重视图。我使用唯一对的哈希作为_id字段,这样ElasticSearch自动在文档进入时进行去重。
作为一个额外的功能,使用外部_timestamp允许我选择哪个文档在重复的情况下被存储。

1

现在在Elasticsearch中,您可以使用复合聚合

复合聚合很昂贵。在生产环境中部署复合聚合之前,请对您的应用程序进行负载测试。

一种多桶聚合,从不同来源创建复合桶。

与其他多桶聚合不同,您可以使用复合聚合有效地分页所有多级聚合的桶。此聚合提供了一种流式传输特定聚合的所有桶的方法,类似于滚动对文档所做的操作。

复合桶是从为每个文档提取/创建的值的组合构建的,并且将每个组合视为复合桶。

这可能有点晚回答您的问题,但它可能会帮助其他人。


非常感谢!给你点赞,但是已经过了太长时间来评估你的答案是否解决了我的问题。 - Joe

-1

我认为你的评估是正确的,虽然基数聚合适用于这里,但在遇到不同值的规模时会出现问题。这种类型的聚合 是ElasticSearch中最接近“GROUP BY”的东西。

话虽如此,我认为有一种方法可以实现

仅选择每个唯一值的一个文档。

通过嵌套在“Should”父级中的多个查询来实现。

您的方法将根据您是否仅针对聚合计数或返回值而有所不同。

我的初步想法是将您的请求视为嵌套在should中的一组兄弟查询。该方法的问题在于基于匹配的大多数字段返回,而不是每个不同查询的第一个文档。据我所知,“should”查询没有任何类型的得分,可以为每个存储桶返回单个匹配项。

如果目标是返回文档,则我认为最适合执行multi-search

POST _msearch
{"index":"INDEX"}
{"query":{"match": {"a": "red"}},"size":1}
{"index":"INDEX"}
{"query":{"match": {"b": "yellow"}},"size":1}
{"index":"INDEX"}
{"query":{"match": {"b": "green"}},"size":1}

以上代码将返回与每个查询匹配的单个文档,给您一个包含“result”对象数组的响应。

原始计数:

POST /INDEX/_search
{
  "size": 0, 
  "aggs": {
    "a_red": {
      "filter": {
        "term": {
          "a": {
            "value": "red"
          }
        }
      }
    },        
    "b_yellow": {
      "filter": {
        "term": {
          "b": {
            "value": "yellow"
          }
        }
      }
    },        
    "b_green": {
      "filter": {
        "term": {
          "b": {
            "value": "green"
          }
        }
      }
    }
  }
}

注意:上面示例中的聚合桶名称(例如:“a_red”)仅用于清晰起见,不是必需的。

谢谢您的回答。为了更清晰地说明,在我的规模下,我认为这需要返回所有100万个不同值的my_field,然后进行一个有100万个元素的多搜索吗?我认为这基本上不符合ElasticSearch的预期用途。 - Joe
根据您展示的数据结构,我同意您的观点(假设基数在这么多不同值的情况下会失败)。如果您能够调整内容摄取方式,我相信您可能会获得更好的结果。您是否考虑过将数据规范化为仅包含您感兴趣的值(和引用字段)的一组文档?采用这种方法,您可以包括一个脚本,将一个文档插入到另一种类型或索引中,该索引填充了每个基数值的文档、脚本递增的计数器和参考文档列表。 - Miek
是的,正如我在问题中所说的,我可以维护自己的索引,但这可能会导致并发问题,因此我正在尝试使用本地ElasticSearch方法进行调查。然而,我认为这是我的唯一选择。 - Joe

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接