Elasticsearch - 通用构面结构 - 计算与过滤器相结合的聚合
Elasticsearch - generic facets structure - calculating aggregations combined with filters
在我们的一个新项目中,我们受到这篇文章的启发 http://project-a.github.io/on-site-search-design-patterns-for-e-commerce/#generic-faceted-search 来构建我们的“facet”结构。虽然我已经按照文章描述的程度让它工作,但在 selecting facet 时,我 运行 遇到了让它工作的问题。我希望有人可以给出一些尝试的提示,这样我就不必再次将所有聚合重做为单独的聚合计算。
问题基本上是我们使用单个聚合一次计算所有“方面”,但是当我添加一个过滤器(fx。检查品牌名称)时,它会“删除”所有其他品牌返回聚合时。我基本上想要的是它应该在计算其他方面时使用该品牌作为过滤器,而不是在计算品牌聚合时。这是必要的,例如,用户可以选择多个品牌。
查看https://www.contorion.de/search/Metabo_Fein/ou1-ou2?q=Winkelschleifer&c=bovy(即上文所述的站点),我select编辑了“Metabo”和“Fein”制造商(Hersteller),并展开了Hersteller菜单它显示了所有制造商,而不仅仅是 selected 的制造商。所以我知道这是可能的,我希望有人能提示如何编写聚合/过滤器,所以我得到了 "correct e-commerce facet behavior".
在ES的产品上我有如下结构:(和原文章一样,只是命名上“C#化”了)
"attributeStrings": [
{
"facetName": "Property",
"facetValue": "Organic"
},
{
"facetName": "Property",
"facetValue": "Without parfume"
},
{
"facetName": "Brand",
"facetValue": "Adidas"
}
]
所以上面的产品有 2 个 attributes/facet 组 – 属性 有 2 个值(有机,无香水)和品牌有 1 个值(阿迪达斯)。
在没有任何过滤器的情况下,我根据以下查询计算聚合:
"aggs": {
"agg_attr_strings_filter": {
"filter": {},
"aggs": {
"agg_attr_strings": {
"nested": {
"path": "attributeStrings"
},
"aggs": {
"attr_name": {
"terms": {
"field": "attributeStrings.facetName"
},
"aggs": {
"attr_value": {
"terms": {
"field": "attributeStrings.facetValue",
"size": 1000,
"order": [
{
"_term": "asc"
}
]
} } } } } } } }
现在,如果我 select 属性 "Organic" 和 Brand "Adidas" 我构建相同的聚合,但使用过滤器来应用这两个约束(就是这样有点不对劲...):
"aggs": {
"agg_attr_strings_filter": {
"filter": {
"bool": {
"filter": [
{
"nested": {
"query": {
"bool": {
"filter": [
{
"term": {
"attributeStrings.facetName": {
"value": "Property"
}
}
},
{
"terms": {
"attributeStrings.facetValue": [
"Organic"
]
}
}
]
}
},
"path": "attributeStrings"
}
},
{
"nested": {
"query": {
"bool": {
"filter": [
{
"term": {
"attributeStrings.facetName": {
"value": "Brand"
}
}
},
{
"terms": {
"attributeStrings.facetValue": [
"Adidas"
]
}
}
]
}
},
"path": "attributeStrings"
}
}
]
}
},
"aggs": {
"agg_attr_strings": {
"nested": {
"path": "attributeStrings"
},
"aggs": {
"attr_name": {
"terms": {
"field": "attributeStrings.facetName",
},
"aggs": {
"attr_value": {
"terms": {
"field": "attributeStrings.facetValue",
"size": 1000,
"order": [
{
"_term": "asc"
}
]
} } } } } } } }
我能看到这个模型的唯一方法是计算每个 selected 方面的聚合并以某种方式合并结果。但这似乎非常复杂,有点违背了文章中描述的模型的意义,所以我希望有一个更干净的解决方案,并且有人可以给出一些尝试的提示。
问题是因为您在 Property
和 Organic
聚合中添加过滤器,因此您选择的方面越多,您越会限制您将获得的条款。在那篇文章中,他们使用的 filter
实际上是 post_filter
, both names were allowed until recently, but filter
got removed 因为这会导致歧义。
您需要做的是将该过滤器从聚合外部移动到 post_filter
部分,以便结果可以通过已选择的任何方面正确过滤掉,但您的所有方面仍会得到正确计算在整个文档集上。
{
"post_filter": {
"bool": {
"filter": [
{
"nested": {
"query": {
"bool": {
"filter": [
{
"term": {
"attributeStrings.facetName": {
"value": "Property"
}
}
},
{
"terms": {
"attributeStrings.facetValue": [
"Organic"
]
}
}
]
}
},
"path": "attributeStrings"
}
},
{
"nested": {
"query": {
"bool": {
"filter": [
{
"term": {
"attributeStrings.facetName": {
"value": "Brand"
}
}
},
{
"terms": {
"attributeStrings.facetValue": [
"Adidas"
]
}
}
]
}
},
"path": "attributeStrings"
}
}
]
}
},
"aggs": {
"agg_attr_strings_full": {
"nested": {
"path": "attributeStrings"
},
"aggs": {
"attr_name": {
"terms": {
"field": "attributeStrings.facetName"
},
"aggs": {
"attr_value": {
"terms": {
"field": "attributeStrings.facetValue",
"size": 1000,
"order": [
{
"_term": "asc"
}
]
}
}
}
}
}
},
"agg_attr_strings_filtered": {
"filter": {
"bool": {
"filter": [
{
"nested": {
"path": "attributeStrings",
"query": {
"bool": {
"filter": [
{
"term": {
"attributeStrings.facetName": {
"value": "Property"
}
}
},
{
"terms": {
"attributeStrings.facetValue": [
"Organic"
]
}
}
]
}
}
}
},
{
"nested": {
"path": "attributeStrings",
"query": {
"bool": {
"filter": [
{
"term": {
"attributeStrings.facetName": {
"value": "Brand"
}
}
},
{
"terms": {
"attributeStrings.facetValue": [
"Adidas"
]
}
}
]
}
}
}
}
]
}
},
"aggs": {
"nested": {
"path": "attributeStrings"
},
"aggs": {
"attr_name": {
"terms": {
"field": "attributeStrings.facetName"
},
"aggs": {
"attr_value": {
"terms": {
"field": "attributeStrings.facetValue",
"size": 1000,
"order": [
{
"_term": "asc"
}
]
}
}
}
}
}
}
}
}
}
The only way I can see forward with this model, is to calculate the aggregation for each selected facet and somehow merge the result.
完全正确。如果选择了一个方面(例如 brand),如果您还想获取其他品牌进行多选,则不能使用全局品牌过滤器。您可以做的是在选定方面应用所有 other 过滤器,在非选定方面应用 all filters方面。结果,您将为 n
个选定的过滤器提供 n+1
个单独的聚合 - 第一个用于所有方面,其余的用于选定的方面。
在您的情况下,查询可能如下所示:
{
"aggs": {
"agg_attr_strings_filter": {
"filter": {
"bool": {
"filter": [
{
"nested": {
"query": {
"bool": {
"filter": [
{
"term": {
"attributeStrings.facetName": {
"value": "Property"
}
}
},
{
"terms": {
"attributeStrings.facetValue": [
"Organic"
]
}
}
]
}
},
"path": "attributeStrings"
}
},
{
"nested": {
"query": {
"bool": {
"filter": [
{
"term": {
"attributeStrings.facetName": {
"value": "Brand"
}
}
},
{
"terms": {
"attributeStrings.facetValue": [
"Adidas"
]
}
}
]
}
},
"path": "attributeStrings"
}
}
]
}
},
"aggs": {
"agg_attr_strings": {
"nested": {
"path": "attributeStrings"
},
"aggs": {
"attr_name": {
"terms": {
"field": "attributeStrings.facetName"
},
"aggs": {
"attr_value": {
"terms": {
"field": "attributeStrings.facetValue",
"size": 1000,
"order": [
{
"_term": "asc"
}
]
}
}
}
}
}
}
}
},
"special_agg_property": {
"filter": {
"nested": {
"query": {
"bool": {
"filter": [
{
"term": {
"attributeStrings.facetName": {
"value": "Brand"
}
}
},
{
"terms": {
"attributeStrings.facetValue": [
"Adidas"
]
}
}
]
}
},
"path": "attributeStrings"
}
},
"aggs": {
"special_agg_property": {
"nested": {
"path": "attributeStrings"
},
"aggs": {
"agg_filtered_special": {
"filter": {
"query": {
"match": {
"attributeStrings.facetName": "Property"
}
}
},
"aggs": {
"facet_value": {
"terms": {
"size": 1000,
"field": "attributeStrings.facetValue"
}
}
}
}
}
}
}
},
"special_agg_brand": {
"filter": {
"nested": {
"query": {
"bool": {
"filter": [
{
"term": {
"attributeStrings.facetName": {
"value": "Property"
}
}
},
{
"terms": {
"attributeStrings.facetValue": [
"Organic"
]
}
}
]
}
},
"path": "attributeStrings"
}
},
"aggs": {
"special_agg_brand": {
"nested": {
"path": "attributeStrings"
},
"aggs": {
"agg_filtered_special": {
"filter": {
"query": {
"match": {
"attributeStrings.facetName": "Brand"
}
}
},
"aggs": {
"facet_value": {
"terms": {
"size": 1000,
"field": "attributeStrings.facetValue"
}
}
}
}
}
}
}
}
}
}
这个查询看起来超级大而且可怕,但是生成这样的查询只需要几十行代码就可以完成。
在解析查询结果时,需要先解析通用聚合(使用所有过滤器的聚合),然后再解析特殊的分面聚合。在上面的示例中,首先解析来自 agg_attr_strings_filter
的结果,但这些结果还将包含 Brand 和 属性 的聚合值应该被 special_agg_property
和 special_agg_brand
的聚合值覆盖
此外,此查询非常高效,因为 Elasticsearch 在缓存单独的过滤器子句方面做得很好,因此在查询的不同部分应用相同的过滤器应该很便宜。
But it seems very complex and kind of defeats the point of having the model as described in the article, so I hope there's a more clean solution and someone can give a hint at something to try.
您需要对不同的方面应用不同的过滤器,同时具有不同的查询过滤器,这一事实确实没有办法解决。如果你需要支持 "correct e-commerce facet behavior" 你将有复杂的查询:)
免责声明:我是上述文章的合著者。
在我们的一个新项目中,我们受到这篇文章的启发 http://project-a.github.io/on-site-search-design-patterns-for-e-commerce/#generic-faceted-search 来构建我们的“facet”结构。虽然我已经按照文章描述的程度让它工作,但在 selecting facet 时,我 运行 遇到了让它工作的问题。我希望有人可以给出一些尝试的提示,这样我就不必再次将所有聚合重做为单独的聚合计算。
问题基本上是我们使用单个聚合一次计算所有“方面”,但是当我添加一个过滤器(fx。检查品牌名称)时,它会“删除”所有其他品牌返回聚合时。我基本上想要的是它应该在计算其他方面时使用该品牌作为过滤器,而不是在计算品牌聚合时。这是必要的,例如,用户可以选择多个品牌。
查看https://www.contorion.de/search/Metabo_Fein/ou1-ou2?q=Winkelschleifer&c=bovy(即上文所述的站点),我select编辑了“Metabo”和“Fein”制造商(Hersteller),并展开了Hersteller菜单它显示了所有制造商,而不仅仅是 selected 的制造商。所以我知道这是可能的,我希望有人能提示如何编写聚合/过滤器,所以我得到了 "correct e-commerce facet behavior".
在ES的产品上我有如下结构:(和原文章一样,只是命名上“C#化”了)
"attributeStrings": [
{
"facetName": "Property",
"facetValue": "Organic"
},
{
"facetName": "Property",
"facetValue": "Without parfume"
},
{
"facetName": "Brand",
"facetValue": "Adidas"
}
]
所以上面的产品有 2 个 attributes/facet 组 – 属性 有 2 个值(有机,无香水)和品牌有 1 个值(阿迪达斯)。 在没有任何过滤器的情况下,我根据以下查询计算聚合:
"aggs": {
"agg_attr_strings_filter": {
"filter": {},
"aggs": {
"agg_attr_strings": {
"nested": {
"path": "attributeStrings"
},
"aggs": {
"attr_name": {
"terms": {
"field": "attributeStrings.facetName"
},
"aggs": {
"attr_value": {
"terms": {
"field": "attributeStrings.facetValue",
"size": 1000,
"order": [
{
"_term": "asc"
}
]
} } } } } } } }
现在,如果我 select 属性 "Organic" 和 Brand "Adidas" 我构建相同的聚合,但使用过滤器来应用这两个约束(就是这样有点不对劲...):
"aggs": {
"agg_attr_strings_filter": {
"filter": {
"bool": {
"filter": [
{
"nested": {
"query": {
"bool": {
"filter": [
{
"term": {
"attributeStrings.facetName": {
"value": "Property"
}
}
},
{
"terms": {
"attributeStrings.facetValue": [
"Organic"
]
}
}
]
}
},
"path": "attributeStrings"
}
},
{
"nested": {
"query": {
"bool": {
"filter": [
{
"term": {
"attributeStrings.facetName": {
"value": "Brand"
}
}
},
{
"terms": {
"attributeStrings.facetValue": [
"Adidas"
]
}
}
]
}
},
"path": "attributeStrings"
}
}
]
}
},
"aggs": {
"agg_attr_strings": {
"nested": {
"path": "attributeStrings"
},
"aggs": {
"attr_name": {
"terms": {
"field": "attributeStrings.facetName",
},
"aggs": {
"attr_value": {
"terms": {
"field": "attributeStrings.facetValue",
"size": 1000,
"order": [
{
"_term": "asc"
}
]
} } } } } } } }
我能看到这个模型的唯一方法是计算每个 selected 方面的聚合并以某种方式合并结果。但这似乎非常复杂,有点违背了文章中描述的模型的意义,所以我希望有一个更干净的解决方案,并且有人可以给出一些尝试的提示。
问题是因为您在 Property
和 Organic
聚合中添加过滤器,因此您选择的方面越多,您越会限制您将获得的条款。在那篇文章中,他们使用的 filter
实际上是 post_filter
, both names were allowed until recently, but filter
got removed 因为这会导致歧义。
您需要做的是将该过滤器从聚合外部移动到 post_filter
部分,以便结果可以通过已选择的任何方面正确过滤掉,但您的所有方面仍会得到正确计算在整个文档集上。
{
"post_filter": {
"bool": {
"filter": [
{
"nested": {
"query": {
"bool": {
"filter": [
{
"term": {
"attributeStrings.facetName": {
"value": "Property"
}
}
},
{
"terms": {
"attributeStrings.facetValue": [
"Organic"
]
}
}
]
}
},
"path": "attributeStrings"
}
},
{
"nested": {
"query": {
"bool": {
"filter": [
{
"term": {
"attributeStrings.facetName": {
"value": "Brand"
}
}
},
{
"terms": {
"attributeStrings.facetValue": [
"Adidas"
]
}
}
]
}
},
"path": "attributeStrings"
}
}
]
}
},
"aggs": {
"agg_attr_strings_full": {
"nested": {
"path": "attributeStrings"
},
"aggs": {
"attr_name": {
"terms": {
"field": "attributeStrings.facetName"
},
"aggs": {
"attr_value": {
"terms": {
"field": "attributeStrings.facetValue",
"size": 1000,
"order": [
{
"_term": "asc"
}
]
}
}
}
}
}
},
"agg_attr_strings_filtered": {
"filter": {
"bool": {
"filter": [
{
"nested": {
"path": "attributeStrings",
"query": {
"bool": {
"filter": [
{
"term": {
"attributeStrings.facetName": {
"value": "Property"
}
}
},
{
"terms": {
"attributeStrings.facetValue": [
"Organic"
]
}
}
]
}
}
}
},
{
"nested": {
"path": "attributeStrings",
"query": {
"bool": {
"filter": [
{
"term": {
"attributeStrings.facetName": {
"value": "Brand"
}
}
},
{
"terms": {
"attributeStrings.facetValue": [
"Adidas"
]
}
}
]
}
}
}
}
]
}
},
"aggs": {
"nested": {
"path": "attributeStrings"
},
"aggs": {
"attr_name": {
"terms": {
"field": "attributeStrings.facetName"
},
"aggs": {
"attr_value": {
"terms": {
"field": "attributeStrings.facetValue",
"size": 1000,
"order": [
{
"_term": "asc"
}
]
}
}
}
}
}
}
}
}
}
The only way I can see forward with this model, is to calculate the aggregation for each selected facet and somehow merge the result.
完全正确。如果选择了一个方面(例如 brand),如果您还想获取其他品牌进行多选,则不能使用全局品牌过滤器。您可以做的是在选定方面应用所有 other 过滤器,在非选定方面应用 all filters方面。结果,您将为 n
个选定的过滤器提供 n+1
个单独的聚合 - 第一个用于所有方面,其余的用于选定的方面。
在您的情况下,查询可能如下所示:
{
"aggs": {
"agg_attr_strings_filter": {
"filter": {
"bool": {
"filter": [
{
"nested": {
"query": {
"bool": {
"filter": [
{
"term": {
"attributeStrings.facetName": {
"value": "Property"
}
}
},
{
"terms": {
"attributeStrings.facetValue": [
"Organic"
]
}
}
]
}
},
"path": "attributeStrings"
}
},
{
"nested": {
"query": {
"bool": {
"filter": [
{
"term": {
"attributeStrings.facetName": {
"value": "Brand"
}
}
},
{
"terms": {
"attributeStrings.facetValue": [
"Adidas"
]
}
}
]
}
},
"path": "attributeStrings"
}
}
]
}
},
"aggs": {
"agg_attr_strings": {
"nested": {
"path": "attributeStrings"
},
"aggs": {
"attr_name": {
"terms": {
"field": "attributeStrings.facetName"
},
"aggs": {
"attr_value": {
"terms": {
"field": "attributeStrings.facetValue",
"size": 1000,
"order": [
{
"_term": "asc"
}
]
}
}
}
}
}
}
}
},
"special_agg_property": {
"filter": {
"nested": {
"query": {
"bool": {
"filter": [
{
"term": {
"attributeStrings.facetName": {
"value": "Brand"
}
}
},
{
"terms": {
"attributeStrings.facetValue": [
"Adidas"
]
}
}
]
}
},
"path": "attributeStrings"
}
},
"aggs": {
"special_agg_property": {
"nested": {
"path": "attributeStrings"
},
"aggs": {
"agg_filtered_special": {
"filter": {
"query": {
"match": {
"attributeStrings.facetName": "Property"
}
}
},
"aggs": {
"facet_value": {
"terms": {
"size": 1000,
"field": "attributeStrings.facetValue"
}
}
}
}
}
}
}
},
"special_agg_brand": {
"filter": {
"nested": {
"query": {
"bool": {
"filter": [
{
"term": {
"attributeStrings.facetName": {
"value": "Property"
}
}
},
{
"terms": {
"attributeStrings.facetValue": [
"Organic"
]
}
}
]
}
},
"path": "attributeStrings"
}
},
"aggs": {
"special_agg_brand": {
"nested": {
"path": "attributeStrings"
},
"aggs": {
"agg_filtered_special": {
"filter": {
"query": {
"match": {
"attributeStrings.facetName": "Brand"
}
}
},
"aggs": {
"facet_value": {
"terms": {
"size": 1000,
"field": "attributeStrings.facetValue"
}
}
}
}
}
}
}
}
}
}
这个查询看起来超级大而且可怕,但是生成这样的查询只需要几十行代码就可以完成。
在解析查询结果时,需要先解析通用聚合(使用所有过滤器的聚合),然后再解析特殊的分面聚合。在上面的示例中,首先解析来自 agg_attr_strings_filter
的结果,但这些结果还将包含 Brand 和 属性 的聚合值应该被 special_agg_property
和 special_agg_brand
的聚合值覆盖
此外,此查询非常高效,因为 Elasticsearch 在缓存单独的过滤器子句方面做得很好,因此在查询的不同部分应用相同的过滤器应该很便宜。
But it seems very complex and kind of defeats the point of having the model as described in the article, so I hope there's a more clean solution and someone can give a hint at something to try.
您需要对不同的方面应用不同的过滤器,同时具有不同的查询过滤器,这一事实确实没有办法解决。如果你需要支持 "correct e-commerce facet behavior" 你将有复杂的查询:)
免责声明:我是上述文章的合著者。