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

时间:2016-12-22 14:48:53

标签: elasticsearch aggregate faceted-search elasticsearch-5

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

这有时被称为"分面结果"或 "分面导航"。 example from the Elasticsearch reference在解释原因/方式时非常清楚 我已将此作为此问题的基础。

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

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

选项1:后置过滤器

请参阅example from the Elasticsearch reference

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

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

  

但也许您还想告诉用户其他颜色有多少Gucci衬衫可供选择。如果您只是在颜色字段上添加术语聚合,则只会返回红色,因为您的查询仅返回Gucci的红色衬衫。

     

相反,您希望在聚合过程中包含所有颜色的衬衫,然后仅将颜色过滤器应用于搜索结果。

在示例代码中查看下面的内容。

这个问题是我们不能使用缓存。这是({尚未提供给5.1)elasticsearch guide警告:

  

效果考虑   仅当您需要差异过滤搜索结果和聚合时,才使用post_filter。有时人们会使用post_filter进行常规搜索。

     

不要这样做! post_filter的性质意味着它在查询之后运行,因此过滤(例如缓存)的任何性能优势都会完全丢失。

     

post_filter只能与聚合一起使用,并且仅在您需要差异过滤时使用。

但是有一个不同的选择:

选项2:全球汇总

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

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

由于我们需要的子聚合,它有点复杂(你不能拥有globalfilter 相同的'水平')。

我读到的关于使用此查询的唯一投诉是,如果您需要这样做,您可能需要重复自己 几个项目。最后我们可以生成大多数查询,所以重复自己并不是我的用例的问题, 我并不认为这是一个与&#34相同的问题;不能使用缓存"。

问题

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

示例

这主要来自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"
              }
            }
          }
        }
      }
    }
  }
}

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

2 个答案:

答案 0 :(得分:1)

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

答案 1 :(得分:1)

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

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

  

偶尔,我会看到一个过于复杂的搜索,其目标是尽可能少地搜索请求。这些过滤器往往尽可能晚,完全与Filter First中的建议相反。不要害怕使用多个搜索请求来满足您的信息需求。多搜索API允许您发送一批搜索请求。

     

请勿将所有内容整理成一个搜索请求。

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

让它们并行运行证明要快得多。看看the multi-search API