Elasticsearch 入门教程 – 多个精准值搜索

查找多个精确值编辑

term 查询对于查找单个值非常有用,但通常我们可能想搜索多个值。 如果我们想要查找价格字段值为 $20 或 $30 的文档该如何处理呢?

不需要使用多个 term 查询,我们只要用单个 terms 查询(注意末尾的 s ), terms 查询好比是 term 查询的复数形式(以英语名词的单复数做比)。

sql中的in

     select * from tbl where col in ("value1", "value2");

它几乎与 term 的使用方式一模一样,与指定单个价格不同,我们只要将 term 字段的值改为数组即可:

{
   "terms" : {
       "price" : [20, 30]
   }
}

与 term 查询一样,也需要将其置入 filter 语句的常量评分查询中使用:

GET /my_store/products/_search
{
   "query" : {
       "constant_score" : {
           "filter" : {
               "terms" : {
                   "price" : [20, 30]
               }
           }
       }
   }
}

这个 terms 查询被置于 constant_score 查询中

运行结果返回第二、第三和第四个文档:

"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" } } ,它会与以下两个文档 同时 匹配:

{ "tags" : ["search"] }
{ "tags" : ["search", "open_source"] }

尽管第二个文档包含除 search 以外的其他词,它还是被匹配并作为结果返回。

回忆一下 term 查询是如何工作的? Elasticsearch 会在倒排索引中查找包括某 term 的所有文档,然后构造一个 bitset 。在我们的例子中,倒排索引表如下:

Token

DocIDs

open_source

2

search

1,2

当 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 } }
                   ]
               }
           }
       }
   }
}

查找所有包含 term search 的文档。

确保文档只有一个标签。

   这个查询现在只会匹配具有单个标签 search 的文档,而不是任意一个包含 search 的文档。

 

测试数据:

1、为帖子数据增加tag字段

POST /forum/article/_bulk

{ "update": { "_id": "1"} }
{ "doc" : {"tag" : ["java", "hadoop"]} }
{ "update": { "_id": "2"} }
{ "doc" : {"tag" : ["java"]} }
{ "update": { "_id": "3"} }
{ "doc" : {"tag" : ["hadoop"]} }
{ "update": { "_id": "4"} }
{ "doc" : {"tag" : ["java", "elasticsearch"]} }

2、搜索articleID为KDKE-B-9947-#kL5或QQPX-R-3956-#aD8的帖子,搜索tag中包含java的帖子

GET /forum/article/_search 
{
  "query": {
    "constant_score": {
      "filter": {
        "terms": {
          "articleID": [
            "KDKE-B-9947-#kL5",
            "QQPX-R-3956-#aD8"
          ]
        }
      }
    }
  }
}

3、优化搜索结果,仅仅搜索tag只包含java的帖子

POST /forum/article/_bulk

{ "update": { "_id": "1"} }
{ "doc" : {"tag_cnt" : 2} }
{ "update": { "_id": "2"} }
{ "doc" : {"tag_cnt" : 1} }
{ "update": { "_id": "3"} }
{ "doc" : {"tag_cnt" : 1} }
{ "update": { "_id": "4"} }
{ "doc" : {"tag_cnt" : 2} }

查询tag_cnt=1并且tag包含java的帖子;

GET /forum/article/_search
{
  "query": {
    "constant_score": {
      "filter": {
        "bool": {
          "must": [
            {
              "term": {
                "tag_cnt": 1
              }
            },
            {
              "terms": {
                "tag": ["java"]
              }
            }
          ]
        }
      }
    }
  }
}

Java客户端代码:

 QueryBuilders.termsQuery("price", new int[]{10,20})

发表评论