可以使用两种方法筛选搜索结果:
- 使用带有filter子句的boolean查询。搜索请求(search requests)对搜索命中(search hits)和聚合(aggregations)应用布尔过滤器(boolean filters)。
- 使用搜索API的post_filter参数。搜索请求仅对search hits应用post filters,而不是聚合。我们可以使用post filter根据更广泛的结果集计算聚合,然后进一步缩小结果范围。
我们还可以在后期筛选后重新存储命中,以提高相关性并重新排序结果。
Post filter
使用post_filter参数筛选搜索结果时,计算聚合后将过滤search hits。post filter对聚合结果没有影响。
例如,我们销售的衬衫具有以下属性:
PUT /shirts
{
"mappings": {
"properties": {
"brand": { "type": "keyword"},
"color": { "type": "keyword"},
"model": { "type": "keyword"}
}
}
}
PUT /shirts/_doc/1?refresh
{
"brand": "gucci",
"color": "red",
"model": "slim"
}
假设用户指定了两个筛选器:
颜色:red,品牌:gucci。我们只想在搜索结果中向他们展示gucci制造的红衫。通常情况下,我们可以通过bool query执行此操作:
GET /shirts/_search
{
"query": {
"bool": {
"filter": [
{ "term": { "color": "red" }},
{ "term": { "brand": "gucci" }}
]
}
}
}
但是,我们还希望使用分面导航来显示用户可以单击的其他选项的列表。也许我们有一个模型字段,允许用户将搜索结果限制为red gucci T恤或连衣裙。
这可以通过terms aggregation实现:
GET /shirts/_search
{
"query": {
"bool": {
"filter": [
{ "term": { "color": "red" }},
{ "term": { "brand": "gucci" }}
]
}
},
"aggs": {
"models": {
"terms": { "field": "model" }
}
}
}
但也许我们也想告诉用户有多少其他颜色的gucci衬衫。如果我们只是在“color”字段上添加一个terms aggregation,我们将只返回红色,因为我们的查询只返回Gucci的红衫。
相反,我们希望在聚合期间包括所有颜色的衬衫,然后仅对搜索结果应用colors filter。这就是post_filter的用途:
GET /shirts/_search
{
"query": {
"bool": {
"filter": {
"term": { "brand": "gucci" } (1)
}
}
},
"aggs": {
"colors": {
"terms": { "field": "color" } (2)
},
"color_red": {
"filter": {
"term": { "color": "red" } (3)
},
"aggs": {
"models": {
"terms": { "field": "model" } (3)
}
}
}
},
"post_filter": { (4)
"term": { "color": "red" }
}
}
(1)现在,主查询将按Gucci查找所有衬衫,而不考虑颜色。
(2)color agg返回Gucci衬衫的流行颜色。
(3)color_red agg将模型子聚合限制为红色Gucci衬衫。
(4)最后,post_filter从搜索点击(search hits)中删除除红色以外的其他颜色。
Multiple rescores(多重扫描)
也可以按顺序执行多个重新存储:
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)"
}
}
}
}
}
} ]
}
第一个获取查询结果,然后第二个获取第一个的结果,以此类推。第二个重新扫描将“看到”第一个重新扫描完成的排序,因此可以在第一个重新扫描上使用一个大窗口,将文档拉入一个较小的窗口中进行第二次重新扫描。