post-过滤器和分面搜索的全局聚合之间有什么区别?

What differs between post-filter and global aggregation for faceted search?

搜索界面中的一个常见问题是您想 return 选择结果, 但可能想要 return 有关所有文件的信息。 (例如,我想看到所有的红色衬衫,但想知道什么 其他颜色可用)。

这有时称为 "faceted results",或者 "faceted navigation"。 example from the Elasticsearch reference 非常清楚地解释了为什么/如何,所以 我用它作为这个问题的基础。

总结/问题:看起来我可以同时使用 post-filter 或全局聚合。他们俩似乎 以不同的方式提供完全相同的功能。我可能对他们有优点或缺点 没看到?如果可以,我应该使用哪个?

我在下面包含了一个完整的示例,其中包含一些文档和基于示例的两种方法的查询 在参考指南中。


选项 1:post-过滤器

查看 example from the Elasticsearch reference

我们可以做的是在我们的原始查询中获得更多结果,因此我们可以汇总 'on' 这些结果,然后 过滤我们的实际结果。

这个例子解释的很清楚:

But perhaps you would also like to tell the user how many Gucci shirts are available in other colors. If you just add a terms aggregation on the color field, you will only get back the color red, because your query returns only red shirts by Gucci.

Instead, you want to include shirts of all colors during aggregation, then apply the colors filter only to the search results.

在下面的示例代码中查看它的外观。

一个问题是我们不能使用缓存。这是在(5.1 尚不可用)elasticsearch guide 警告:

Performance consideration Use a post_filter only if you need to differentially filter search results and aggregations. Sometimes people will use post_filter for regular searches.

Don’t do this! The nature of the post_filter means it runs after the query, so any performance benefit of filtering (such as caches) is lost completely.

The post_filter should be used only in combination with aggregations, and only when you need differential filtering.

但是还有一个不同的选择:

选项 2:全局聚合

有一种方法可以进行不受搜索查询影响的聚合。 因此,我们不是获取很多,聚合,然后过滤,我们只是得到我们过滤的结果,但是聚合 一切。看看at the reference

我们可以得到完全相同的结果。我没有阅读任何关于为此缓存的警告,但似乎最后 我们需要做大约相同数量的工作。所以这可能是唯一的遗漏。

由于我们需要子聚合,它有点复杂(你不能在 globalfilter 相同 'level').

我读到的关于使用此查询的唯一抱怨是,如果您需要这样做,您可能不得不重复自己 对于几个项目。最后我们可以生成大多数查询,所以重复自己对我的用例来说不是什么大问题, 我真的不认为这是一个与 "can not use cache".

相提并论的问题

问题

这两个功能似乎至少有重叠,或者可能提供完全相同的功能。这让我很困惑。 除此之外,我想知道其中一个或另一个是否具有我没有看到的优势,以及这里是否有任何最佳实践?

例子

这主要来自 post-filter reference page, but I added the global filter 查询。

地图和文件

PUT /shirts
{
    "mappings": {
        "item": {
            "properties": {
                "brand": { "type": "keyword"},
                "color": { "type": "keyword"},
                "model": { "type": "keyword"}
            }
        }
    }
}

PUT /shirts/item/1?refresh
{
    "brand": "gucci",
    "color": "red",
    "model": "slim"
}

PUT /shirts/item/2?refresh
{
    "brand": "gucci",
    "color": "blue",
    "model": "slim"
}


PUT /shirts/item/3?refresh
{
    "brand": "gucci",
    "color": "red",
    "model": "normal"
}


PUT /shirts/item/4?refresh
{
    "brand": "gucci",
    "color": "blue",
    "model": "wide"
}


PUT /shirts/item/5?refresh
{
    "brand": "nike",
    "color": "blue",
    "model": "wide"
}

PUT /shirts/item/6?refresh
{
    "brand": "nike",
    "color": "red",
    "model": "wide"
}

我们现在要求所有红色 gucci 衬衫(项目 1 和 3),我们为这两件衬衫提供的衬衫类型(修身和普通), 以及 gucci 有哪些颜色(红色和蓝色)。

首先,一个 post 过滤器:获取所有衬衫,聚合红色 gucci 衬衫的型号和 gucci 衬衫的颜色(所有颜色), 和 post- 过滤红色 gucci 衬衫以仅显示结果:(这与示例有点不同,因为我们 尽量让它尽可能接近post过滤器的清晰应用。)

GET /shirts/_search
{
  "aggs": {
    "colors_query": {
      "filter": {
        "term": {
          "brand": "gucci"
        }
      },
      "aggs": {
        "colors": {
          "terms": {
            "field": "color"
          }
        }
      }
    },
    "color_red": {
      "filter": {
        "bool": {
          "filter": [
            {
              "term": {
                "color": "red"
              }
            },
            {
              "term": {
                "brand": "gucci"
              }
            }
          ]
        }
      },
      "aggs": {
        "models": {
          "terms": {
            "field": "model"
          }
        }
      }
    }
  },
  "post_filter": {
    "bool": {
      "filter": [
        {
          "term": {
            "color": "red"
          }
        },
        {
          "term": {
            "brand": "gucci"
          }
        }
      ]
    }
  }
}

我们还可以获取所有红色 gucci 衬衫(我们的原始查询),然后对模型进行全局聚合(对于所有 红色 gucci 衬衫)和颜色(适用于所有 gucci 衬衫)。

GET /shirts/_search
{
  "query": {
    "bool": {
      "filter": [
        { "term": { "color": "red"   }},
        { "term": { "brand": "gucci" }}
      ]
    }
  },
  "aggregations": {
    "color_red": {
      "global": {},
      "aggs": {
        "sub_color_red": {
          "filter": {
            "bool": {
              "filter": [
                { "term": { "color": "red"   }},
                { "term": { "brand": "gucci" }}
              ]
            }
          },
          "aggs": {
            "keywords": {
              "terms": {
                "field": "model"
              }
            }
          }
        }
      }
    },
    "colors": {
      "global": {},
      "aggs": {
        "sub_colors": {
          "filter": {
            "bool": {
              "filter": [
                { "term": { "brand": "gucci" }}
              ]
            }
          },
          "aggs": {
            "keywords": {
              "terms": {
                "field": "color"
              }
            }
          }
        }
      }
    }
  }
}

两者都会 return 相同的信息,第二个只是因为子聚合引入的额外级别而不同。第二个查询看起来有点复杂,但我认为这不是什么大问题。真实世界的查询是由代码生成的,无论如何可能更复杂,它应该是一个很好的查询,如果这意味着复杂,那就这样吧。

在这两种情况下,Elasticsearch 最终都会做同样的事情。如果我必须选择,我想我会使用 global 聚合,这可能会为您节省一些开销,而不必同时为两个 Lucene 收集器提供数据。

我们使用的实际解决方案虽然不是问题的直接答案,但基本上是“两者都不是”。

this elastic blogpost我们得到了初步提示:

Occasionally, I see an over-complicated search where the goal is to do as much as possible in as few search requests as possible. These tend to have filters as late as possible, completely in contrary to the advise in Filter First. Do not be afraid to use multiple search requests to satisfy your information need. The multi-search API lets you send a batch of search requests.

Do not shoehorn everything into a single search request.

这基本上就是我们在上面的查询中所做的:一大堆聚合和一些过滤。

并行使用它们 运行 事实证明要快得多。看看the multi-search API