英文版地址: https://www.elastic.co/guide/en/elasticsearch/guide/current/_cross_fields_queries.html
本书基于 Elasticsearch 2.x 版本,有些内容可能已经过时。
cross-fields 跨字段查询edit
自定义 _all
的方式是一个好的解决方案,只需在索引文档前为其设置好映射。不过, Elasticsearch 还在搜索时提供了相应的解决方案:使用 cross_fields
类型进行 multi_match
查询。 cross_fields
使用词中心式(term-centric)的查询方式,这与 best_fields
和 most_fields
使用字段中心式(field-centric)的查询方式非常不同,它将所有字段当成一个大字段,并在 每个字段 中查找 每个词 。
为了说明字段中心式(field-centric)与词中心式(term-centric)这两种查询方式的不同,先看看以下字段中心式的 most_fields
查询的 explanation
解释:
GET /_validate/query?explain { "query": { "multi_match": { "query": "peter smith", "type": "most_fields", "operator": "and", "fields": [ "first_name", "last_name" ] } } }
对于匹配的文档, peter
和 smith
都必须同时出现在相同字段中,要么是 first_name
字段,要么 last_name
字段:
(+first_name:peter +first_name:smith) (+last_name:peter +last_name:smith)
词中心式 会使用以下逻辑:
+(first_name:peter last_name:peter) +(first_name:smith last_name:smith)
换句话说,词 peter
和 smith
都必须出现,但是可以出现在任意字段中。
cross_fields
类型首先分析查询字符串并生成一个词列表,然后它从所有字段中依次搜索每个词。这种不同的搜索方式很自然的解决了 字段中心式 查询三个问题中的二个。剩下的问题是逆向文档频率不同。
幸运的是 cross_fields
类型也能解决这个问题,通过 validate-query
可以看到:
GET /_validate/query?explain { "query": { "multi_match": { "query": "peter smith", "type": "cross_fields", "operator": "and", "fields": [ "first_name", "last_name" ] } } }
它通过 混合 不同字段逆向索引文档频率的方式解决了词频的问题:
+blended("peter", fields: [first_name, last_name]) +blended("smith", fields: [first_name, last_name])
换句话说,它会同时在 first_name
和 last_name
两个字段中查找 smith
的 IDF ,然后用两者的最小值作为两个字段的 IDF 。结果实际上就是 smith
会被认为既是个平常的姓,也是平常的名。
为了让 cross_fields
查询以最优方式工作,所有的字段都须使用相同的分析器,具有相同分析器的字段会被分组在一起作为混合字段使用。
如果包括了不同分析链的字段,它们会以 best_fields
的相同方式被加入到查询结果中。例如:我们将 title
字段加到之前的查询中(假设它们使用的是不同的分析器), explanation 的解释结果如下:
(+title:peter +title:smith) ( +blended("peter", fields: [first_name, last_name]) +blended("smith", fields: [first_name, last_name]) )
当在使用 minimum_should_match
和 operator
参数时,这点尤为重要。
按字段提高权重edit
采用 cross_fields
查询与 自定义 _all
字段 相比,其中一个优势就是它可以在搜索时为单个字段提升权重。
这对像 first_name
和 last_name
具有相同值的字段并不是必须的,但如果要用 title
和 description
字段搜索图书,可能希望为 title
分配更多的权重,这同样可以使用前面介绍过的 ^
符号语法来实现:
GET /books/_search { "query": { "multi_match": { "query": "peter smith", "type": "cross_fields", "fields": [ "title^2", "description" ] } } }
自定义单字段查询是否能够优于多字段查询,取决于在多字段查询与单字段自定义 _all
之间代价的权衡,即哪种解决方案会带来更大的性能优化就选择哪一种。