elasticsearch – 分面搜索的后过滤和全局聚合有何不同?

搜索界面中的一个常见问题是您希望返回一系列结果,

但可能想要返回有关所有文档的信息. (例如,我想看到所有的红色衬衫,但想知道什么

其他颜色可供选择).

这有时被称为“分面结果”,或
“分面导航”. example from the Elasticsearch reference在解释原因/方式方面非常清楚
我用它作为这个问题的基础.

摘要/问题:看起来我可以同时使用后过滤器或全局聚合.他们似乎都是
以不同的方式提供完全相同的功能.我可能有优点或缺点
看不到?如果是这样,我应该使用哪个?

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

选项1:后过滤器

example from the Elasticsearch reference

我们可以做的是在我们的原始查询中获得更多结果,因此我们可以聚合“开启”这些结果,然后再进行聚合
过滤我们的实际结果.

这个例子很清楚地解释了它:

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

我们可以得到完全相同的结果.我没有阅读有关缓存的任何警告,但最终似乎是这样
我们需要做同样多的工作.所以这可能是唯一的遗漏.

由于我们需要的子聚合(你不能拥有全局和过滤器),这有点复杂
同一水平’).

我读到的关于使用此查询的唯一投诉是,如果您需要这样做,您可能需要重复自己
几个项目.最后我们可以生成大多数查询,所以重复自己对我的用例来说不是那么多问题,
我并不认为这是一个与“无法使用缓存”相同的问题.

似乎两个函数至少重叠,或者可能提供完全相同的功能.这令我感到困惑.
除此之外,我想知道一个或另一个有没有我见过的优势,如果这里有最好的做法?

这主要来自post-filter reference page,但我添加了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有哪些颜色(红色和蓝色).

首先,后过滤器:获得所有衬衫,聚合红色gucci衬衫的模型和gucci衬衫的颜色(所有颜色),
和红色gucci衬衫的后过滤器只显示结果:(这与我们的例子有点不同
尝试尽可能接近明确的后滤波器应用.)

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"
              }
            }
          }
        }
      }
    }
  }
}

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

最佳答案 在这两种情况下,Elasticsearch最终都会做同样的事情.如果我不得不选择,我想我会使用全局聚合,这可能会节省一些开销,因为必须同时提供两个Lucene收集器.

点赞