5

搜索界面中的一个常见问题是您希望返回结果的选择,但可能希望返回有关所有文档的信息。(例如,我想查看所有红色衬衫,但想知道还有哪些其他颜色可用)。

这有时被称为“分面结果”或“分面导航”。Elasticsearch 参考中的示例非常清楚地解释了为什么/如何,所以我用它作为这个问题的基础。

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

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


选项 1:后过滤器

请参阅Elasticsearch 参考中的示例

我们可以做的是在我们的原始查询中有更多的结果,所以我们可以聚合这些结果,然后过滤我们的实际结果。

这个例子解释得很清楚:

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

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

请参阅下面的示例代码中的外观。

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

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

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

post_filter 应该仅与聚合结合使用,并且仅在您需要差分过滤时使用。

然而,有一个不同的选择:

选项 2:全局聚合

有一种方法可以进行不受搜索查询影响的聚合。所以不是得到很多,聚合然后过滤,我们只是得到过滤后的结果,但对所有内容进行聚合。看看参考资料

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

global由于我们需要子聚合(你不能拥有和 afilter在同一个“级别”上),所以它稍微复杂一点。

我读到的关于使用此查询的唯一抱怨是,如果您需要对多个项目执行此操作,您可能必须重复自己。最后,我们可以生成大多数查询,所以重复自己对我的用例来说并不是什么大问题,而且我并不认为这是一个与“不能使用缓存”相提并论的问题。

问题

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

例子

这主要来自过滤后参考页面,但我添加了全局过滤查询。

映射和文档

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 项),我们为这 2 件衬衫提供的衬衫类型(修身和普通),以及 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"
              }
            }
          }
        }
      }
    }
  }
}

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

4

2 回答 2

2

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

这篇弹性博文中,我们得到了初步提示:

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

不要将所有内容都塞进一个搜索请求中。

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

事实证明,让它们并行运行要快得多。查看多搜索 API

于 2017-01-06T16:02:09.773 回答
1

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

于 2016-12-22T19:34:09.033 回答