查找多个精确值 | Elasticsearch: 权威指南 | Elastic
2024-12-21
term
查询对于查找单个值非常有用,但通常我们可能想搜索多个值。
如果我们想要查找价格字段值为 $20 或 $30 的文档该如何处理呢?
不需要使用多个 term
查询,我们只要用单个 terms
查询(注意末尾的 s ), terms
查询好比是 term
查询的复数形式(以英语名词的单复数做比)。
它几乎与 term
的使用方式一模一样,与指定单个价格不同,我们只要将 term
字段的值改为数组即可:
{ "terms" : { "price" : [20, 30] } }
与 term
查询一样,也需要将其置入 filter
语句的常量评分查询中使用:
GET /my_store/products/_search { "query" : { "constant_score" : { "filter" : { "terms" : { "price" : [20, 30] } } } } }
运行结果返回第二、第三和第四个文档:
"hits" : [ { "_id" : "2", "_score" : 1.0, "_source" : { "price" : 20, "productID" : "KDKE-B-9947-#kL5" } }, { "_id" : "3", "_score" : 1.0, "_source" : { "price" : 30, "productID" : "JODL-X-1937-#pV7" } }, { "_id": "4", "_score": 1.0, "_source": { "price": 30, "productID": "QQPX-R-3956-#aD8" } } ]
一定要了解 term
和 terms
是 包含(contains) 操作,而非 等值(equals) (判断)。
如何理解这句话呢?
如果我们有一个 term(词项)过滤器 { "term" : { "tags" : "search" } }
,它会与以下两个文档 同时 匹配:
回忆一下 term
查询是如何工作的? Elasticsearch 会在倒排索引中查找包括某 term 的所有文档,然后构造一个 bitset 。在我们的例子中,倒排索引表如下:
Token | DocIDs |
|
|
|
|
当 term
查询匹配标记 search
时,它直接在倒排索引中找到记录并获取相关的文档 ID,如倒排索引所示,这里文档 1 和文档 2 均包含该标记,所以两个文档会同时作为结果返回。
由于倒排索引表自身的特性,整个字段是否相等会难以计算,如果确定某个特定文档是否 只(only) 包含我们想要查找的词呢?首先我们需要在倒排索引中找到相关的记录并获取文档 ID,然后再扫描 倒排索引中的每行记录 ,查看它们是否包含其他的 terms 。
可以想象,这样不仅低效,而且代价高昂。正因如此, term
和 terms
是 必须包含(must contain) 操作,而不是 必须精确相等(must equal exactly) 。
如果一定期望得到我们前面说的那种行为(即整个字段完全相等),最好的方式是增加并索引另一个字段, 这个字段用以存储该字段包含词项的数量,同样以上面提到的两个文档为例,现在我们包括了一个维护标签数的新字段:
{ "tags" : ["search"], "tag_count" : 1 } { "tags" : ["search", "open_source"], "tag_count" : 2 }
一旦增加这个用来索引项 term 数目信息的字段,我们就可以构造一个 constant_score
查询,来确保结果中的文档所包含的词项数量与要求是一致的:
GET /my_index/my_type/_search { "query": { "constant_score" : { "filter" : { "bool" : { "must" : [ { "term" : { "tags" : "search" } }, { "term" : { "tag_count" : 1 } } ] } } } } }
这个查询现在只会匹配具有单个标签 search
的文档,而不是任意一个包含 search
的文档。
官方地址:https://www.elastic.co/guide/cn/elasticsearch/guide/current/_finding_multiple_exact_values.html