【问题标题】:How to filter hits by sub-aggregated results in Elasticsearch如何在 Elasticsearch 中按子聚合结果过滤命中
【发布时间】:2018-11-13 03:17:38
【问题描述】:

我一直在实施基于 elasticsearch 的事件溯源解决方案。文档代表状态变化事件,由_source 上的id 字段链接。有一个从0 开始的sequence 字段,因此每个id 的最高序列是该id 的最新事件。在实践中,附加数据将仅在第一个事件中可用,后续事件将仅包含已更改的字段。目标是拥有一个我永远不必向其发送更新的索引,只需插入。

尝试创建一个查询,该查询将返回按其 ID 分组的第一个和最后一个事件,前提是仅当其最新事件的 statusREADY 匹配时。

样本数据:

[  
    {  
        "_index":"events",
        "_type":"event",
        "_id":"AWcFf2N-IqNGd75vWMgc",
        "_score":1,
        "_source":{  
            "id":"event_chain-1",
            "status":"SENT",
            "sequence":1,
            "timestamp":"1541985493824",
            "export_batch_id":"103709fe-959f-4b4e-8255-ef59f18a3cf6"
        }
    },
    {  
        "_index":"events",
        "_type":"event",
        "_id":"AWbQomwoIqNGd75vWMf6",
        "_score":1,
        "_source":{  
            "id":"event_chain-1",
            "status":"READY",
            "sequence":"0",
            "timestamp":"2018-10-31T00:00:00Z"
        }
    },
    {  
        "_index":"events",
        "_type":"event",
        "_id":"AWbQomwoIqNGd75vWabc",
        "_score":1,
        "_source":{  
            "id":"event_chain-2",
            "status":"READY",
            "sequence":"0",
            "timestamp":"2018-10-31T00:00:00Z"
        }
    }
]

我在 id.keyword 字段上编写了一个 terms 聚合,以及两个 top_hits 子聚合,通过对序列进行排序并分别抓取顶部和底部的结果来获取第一个和最新的事件。

问题是我对状态所做的任何匹配都发生在聚合之前,我需要一种方法从术语聚合结果中排除所有最新事件的状态与READY 不匹配的匹配。

到目前为止我所拥有的:

POST /events/_search
{
    "size": 0,
    "query": {
        "bool": {
            "must": {
                "match": {
                    "status": "READY"
                }
            }
        }
    },
    "aggs": {
        "group_by_id": {
            "terms": {
                "field": "id.keyword",
                "order": {
                    "_term": "asc"
                },
                "size": 100
            },
            "aggs": {
                "latest_event": {
                    "top_hits": {
                        "sort": [
                            {
                                "sequence": {
                                    "order": "desc"
                                }
                            }
                        ],
                        "from": 0,
                        "size": 1
                    }
                },
                "first_event": {
                    "top_hits": {
                        "sort": [
                            {
                                "sequence": {
                                    "order": "asc"
                                }
                            }
                        ],
                        "from": 0,
                        "size": 1
                    }
                }
            }
        },
        "num_ready": {
            "cardinality": {
                "field": "id.keyword"
            }
        }
    }
}

这将返回两个术语,一个用于event_chain-1,一个用于event_chain-2,而我只想要一个用于event_chain-2

Terms agg size 是这样的,所以这个查询可以在预定的批次中运行,总是抓取结果的顶部并更新链,这样它们就不会出现在下一个查询中。

【问题讨论】:

  • 与 Elasticsearch 无关,但我的应用程序每个聚合的事件数通常少于 100,因此我们只需将它们全部加载并随后过滤。
  • 这个写的很重,按计划阅读,所以我们的批次必须是可调整的并且在数据方面相当小(这使得查询后的过滤可能难以实现,因为没有 garauntee 你会如果过滤和未过滤的总和大于批量大小,则获得每批次的任何预期命中)。

标签: elasticsearch event-sourcing


【解决方案1】:

我对此进行了深入研究并试图查看它。我认为这归结为单个聚合的局限性。无法对 top_hits 进行子聚合,因此我需要其他方法来过滤返回的结果。

我最终发现有人在做类似的事情:https://rahulsinghai.blogspot.com/2016/07/elasticsearch-pipeline-bucket-selector.html

输入组合top_hitsmax 以查找max 序列每个idfilter 聚合在同一级别,然后在filter 聚合上找到另一个max 聚合以查找@ 987654330@ 序列每个id 仅针对状态为READY 的每个结果,假设共享一个id 的所有事件在READY 状态下至少有一个事件,然后使用bucket_selector 聚合来选择基于@987654335 的相关集@ 和 filter 结果。

潜在解决方案:

POST /events/_search
{
    "size": 0,
    "aggs": {
        "grouped_by_id": {
            "terms": {
                "field": "id.keyword",
                "size": 100,
                "order": {"max_seq":"desc"}
            },
            "aggs": {
                "max_seq": {"max":{"field":"sequence"}},
                "latest_event": {
                    "top_hits": {
                        "sort": [{"sequence":{"order":"desc"}}],
                        "from": 0,
                        "size": 1
                    }
                },
                "first_event": {
                    "top_hits": {
                        "sort": [{"sequence":{"order":"asc"}}],
                        "from": 0,
                        "size": 1
                    }
                },
                "filters": {
                    "filter": {"bool":{"must":[{"match":{"status":"READY"}}]}},
                    "aggs": {
                        "latest_ready_seq": {"max":{"field":"sequence"}}
                    }
                },
                "should_we_consider": {
                    "bucket_selector": {
                        "buckets_path": {
                            "latest_seq": "max_seq",
                            "latest_ready_seq": "filters>latest_ready_seq"
                        },
                        "script": "params.latest_seq == params.latest_ready_seq"
                    }
                }
            }
        }
    }
}

【讨论】:

    猜你喜欢
    • 2015-10-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-17
    • 1970-01-01
    • 1970-01-01
    • 2019-12-02
    • 2020-02-06
    相关资源
    最近更新 更多