过滤搜索结果编辑

您可以使用两种方法来过滤搜索结果

  • 使用带有 filter 子句的布尔查询。搜索请求将 布尔过滤器 应用于搜索命中和 聚合
  • 使用搜索 API 的 post_filter 参数。搜索请求将 后置过滤器 仅应用于搜索命中,而不是聚合。您可以使用后置过滤器根据更广泛的结果集计算聚合,然后进一步缩小结果。

    您还可以 重新评分 后置过滤器后的命中,以提高相关性并重新排序结果。

后置过滤器编辑

当您使用 post_filter 参数来过滤搜索结果时,搜索命中将在计算聚合后进行过滤。后置过滤器不会影响聚合结果。

例如,您正在销售具有以下属性的衬衫

response = client.indices.create(
  index: 'shirts',
  body: {
    mappings: {
      properties: {
        brand: {
          type: 'keyword'
        },
        color: {
          type: 'keyword'
        },
        model: {
          type: 'keyword'
        }
      }
    }
  }
)
puts response

response = client.index(
  index: 'shirts',
  id: 1,
  refresh: true,
  body: {
    brand: 'gucci',
    color: 'red',
    model: 'slim'
  }
)
puts response
PUT /shirts
{
  "mappings": {
    "properties": {
      "brand": { "type": "keyword"},
      "color": { "type": "keyword"},
      "model": { "type": "keyword"}
    }
  }
}

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

假设用户指定了两个过滤器

color:redbrand:gucci。您只想在搜索结果中向他们展示 Gucci 制造的红色衬衫。通常,您会使用 bool 查询

response = client.search(
  index: 'shirts',
  body: {
    query: {
      bool: {
        filter: [
          {
            term: {
              color: 'red'
            }
          },
          {
            term: {
              brand: 'gucci'
            }
          }
        ]
      }
    }
  }
)
puts response
GET /shirts/_search
{
  "query": {
    "bool": {
      "filter": [
        { "term": { "color": "red"   }},
        { "term": { "brand": "gucci" }}
      ]
    }
  }
}

但是,您还想使用分面导航来显示用户可以点击的其他选项列表。也许您有一个 model 字段,允许用户将搜索结果限制为红色的 Gucci t-shirtsdress-shirts

这可以通过 terms 聚合

response = client.search(
  index: 'shirts',
  body: {
    query: {
      bool: {
        filter: [
          {
            term: {
              color: 'red'
            }
          },
          {
            term: {
              brand: 'gucci'
            }
          }
        ]
      }
    },
    aggregations: {
      models: {
        terms: {
          field: 'model'
        }
      }
    }
  }
)
puts response
GET /shirts/_search
{
  "query": {
    "bool": {
      "filter": [
        { "term": { "color": "red"   }},
        { "term": { "brand": "gucci" }}
      ]
    }
  },
  "aggs": {
    "models": {
      "terms": { "field": "model" } 
    }
  }
}

返回 Gucci 制造的红色衬衫中最受欢迎的款式。

但也许您还想告诉用户有多少 Gucci 衬衫有 其他颜色。如果您只是在 color 字段上添加一个 terms 聚合,您只会得到颜色 red,因为您的查询只返回 Gucci 制造的红色衬衫。

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

response = client.search(
  index: 'shirts',
  body: {
    query: {
      bool: {
        filter: {
          term: {
            brand: 'gucci'
          }
        }
      }
    },
    aggregations: {
      colors: {
        terms: {
          field: 'color'
        }
      },
      color_red: {
        filter: {
          term: {
            color: 'red'
          }
        },
        aggregations: {
          models: {
            terms: {
              field: 'model'
            }
          }
        }
      }
    },
    post_filter: {
      term: {
        color: 'red'
      }
    }
  }
)
puts response
GET /shirts/_search
{
  "query": {
    "bool": {
      "filter": {
        "term": { "brand": "gucci" } 
      }
    }
  },
  "aggs": {
    "colors": {
      "terms": { "field": "color" } 
    },
    "color_red": {
      "filter": {
        "term": { "color": "red" } 
      },
      "aggs": {
        "models": {
          "terms": { "field": "model" } 
        }
      }
    }
  },
  "post_filter": { 
    "term": { "color": "red" }
  }
}

主查询现在找到所有 Gucci 制造的衬衫,无论颜色如何。

colors agg 返回 Gucci 制造的衬衫的流行颜色。

color_red agg 将 models 子聚合限制为 红色 Gucci 衬衫。

最后,post_filter 从搜索 hits 中删除红色以外的颜色。

重新评分过滤后的搜索结果编辑

重新评分可以通过使用辅助(通常更昂贵)算法重新排序由 querypost_filter 阶段返回的顶部(例如 100-500)文档来帮助提高精度,而不是将昂贵的算法应用于索引中的所有文档。

rescore 请求在每个分片上执行,然后将其结果返回到处理整体搜索请求的节点。

目前,重新评分 API 只有一个实现:查询重新评分器,它使用查询来调整评分。将来,可能会提供其他重新评分器,例如成对重新评分器。

如果在 rescore 查询中提供了显式 sort(除了按降序排列的 _score),则会抛出错误。

当向用户公开分页时,您不应该更改 window_size,因为您在浏览每个页面时(通过传递不同的 from 值)会更改它,因为这可能会改变热门结果,导致结果在用户浏览页面时令人困惑地发生变化。

查询重新评分器编辑

查询重新评分器仅对由 querypost_filter 阶段返回的 Top-K 结果执行第二个查询。可以在每个分片上检查的文档数量可以通过 window_size 参数控制,该参数默认为 10。

默认情况下,来自原始查询和重新评分查询的得分将线性组合以生成每个文档的最终 _score。原始查询和重新评分查询的相对重要性可以通过 query_weightrescore_query_weight 分别控制。两者都默认为 1

例如

response = client.search(
  body: {
    query: {
      match: {
        message: {
          operator: 'or',
          query: 'the quick brown'
        }
      }
    },
    rescore: {
      window_size: 50,
      query: {
        rescore_query: {
          match_phrase: {
            message: {
              query: 'the quick brown',
              slop: 2
            }
          }
        },
        query_weight: 0.7,
        rescore_query_weight: 1.2
      }
    }
  }
)
puts response
POST /_search
{
   "query" : {
      "match" : {
         "message" : {
            "operator" : "or",
            "query" : "the quick brown"
         }
      }
   },
   "rescore" : {
      "window_size" : 50,
      "query" : {
         "rescore_query" : {
            "match_phrase" : {
               "message" : {
                  "query" : "the quick brown",
                  "slop" : 2
               }
            }
         },
         "query_weight" : 0.7,
         "rescore_query_weight" : 1.2
      }
   }
}

可以通过 score_mode 控制得分组合的方式

得分模式 描述

total

添加原始得分和重新评分查询得分。默认值。

multiply

将原始得分乘以重新评分查询得分。适用于 function query 重新评分。

avg

对原始得分和重新评分查询得分求平均值。

max

取原始得分和重新评分查询得分的最大值。

min

取原始得分和重新评分查询得分的最小值。

多个重新评分编辑

也可以按顺序执行多个重新评分

response = client.search(
  body: {
    query: {
      match: {
        message: {
          operator: 'or',
          query: 'the quick brown'
        }
      }
    },
    rescore: [
      {
        window_size: 100,
        query: {
          rescore_query: {
            match_phrase: {
              message: {
                query: 'the quick brown',
                slop: 2
              }
            }
          },
          query_weight: 0.7,
          rescore_query_weight: 1.2
        }
      },
      {
        window_size: 10,
        query: {
          score_mode: 'multiply',
          rescore_query: {
            function_score: {
              script_score: {
                script: {
                  source: 'Math.log10(doc.count.value + 2)'
                }
              }
            }
          }
        }
      }
    ]
  }
)
puts response
POST /_search
{
   "query" : {
      "match" : {
         "message" : {
            "operator" : "or",
            "query" : "the quick brown"
         }
      }
   },
   "rescore" : [ {
      "window_size" : 100,
      "query" : {
         "rescore_query" : {
            "match_phrase" : {
               "message" : {
                  "query" : "the quick brown",
                  "slop" : 2
               }
            }
         },
         "query_weight" : 0.7,
         "rescore_query_weight" : 1.2
      }
   }, {
      "window_size" : 10,
      "query" : {
         "score_mode": "multiply",
         "rescore_query" : {
            "function_score" : {
               "script_score": {
                  "script": {
                    "source": "Math.log10(doc.count.value + 2)"
                  }
               }
            }
         }
      }
   } ]
}

第一个获取查询的结果,然后第二个获取第一个的结果,等等。第二个重新评分将“看到”由第一个重新评分完成的排序,因此可以在第一个重新评分中使用一个大的窗口将文档拉入第二个重新评分的较小窗口中。