在 Elasticsearch 中对多字段进行条件聚合

7

以下是我ES索引中的文档示例:

{ 
    "concepts": [ 
        { 
            "type": "location",
            "entities": [ 
                { "text": "Raleigh" }, 
                { "text": "Damascus" }, 
                { "text": "Brussels" } 
            ] 
        }, 
        { 
            "type": "person", 
            "entities": [ 
                { "text": "Johnny Cash" }, 
                { "text": "Barack Obama" }, 
                { "text": "Vladimir Putin" }, 
                { "text": "John Hancock" } 
            ] 
        }, 
        { 
            "type": "organization", 
            "entities": [ 
                { "text": "WTO" }, 
                { "text": "IMF" }, 
                { "text": "United States of America" } 
            ] 
        } 
    ] 
}

我正在尝试汇总并计算我的一组文档中每个概念实体的频率,特别是某种特定类型的概念实体。假设我只对聚合“位置”类型的概念实体感兴趣。我的聚合桶将是“concepts.entities.text”,但如果“concepts.type”等于“location”,我仅想聚合它们。以下是我的尝试:
{
    "query": {
        // Whatever query
    },
    "aggs": {
        "location_concept_type": {
            "filter": {
                "term": { "concepts.type": "location" }
            },
            "aggs": {
                "entities": {
                    "terms": { "field": "concepts.hits.text" }
                }
            }
        }
    }
}

这样做的问题在于,它会过滤掉聚合中没有任何名为“位置”的概念实体的文档。但对于确实具有“位置”类型概念实体以及其他内容的文档,它会将所有概念实体放入同一个篮子中,而不考虑概念类型。

我还尝试通过以下方式重新构建我的文档:

{ 
    "concepts": [ 
        { 
            "type": "location",
            "text": "Raleigh"
        },
        { 
            "type": "location",
            "text": "Damascus"
        },
        { 
            "type": "location",
            "text": "Brussels"
        }, 
        { 
            "type": "person",
            "text": "Johnny Cash"
        },
        { 
            "type": "person",
            "text": "Barack Obama"
        }
        { 
            "type": "person",
            "text": "Vladimir Putin"
        }
        { 
            "type": "person",
            "text": "John Hancock"
        }, 
        { 
            "type": "organization",
            "text": "WTO" 
        },
        { 
            "type": "organization",
            "text": "IMF" 
        },
        { 
            "type": "organization",
            "text": "United States of America" 
        }
    ] 
}

但是这也行不通。最终我不能将概念类型作为键(我相信它会解决我的问题),因为我还需要能够在所有概念类型上进行聚合查询(而潜在的概念类型数量可能无限且变化)。有任何想法怎么继续吗?提前感谢你的帮助。

似乎与此问题有关:https://dev59.com/71sX5IYBdhLWcg3wRNjK - arbazkhan002
2个回答

8
如果您按以下方式构建索引:
{ 
    "concepts": [ 
        { 
            "type": "location",
            "text": "Raleigh"
        },
        { 
            "type": "location",
            "text": "Damascus"
        }
    ]
}

如果您在映射中将"concepts"字段定义为一个嵌套对象,您可以应用以下搜索,将过滤聚合嵌套在嵌套聚合中:
{
    "query": {
        "match_all": {}
    },
    "aggs": {
        "location_entities": {
            "nested": { "path": "concepts" }
        },
        "aggs": {
            "filtered_aggregation": {
                "filter": { "term": { "concepts.type": "location" } },
                "aggs": {
                    "my_aggregation": {
                        "terms": { "field": "concepts.text" }
                    }
                }
            }
        }
    }
}

在响应中,你知道你只得到位置实体。这种方法比其他答案中的“hack”要快得多。
从1.0.4Beta1版本开始,Elasticsearch提供过滤聚合。用filters聚合替换嵌套聚合中的filter聚合,可以按实体类型对聚合进行分组。

1

我找到了一个解决方法,有点像是hack。我会将其作为答案放在这里,但请随意添加更优雅的替代方案。我的做法是在“type”和“text”旁边添加一个属性,称之为“text_exp”,它将类型和文本组合如下:

{
    "concepts": [
        { "type": "location", "text": "Raleigh", "text_exp": "location~Raleigh" },
        //...
    ]
}

然后我在terms聚合中使用正则表达式,如下所示。假设我只想聚合类型为“location”的实体:

{
    "query": {
        // Whatever query
    },
    "aggs": {
        "location_entities": {
            "terms": { 
                "field": "concepts.text_exp",
                "include": "location~.*"
            }
        }
    }
}

然后在响应中,我只是按“~”拆分并取右侧部分。

1
就此而言,你的“hack”是elasticsearch开发人员用于多字段聚合的推荐方法:https://github.com/elasticsearch/elasticsearch/issues/5100#issuecomment-51841812 - Shane

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