过滤和聚合

聚合范围限定还有一个自然的扩展就是过滤。因为聚合是在查询结果范围内操作的,任何可以适用于查询的过滤器也可以应用在聚合上。

过滤

如果我们想找到售价在 $10,000 美元之上的所有汽车同时也为这些车计算平均售价,可以简单地使用一个 constant_score 查询和 filter 约束:

  1. GET /cars/transactions/_search
  2. {
  3. "size" : 0,
  4. "query" : {
  5. "constant_score": {
  6. "filter": {
  7. "range": {
  8. "price": {
  9. "gte": 10000
  10. }
  11. }
  12. }
  13. }
  14. },
  15. "aggs" : {
  16. "single_avg_price": {
  17. "avg" : { "field" : "price" }
  18. }
  19. }
  20. }

这正如我们在前面章节中讨论过那样,从根本上讲,使用 non-scoring 查询和使用 match 查询没有任何区别。查询(包括了一个过滤器)返回一组文档的子集,聚合正是操作这些文档。使用 filtering query 会忽略评分,并有可能会缓存结果数据等等。

过滤桶

但是如果我们只想对聚合结果过滤怎么办?假设我们正在为汽车经销商创建一个搜索页面,我们希望显示用户搜索的结果,但是我们同时也想在页面上提供更丰富的信息,包括(与搜索匹配的)上个月度汽车的平均售价。

这里我们无法简单的做范围限定,因为有两个不同的条件。搜索结果必须是 ford ,但是聚合结果必须满足 ford AND sold > now - 1M

为了解决这个问题,我们可以用一种特殊的桶,叫做 filter (注:过滤桶) 。我们可以指定一个过滤桶,当文档满足过滤桶的条件时,我们将其加入到桶内。

查询结果如下:

  1. GET /cars/transactions/_search
  2. {
  3. "size" : 0,
  4. "query":{
  5. "match": {
  6. "make": "ford"
  7. }
  8. },
  9. "aggs":{
  10. "recent_sales": {
  11. "filter": { (1)
  12. "range": {
  13. "sold": {
  14. "from": "now-1M"
  15. }
  16. }
  17. },
  18. "aggs": {
  19. "average_price":{
  20. "avg": {
  21. "field": "price" (2)
  22. }
  23. }
  24. }
  25. }
  26. }
  27. }

<1> 使用 过滤 桶在 查询 范围基础上应用过滤器。

<2> avg 度量只会对 +ford+ 和上个月售出的文档计算平均售价。

因为 filter 桶和其他桶的操作方式一样,所以可以随意将其他桶和度量嵌入其中。所有嵌套的组件都会 “继承” 这个过滤,这使我们可以按需针对聚合过滤出选择部分。

后过滤器

目前为止,我们可以同时对搜索结果和聚合结果进行过滤(不计算得分的 filter 查询),以及针对聚合结果的一部分进行过滤( filter 桶)。

我们可能会想,”只过滤搜索结果,不过滤聚合结果呢?” 答案是使用 post_filter

它是接收一个过滤器的顶层搜索请求元素。这个过滤器在查询 之后 执行(这正是该过滤器的名字的由来:它在查询之后 +post+ 执行)。正因为它在查询之后执行,它对查询范围没有任何影响,所以对聚合也不会有任何影响。

我们可以利用这个行为对查询条件应用更多的过滤器,而不会影响其他的操作,就如 UI 上的各个分类面。让我们为汽车经销商设计另外一个搜索页面,这个页面允许用户搜索汽车同时可以根据颜色来过滤。颜色的选项是通过聚合获得的:

  1. GET /cars/transactions/_search
  2. {
  3. "size" : 0,
  4. "query": {
  5. "match": {
  6. "make": "ford"
  7. }
  8. },
  9. "post_filter": { (1)
  10. "term" : {
  11. "color" : "green"
  12. }
  13. },
  14. "aggs" : {
  15. "all_colors": {
  16. "terms" : { "field" : "color" }
  17. }
  18. }
  19. }

<1> post_filter 元素是 +top-level+ 而且仅对命中结果进行过滤。

查询 部分找到所有的 +ford+ 汽车,然后用 terms 聚合创建一个颜色列表。因为聚合对查询范围进行操作,颜色列表与福特汽车有的颜色相对应。

最后, post_filter 会过滤搜索结果,只展示绿色 +ford+ 汽车。这在查询执行过 发生,所以聚合不受影响。

这通常对 UI 的连贯一致性很重要,可以想象用户在界面商选择了一类颜色(比如:绿色),期望的是搜索结果已经被过滤了,而 不是 过滤界面上的选项。如果我们应用 filter 查询,界面会马上变成 显示 +绿色+ 作为选项,这不是用户想要的!

[WARNING]

性能考虑(Performance consideration)

当你需要对搜索结果和聚合结果做不同的过滤时,你才应该使用 post_filter ,(((“post filter”, “performance and”))) 有时用户会在普通搜索使用 post_filter

不要这么做! post_filter 的特性是在查询 之后 执行,任何过滤对性能带来的好处(比如缓存)都会完全失去。

在我们需要不同过滤时, post_filter 只与聚合一起使用。


小结

选择合适类型的过滤(如:搜索命中、聚合或两者兼有)通常和我们期望如何表现用户交互有关。选择合适的过滤器(或组合)取决于我们期望如何将结果呈现给用户。

  • filter 过滤中的 non-scoring 查询,同时影响搜索结果和聚合结果。
  • filter 桶影响聚合。
  • post_filter 只影响搜索结果。