【问题标题】:How to use term-aggregations in elastic search for OR-options如何在 OR 选项的弹性搜索中使用术语聚合
【发布时间】:2014-09-21 18:41:14
【问题描述】:

假设我有以下数据:

car1: { color: blue, brand: mercedes }
car2: { color: blue, brand: toyota }
car3  { color: red,  brand: mercedes }
car4: { color: red,  brand: toyota }
car5: { color: green,brand: toyota }

“颜色”的聚合给了我{ blue: 2, red: 2, green: 1}

“品牌”的聚合给了我{ mercedes: 2, toyota: 3 }

到目前为止还不错。

如果我搜索“color = blue”并在过滤后进行聚合,我最终会得到:

聚合“颜色”给我{ blue: 2 } // 仅适用于蓝色汽车

聚合“品牌”给我{ mercedes: 1, toyota: 1 } // 仅适用于蓝色汽车

在我的搜索中,我可能会为每个属性选择多个方面,因此搜索“蓝色”或“红色”或“绿色”汽车会得到所有记录。

所以我想在过滤相应属性之前获取每个聚合值,但在其他属性之后,所以我也可以看到其他数量。

此处不适用后置过滤器(循环引用)。

我真的必须触发多个查询(针对主查询和每个聚合)吗?

或者我是否必须在后过滤之前使用所有聚合并将除对应的所有过滤器再次放入每个聚合中(filter-aggr.)?

任何帮助表示赞赏!


[A]
没有过滤器

Color:
  ( ) blue: 2
  ( ) red: 2
  ( ) green: 1
Brand:
  ( ) toyota: 3
  ( ) mercedes: 2

[B]
过滤器:颜色=蓝色

Color:
  (x) blue: 2
Brand:
  ( ) toyota: 1
  ( ) mercedes: 1

[C]
过滤器:颜色=蓝色

Color:
  (x) blue: 2
  ( ) red: 2
  ( ) green: 1
Brand:
  ( ) toyota: 1
  ( ) mercedes: 1

【问题讨论】:

  • 无论用户搜索什么,您的目标是保持聚合或方面的全球性吗(上面的示例 C)?或者让您的聚合按用户选择的方面进行过滤(上面的示例 B)?
  • 不完全是全局的 .. 过滤通常会影响 facet-values。但是过滤不应该影响每个过滤属性本身的方面。过滤颜色应该会影响品牌价值,但不会影响颜色价值 - 反之亦然。

标签: elasticsearch filter aggregation facets


【解决方案1】:

根据您想要的上述示例功能,这里有一些查询可以帮助您实现目标。

例如 B: 此查询只会为您提供整个查询的聚合。第一次在页面加载时运行,查询部分中将没有术语,因此您将获得颜色和品牌的完整汇总列表。如果用户选择了蓝色方面,则查询将仅返回与该方面匹配的文档。这意味着从返回的集合生成的聚合将与示例 B 中的构面列表匹配。

POST /cars/car/_search
{
    "query": {
        "term": {
           "color": {
              "value": "blue"
           }
        }
    }, 
    "aggs" : {
        "colors" : {
            "terms" : { "field" : "color" }
        },
        "brands" : {
            "terms" : { "field" : "brand" } 
        }
    }
}

示例 C: 对于示例 C,我相信您的目标是所有潜在方面都应该可见,即使在执行搜索之后也是如此。为此,您需要创建一个聚合,该聚合使用全局上下文来获取所有文档,而不仅仅是查询返回的文档。在此示例中,所有搜索都将返回颜色和品牌方面的完整列表。

POST /cars/car/_search
{
    "query": {
        "term": {
           "color": {
              "value": "blue"
           }
        }
    }, 
    "aggs" : {
        "all_cars" : {
            "global": {},
            "aggs" : {
                "colors" : {
                    "terms" : { "field" : "color" }
                },
                "brands" : {
                    "terms" : { "field" : "brand" }
                }
            }
        }
    }
}

如果您的目标是让用户始终看到所有可用的方面,请确保您的查询根据用户选择的方面的数量和类型自行调整。

更新:根据下面的评论,这将结合两个选项。您可以通过这种方式获得完整的未过滤聚合和过滤聚合。您必须根据用户的操作以编程方式决定在显示中使用哪个构面列表。

POST /cars/car/_search
{
    "query": {
        "term": {
           "color": {
              "value": "blue"
           }
        }
    }, 
    "aggs" : {
        "colors" : {
            "terms" : { "field" : "color" }
        },
        "brands" : {
            "terms" : { "field" : "brand" } 
        },
        "all_cars" : {
                "global": {},
                "aggs" : {
                    "colors" : {
                        "terms" : { "field" : "color" }
                    },
                    "brands" : {
                        "terms" : { "field" : "brand" }
                }
            }
        }
    }
}

更新 2:您还可以嵌套聚合,以便您可以通过过滤器预先计算各个方面。

POST /cars/car/_search
{
    "aggs" : {
        "colors" : {
            "terms" : { "field" : "color" },
            "aggs" : {
                "brandsByColor" : {
                    "terms" : { "field" : "brand" }
                }
            }
        },
        "brands" : {
            "terms" : { "field" : "brand" },
            "aggs" : {
                "colorsByBrand" : {
                    "terms" : { "field" : "color" }
                }
            }
        }
    }
}

【讨论】:

  • 不完全是,品牌过滤应该影响颜色的方面值,反之亦然......你明白吗?
  • 所以,如果我理解的话,您希望分面选择影响其他分面列表。因此,如果客户端在颜色中选择“蓝色”,它应该过滤品牌,但颜色方面列表应该仍然是完整列表。
  • 正是 .. 所以品牌的聚合需要针对颜色过滤的结果,而颜色的聚合需要针对品牌过滤的结果.. 但是由于您只能“查询 -> 过滤器 -> 聚合 -> 后过滤器”我不知道如何达到预期的效果。
  • 使用更新的搜索查询。它将使您可以访问完整的未过滤聚合和过滤聚合。然后根据用户的操作只显示你需要的。
  • “更新的搜索查询”是什么意思——这是 ES 的一个特性吗?
【解决方案2】:

我遇到了同样的问题并设法解决了。

您需要 ElasticSearch 1.4 来执行此操作,它是通过过滤器聚合和“后过滤器”实现的。 在 1.4 之前,我使用过滤查询来执行此操作以获得正确的聚合计数,并试图通过“global: {}, filter: {}”聚合来实现这一点。就像上面的答案一样。问题是全局 {} 聚合会忽略查询本身,如果您在过滤的同时进行全文搜索,它将不起作用。

现在是 1.4。您所要做的就是运行正常的未过滤查询并对其应用后过滤器。对于聚合,您使用过滤聚合,您可以在其中应用所有后过滤器,除了应用聚合的字段上的那个。

因此,在您的示例中,您将对颜色和品牌都有一个 AND 后置过滤器。在颜色聚合中,您将拥有品牌过滤器,而在品牌聚合中,您将拥有颜色过滤器。

就我而言,我有 company_id 和 subject 过滤器。以前我会这样做,但它只适用于 match_all 查询。

POST /cars/car/_search
{
    "size": "20",
    "from": "0",
    "aggs": {
        "company_id": {
            "terms": {
                "field": "company_id",
                "size": 10
            }
        },
        "global_company": {
            "global": {},
            "aggs": {
                "company_id_3": {
                    "filter": {
                        "term": {
                            "subjects": "710"
                        }
                    },
                    "aggs": {
                        "company_id_2": {
                            "terms": {
                                "field": "company_id"
                            }
                        }
                    }
                }
            }
        }
    },
    "sort": [
        {
            "_score": "desc"
        }
    ],
    "query": {
        "filtered": {
            "query": {
                "match_all": {}
            },
            "filter": {
                "and": [
                    {
                        "term": {
                            "company_id": "1215"
                        }
                    },
                    {
                        "term": {
                            "subjects": "710"
                        }
                    }
                ]
            }
        }
    }
}

现在我这样做了,它适用于所有人:

POST /cars/car/_search
{
    "size": "20",
    "from": "0",
    "aggs": {
        "company_id": {
            "terms": {
                "field": "company_id",
                "size": 10
            }
        },
        "global_company": {
            "global": {},
            "aggs": {
                "company_id_3": {
                    "filter": {
                        "term": {
                            "subjects": "710"
                        }
                    },
                    "aggs": {
                        "company_id_2": {
                            "terms": {
                                "field": "company_id"
                            }
                        }
                    }
                }
            }
        }
    },
    "sort": [
        {
            "_score": "desc"
        }
    ],
    "query": {
        "match_all": {}
    },
    "filter": {
        "and": [
            {
                "term": {
                    "company_id": "1215"
                }
            },
            {
                "term": {
                    "subjects": "710"
                }
            }
        ]
    }
}

您可以看到我如何仅在 company_id_3 聚合上应用了其他过滤器。在这里你可以用任何你喜欢的查询替换 match_all 查询,你仍然会得到你需要的 OR 逻辑

【讨论】:

  • 认为这个答案比 rclement 的答案更准确地解决了原始问题(但它们仍然非常有用且信息丰富)。
【解决方案3】:

或者我必须在后过滤之前使用所有聚合并将所有 在每个聚合中再次过滤除对应的 (filter-aggr.)?

这样做。

【讨论】:

    猜你喜欢
    • 2015-08-15
    • 1970-01-01
    • 2020-03-19
    • 1970-01-01
    • 2021-07-03
    • 1970-01-01
    • 2017-09-09
    • 2019-09-10
    • 1970-01-01
    相关资源
    最近更新 更多