Elasticsearch系列(8)Search之折叠、过滤及高亮

1. 折叠

使用collapse参数根据字段折叠搜索结果。折叠会合并指定折叠字段内容相同的数据,并选择排序文档结果集中第一个文档返回。例如,按照user.id折叠搜索结果,示例如下:

PUT my_index_01
{
  "mappings": {
    "properties": {
      "message": {
        "type": "text"
      },
      "user.id": {
        "type": "long"
      },
      "user.name": {
        "type": "keyword"
      },
      "http.response.bytes": {
        "type": "integer"
      },
      "@timestamp": {
        "type": "date"
      }
    }
  }
}
PUT my_index_01/_doc/1
{
  "message": "GET /search",
  "user": {
    "id": 10001,
    "name": "johnny"
  },
  "@timestamp": "2015-01-01",
  "http.response.bytes": 10
}
PUT my_index_01/_doc/2
{
  "message": "GET /_doc",
  "user": {
    "id": 10001,
    "name": "johnny"
  },
  "@timestamp": "2015-01-02",
  "http.response.bytes": 8
}
GET /my_index_01/_search
{
  "query": {
    "match": {
      "message": "GET /search"
    }
  },
  "collapse": {
    "field": "user.id"                
  }
}

返回折叠结果

{
  "took" : 4,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 2,
      "relation" : "eq"
    },
    "max_score" : null,
    "hits" : [
      {
        "_index" : "my_index_01",
        "_type" : "_doc",
        "_id" : "1",
        "_score" : 0.8754687,
        "_source" : {
          "message" : "GET /search",
          "user" : {
            "id" : 10001,
            "name" : "johnny"
          }
        },
        "fields" : {
          "user.id" : [
            10001
          ]
        }
      }
    ]
  }
}
  • collapse.field参数使用的必须是数字类型或keyword类型的单个值字段,并且字段的doc_values激活。

扩展折叠结果

对于每个折叠后的命中结果集,可以再次请求其多个inner_hits,在展示折叠命中结果集多种表示形式时有用,通过参数collapse. inner_hits可以做到,示例如下:

GET /my_index_01/_search
{
  "query": {
    "match": {
      "message": "GET /search"
    }
  },
  "collapse": {
    "field": "user.id",                      
    "inner_hits": [
      {
        "name": "largest_responses",         //1
        "size": 1,
        "sort": [ {"http.response.bytes":"desc"} ]
      },
      {
        "name": "most_recent",             //2  
        "size": 1,
        "sort": [ { "@timestamp": "asc" } ]
      }
    ]
  },
  "sort": [ "http.response.bytes" ]
}

注释1:inner_hits结果集名称为largest_responses,基于折叠后搜索结果,按照http.response.bytes倒叙排列,并获取size指定条数的数据。
注释2:inner_hits结果集名称为most_recent,基于折叠后搜索结果,按照@timestamp顺序排列,并获取size指定条数的数据。

二级折叠

inner_hits也支持再次折叠,通过参数collapse. inner_hits. collapse可以折叠inner_hits结果集。示例如下:

GET /my_index_01/_search
{
  "query": {
    "match": {
      "message": "GET /search"
    }
  },
  "collapse": {
    "field": "user.id",                      
    "inner_hits": [
      {
        "name": "largest_responses",         
        "size": 2,
        "collapse": { "field": "user.name" },
        "sort": [ {"http.response.bytes":"desc"} ]
      }
    ]
  }
}

2. 过滤

可以使用以下两种方式来过滤搜索结果:

(1)使用带有filter子句的boolean query(布尔查询)。搜索请求中的搜索命中集和聚合都可使用boolean filters(boolean过滤器),即聚合会基于boolean query过滤。
(2)使用search API(搜索API)中的post_filter参数。搜索请求仅将post过滤器应用于搜索命中集,不会应用于聚合。可以使用post过滤器基于更广泛的结果集来进行计算聚合,然后进一步缩小结果。

可以在post过滤器之后重新计算得分,以提高相关性和结果重排。

Post过滤器

使用post_filter参数来过滤搜索结果,但它不影响聚合结果。例如,搜索华为品牌,红色类型的手机,并且希望聚合所有华为手机,操作示例如下:

PUT /my_index_phone
{
  "mappings": {
    "properties": {
      "brand": { "type": "keyword" },
      "color": { "type": "keyword" },
      "model": { "type": "keyword" }
    }
  }
}
PUT /my_index_phone/_doc/1?refresh
{"brand":"huawei","color":"red","model":"mate40"}
PUT /my_index_phone/_doc/2?refresh
{"brand":"huawei","color":"red","model":"p30"}
PUT /my_index_phone/_doc/3?refresh
{"brand":"huawei","color":"white","model":"p30"}

GET /my_index_phone/_search
{
  "query": {
    "bool": {
      "filter": { //1
        "term": { "brand": "huawei" }  
      }
    }
  },
  "aggs": {
    "colors": { //2
      "terms": { "field": "color" } 
    },
    "color_red": { //3
      "filter": {
        "term": { "color": "red" } 
      },
      "aggs": {
        "models": {
          "terms": { "field": "model" } 
        }
      }
    }
  },
  "post_filter": {  //4
    "term": { "color": "red" }
  }
}

注释1:boolean查询过滤 品牌为华为的手机,此查询影响聚合操作,会影响操作2和3。
注释2:以color字段聚合,也就是按颜色归类聚合华为手机,有红色和白色两种,聚合结果返回有key=red和key=white两个桶(buckets)。
注释3:先按照color:red过滤,然后再按照model字段聚合,即查询红色的、华为品牌的手机,并且按照model分类聚合,聚合结果返回key=mate40和key=p30两个桶(buckets)。
注释4:搜索查询只查询颜色为红色的,Post过滤器应用于搜索查询,不会影响聚合操作,即不影响操作2和3。

重新计分器

查询重新计分器(rescorer)只对query和post_filter阶段返回的Top-K结果执行第二个查询。
通过window_size参数控制每个分片上被检查的文档数量,默认值是10。
默认情况下,原始查询和rescore查询的分数被线性组合(可配置),从而为每个文档生成最终的_score,原始查询和rescore查询的相对重要性可以分别用query_weight参数和rescore_query_weight参数来控制,两者都默认为1。

  • 通过参数rescore来设置,操作示例如下:
POST /my_index_phone/_search
{
   "query" : {
      "match" : {
         "model" : {
            "operator" : "or",
            "query" : "p30"
         }
      }
   },
   "rescore" : [ {
      "window_size" : 100,
      "query" : {
         "rescore_query" : { // 1
            "match_phrase" : {
               "color" : {
                  "query" : "white",
                  "slop" : 2
               }
            }
         },
         "query_weight" : 0.7, 
         "rescore_query_weight" : 1.2 //2
      }
   }]
}

注释1:rescore查询颜色为白色的短语查询并计算得分,该得分线性组合原始得分来决定最终得分。
注释2:rescore查询相关性权重设置。

通过参数score_mode配置得分组合的方式,score_mode配置值如下:

  • total:原始查询得分和rescore查询得分累加,默认值。
  • multiply:原始查询得分和rescore查询得分相乘,需要使用函数查询rescore。
  • avg:原始查询得分和rescore查询得分平均值。
  • max:原始查询得分和rescore查询得分取最大值。
  • min:原始查询得分和rescore查询得分取最小值。

并且Elasticsearch支持按顺序多个rescore查询,示例如下:

POST /my_index_phone/_search
{
   "query" : {
      "match" : {
         "brand" : {
            "operator" : "or",
            "query" : "huawei"
         }
      }
   },
   "rescore" : [ {
        "window_size" : 100,
        "query" : {
           "rescore_query" : {
              "match_phrase" : {
                 "color" : {
                    "query" : "red",
                    "slop" : 2
                 }
              }
           },
           "query_weight" : 1,
           "rescore_query_weight" : 2
        }
     },
      {
        "window_size" : 10,
        "query" : {
           "score_mode": "multiply",
           "rescore_query" : {
              "function_score" : {
                 "script_score": {
                    "script": {
                      "source": "Math.log10(_score + 2)"
                    }
                 }
              }
           }
        }
     }
   ]
}
  • 第一个rescore获取查询的结果,第二个rescore获取第一个rescore的结果,以此类推。第二次rescore将“看到”第一次rescore所做的排序,因此可以在第一次rescore时使用一个大窗口将文档拉到一个小窗口中进行第二次rescore。

3. 高亮

高亮器(Highlighters)能够将搜索结果中的一个或多个字段加上代码片段以突出显示。当要请求高亮显示时,Elasticsearch响应会为每个搜索命中集包含一个额外的高亮元素,其中包括高亮显示的字段和高亮显示的片段。

  • 例如,将查询匹配的搜索命中集中model字段值高亮显示,示例如下:
GET /my_index_phone/_search
{
  "query": {
    "match": { "model": "p30" }
  },
  "highlight": {
    "fields": {
      "model": {}
    }
  }
}

返回结果片段:

 ...
{
    "_index" : "my_index_phone",
    "_type" : "_doc",
    "_id" : "3",
    "_score" : 0.4700036,
    "_source" : {
      "brand" : "huawei",
      "color" : "white",
      "model" : "p30"
    },
    "highlight" : {
      "model" : [
        "p30" //1
      ]
    }
}
...

注释1:高亮显示,默认加上标签。

高亮器类型

高亮器类型有三种:unified,plain和fvh。可以通过type参数来修改每个字段的高亮器类型,Elasticsearch高亮器默认unified。

  • unified: unified highlighter使用Lucene的统unified highlighter。该高亮器将文本分解成句子,并使用BM25算法给单个句子打分。它还支持精确短语和多词条(模糊、前缀、正则表达式)突出显示。这是默认高亮器。示例如下图所示:
  • plain: plain highlighter使用标准的Lucene highlighter。它尝试从理解短语查询中的单词重要性和任何单词定位标准方面反映查询匹配逻辑。示例如下图所示:
  • fvh: fast vector highlighter使用Lucene fast vector highlighter。可以在映射(mapping)中将字段上的参数term_vector设置为with_positions_offsets的来使用此高亮显示。该高亮器具有下列特点:
    (1)可以用一个边界扫描(boundary_scanner)来自定义。
    (2)需要将参数term_vector设置为with_positions_offsets,这样会增加索引的大小。
    (3)可以将多个字段的匹配项合并到一个结果中。具体可了解matched_fields。
    (4)可以分配不同的权重在不同位置的匹配,允许像短语匹配被排序在词条匹配上面突出显示一个增强查询,增强短语匹配超过词条匹配。
    示例如下图所示:

高亮器设置

高亮器设置(Highlighting settings)可以在全局级别设置,并在字段级别覆盖。具体参数设置有以下:

boundary_chars

包含每个边界字符的字符串。默认为.,!? \t\n。

boundary_max_scan

扫描边界字符的范围。默认为20。

boundary_scanner

指定如何切分突出显示的片段(fragments),有三个设置值:chars、sentence或word。仅适用于unified和fvh高亮器。unified高亮器默认该设置值为sentence,fvh高亮器默认该设置值为chars。

  • chars
    使用由boundary_chars指定的字符高亮显示边界。boundary_max_scan设置控制扫描边界字符的范围。仅对fvh高亮器有效。
  • sentence
    根据Java的BreakIterator来确定下一个句子边界处切分突出显示的片段(fragments)。可以通过boundary_scanner_locale指定要使用的语言环境。
  • word
    根据Java的BreakIterator来确定下一个单词边界处切分突出显示的片段(fragments)。可以通过boundary_scanner_locale指定要使用的语言环境。
boundary_scanner_locale

控制用于搜索句子和单词边界的语言环境。这个参数采用语言标记的形式,例如。“en-us”、“-fr”、“ja-JP”。更多信息可以在 Locale Language Tag
文档中找到。默认值是local.root。

encoder

指示代码段是否应该用HTML编码:default(无编码)或html (HTML-转义代码段文本,然后插入高亮显示标记)。

fields

指定要检索高亮显示的字段。可以使用通配符来指定字段。例如,可以指定content*来高亮显示所有以content开头的text和keyword字段。示例如下:

GET my_index_01/_search
{
  "query": {
    "match": {
      "content": "红豆生南国。"
    }
  },
   "highlight": {
    "fields": {
      "content*": {"type": "plain"}
    }
  }
}
force_source

即使字段是单独存储的,也要基于字段_source高亮显示。默认值为false。

fragmenter

指定文本应该如何在高亮显示片段中拆分:simplespan。仅对plain highlighter有效。默认值为span。

  • simple:将文本分隔成大小相同的片段。
  • span:将文本分割成大小相同的片段,但尽量避免在高亮显示的词条(term)之间分割文本。
fragment_offset

控制开始高亮显示的页边距。仅对fvh高亮器有效。

fragment_size

以字符为单位高亮显示的片段大小。默认为100。

highlight_query

高亮显示搜索查询以外的其他查询的匹配项。这在使用rescore查询时特别有用,因为默认情况下高亮显示不会考虑rescore查询。

matched_fields

如果没有要高亮显示的匹配片段,则希望从字段的开始位置返回的文本数量。默认值为0(不返回任何内容)。

number_of_fragments

返回的最大片段数。如果片段数设置为0,则不返回片段。相反,整个字段内容将高亮显示并返回。当需要高亮显示短文本(如标题或地址)时,这很方便,但不需要分割。如果number_of_fragments为0,则会忽略fragment_size。默认为5。

order

当设置值为score时,根据得分对高亮显示的片段进行排序。默认情况下(默认值none),片段将按照它们在字段中出现的顺序输出。

phrase_limit

控制文档中要考虑的匹配短语的数量。防止fvh高亮器分析太多的短语和消耗太多的内存。在使用matched_fields时,将考虑每个匹配字段的phrase_limit。提高限制值会增加查询时间并消耗更多内存。只支持fvh高亮器。默认为256。

pre_tags

与post_tags一起使用,定义用于高亮显示文本的HTML标记。默认情况下,高亮显示的文本被包裹在标签中。指定为字符串数组。

post_tags

与pre_tags一起使用,定义用于高亮显示文本的HTML标记。默认情况下,高亮显示的文本被包装在标签中。指定为字符串数组。操作示例如下:

GET my_index_01/_search
{
  "query": {
    "match": {
      "content": "红豆生南国"
    }
  },
   "highlight": {
    "fields": {
      "content*": {"type": "plain"}
    },
    "pre_tags": [""],
    "post_tags": [""]
  }
}
require_field_match

默认情况下(默认值为true),只有包含查询匹配的字段才会高亮显示。如果将require_field_match设置为false,那么会高亮显示所有字段。

tags_schema

通过使用内置标签模式(tags schema)设置样式。该模式定义了以下pre_tags的样式并将post_tags定义为。

, , ,
, , ,
, , ,

type

使用的高亮器类型:unified,plain和fvh。

高亮样例

先创建索引并索引数据。

PUT /my_index_01
{
  "mappings": {
    "properties": {
       "content": {
        "type": "text"
      },
      "name":{
        "type": "text"
      }
    }
  }
}
POST /my_index_01/_create/1
{
  "content": "Quick Brown Fox,Quick White Fox, Quick gray Fox",
  "name": "Fox"
}
高亮所有字段

通过require_field_match参数设置false来高亮所有字段,示例如下:

GET my_index_01/_search
{
  "query": {
    "match": {
      "name": "Fox"
    }
  },
   "highlight": {
    "require_field_match": false,
    "fields": {
      "name": { },
      "content": {}
    }
  }
}
配置高亮标签样式

通过tags_schema参数设置标签样式,示例如下:

GET my_index_01/_search
{
  "query": {
    "match": {
      "name": "Fox"
    }
  },
   "highlight": {
    "tags_schema" : "styled",
    "fields": {
      "name": { }
    }
  }
}
控制高亮片段

每个高亮显示的字段可以控制其高亮显示的片段的大小(默认为100),以及返回的最大片段数(默认为5)。示例如下:

GET my_index_01/_search
{
  "query": {
    "match": {
      "content": "Fox"
    }
  },
   "highlight": {
    "fields": {
      "content": {"fragment_size" : 15, "number_of_fragments" : 2}
    }
  }
}
为plain高亮器指定fragmenter

通过fragmenter参数和type参数设置。示例如下:

GET my_index_01/_search
{
  "query": {
    "match_phrase": { "content": "Brown Fox" }
  },
  "highlight": {
    "fields": {
      "content": {
        "type": "plain",
        "fragment_size": 15,
        "number_of_fragments": 2,
        "fragmenter": "simple"
      }
    }
  }
}

你可能感兴趣的:(Elasticsearch系列(8)Search之折叠、过滤及高亮)