一定要了解 term
和 terms
是 包含(contains) 操作,而非 等值(equals) (判断)。 如何理解这句话呢?
如果我们有一个 term(词项)过滤器 { "term" : { "tags" : "search" } }
,它会与以下两个文档 同时 匹配:
{ "tags" : ["search"] }
{ "tags" : ["search", "open_source"] } (1)
-
尽管第二个文档包含除
search
以外的其他词,它还是被匹配并作为结果返回。
回忆一下 term
查询是如何工作的? Elasticsearch 会在倒排索引中查找包括某 term 的所有文档,然后构造一个 bitset 。在我们的例子中,倒排索引表如下:
Token |
DocIDs |
|
|
|
|
当 term
查询匹配标记 search
时,它直接在倒排索引中找到记录并获取相关的文档 ID,如倒排索引所示,这里文档 1 和文档 2 均包含该标记,所以两个文档会同时作为结果返回。
Note
|
由于倒排索引表自身的特性,整个字段是否相等会难以计算,如果确定某个特定文档是否 只(only) 包含我们想要查找的词呢?首先我们需要在倒排索引中找到相关的记录并获取文档 ID,然后再扫描 倒排索引中的每行记录 ,查看它们是否包含其他的 terms 。 可以想象,这样不仅低效,而且代价高昂。正因如此, |
如果一定期望得到我们前面说的那种行为(即整个字段完全相等),最好的方式是增加并索引另一个字段, 这个字段用以存储该字段包含词项的数量,同样以上面提到的两个文档为例,现在我们包括了一个维护标签数的新字段:
{ "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" } }, (1)
{ "term" : { "tag_count" : 1 } } (2)
]
}
}
}
}
}
-
查找所有包含 term
search
的文档。 -
确保文档只有一个标签。
这个查询现在只会匹配具有单个标签 search
的文档,而不是任意一个包含 search
的文档。