Elasticsearch 入门教程 – match匹配查询

    匹配查询 match 是个 核心 查询。无论需要查询什么字段, match 查询都应该会是首选的查询方式。 它是一个高级 全文查询 ,这表示它既能处理全文字段,又能处理精确字段。

    这就是说, match 查询主要的应用场景就是进行全文搜索,我们以下面一个简单例子来说明全文搜索是如何工作的:

索引一些数据


首先,我们使用 bulk API 创建一些新的文档和索引:

DELETE /my_index PUT /my_index
{ "settings": { "number_of_shards": 1 }} POST /my_index/my_type/_bulk
{ "index": { "_id": 1 }}
{ "title": "The quick brown fox" }
{ "index": { "_id": 2 }}
{ "title": "The quick brown fox jumps over the lazy dog" }
{ "index": { "_id": 3 }}
{ "title": "The quick brown fox jumps over the quick dog" }
{ "index": { "_id": 4 }}
{ "title": "Brown fox brown dog" }

删除已有的索引。

稍后,我们会在 被破坏的相关性! 中解释只为这个索引分配一个主分片的原因。

单个词查询

我们用第一个示例来解释使用 match 查询搜索全文字段中的单个词:

GET /my_index/my_type/_search
{
   "query": {
       "match": {
           "title": "QUICK!"
       }
   }
}

Elasticsearch 执行上面这个 match 查询的步骤是:

  1. 检查字段类型 。

    标题 title 字段是一个 string 类型( analyzed )已分析的全文字段,这意味着查询字符串本身也应该被分析。

  2. 分析查询字符串 。

    将查询的字符串 QUICK! 传入标准分析器中,输出的结果是单个项 quick 。因为只有一个单词项,所以 match 查询执行的是单个底层 term 查询。

  3. 查找匹配文档 。

    用 term 查询在倒排索引中查找 quick 然后获取一组包含该项的文档,本例的结果是文档:1、2 和 3 。

  4. 为每个文档评分 。

    用 term 查询计算每个文档相关度评分 _score ,这是种将 词频(term frequency,即词 quick 在相关文档的 title 字段中出现的频率)和反向文档频率(inverse document frequency,即词 quick 在所有文档的 title 字段中出现的频率),以及字段的长度(即字段越短相关度越高)相结合的计算方式。参见 相关性的介绍 。

这个过程给我们以下(经缩减)结果:

"hits": [
{
   "_id":      "1",
   "_score":   0.5,
   "_source": {
      "title": "The quick brown fox"
   }
},
{
   "_id":      "3",
   "_score":   0.44194174,
   "_source": {
      "title": "The quick brown fox jumps over the quick dog"
   }
},
{
   "_id":      "2",
   "_score":   0.3125,
   "_source": {
      "title": "The quick brown fox jumps over the lazy dog"
   }
}
]

文档 1 最相关,因为它的 title 字段更短,即 quick 占据内容的一大部分。

 

文档 3 比 文档 2 更具相关性,因为在文档 2 中 quick 出现了两次。

Java客户端代码:
    QueryBuilders.matchQuery("title", "BROWN DOG!")

多词查询

如果我们一次只能搜索一个词,那么全文搜索就会不太灵活,幸运的是 match 查询让多词查询变得简单:

GET /my_index/my_type/_search
{
    "query": {
        "match": {
            "title": "BROWN DOG!"
        }
    }
}

上面这个查询返回所有四个文档:

{
  "hits": [
     {
        "_id":      "4",
        "_score":   0.73185337, 
        "_source": {
           "title": "Brown fox brown dog"
        }
     },
     {
        "_id":      "2",
        "_score":   0.47486103, 
        "_source": {
           "title": "The quick brown fox jumps over the lazy dog"
        }
     },
     {
        "_id":      "3",
        "_score":   0.47486103, 
        "_source": {
           "title": "The quick brown fox jumps over the quick dog"
        }
     },
     {
        "_id":      "1",
        "_score":   0.11914785, 
        "_source": {
           "title": "The quick brown fox"
        }
     }
  ]
}

文档 4 最相关,因为它包含词 "brown" 两次以及 "dog" 一次。

 

文档 2、3 同时包含 brown 和 dog 各一次,而且它们 title 字段的长度相同,所以具有相同的评分。

文档 1 也能匹配,尽管它只有 brown 没有 dog 。

因为 match 查询必须查找两个词( ["brown","dog"] ),它在内部实际上先执行两次 term 查询,然后将两次查询的结果合并作为最终结果输出。为了做到这点,它将两个 term 查询包入一个 bool 查询中,详细信息见 布尔查询

以上示例告诉我们一个重要信息:即任何文档只要 title 字段里包含 指定词项中的至少一个词 就能匹配,被匹配的词项越多,文档就越相关。

提高精度

用 任意 查询词项匹配文档可能会导致结果中出现不相关的长尾。 这是种散弹式搜索。可能我们只想搜索包含 所有 词项的文档,也就是说,不去匹配 brown OR dog ,而通过匹配 brown AND dog 找到所有文档。

match 查询还可以接受 operator 操作符作为输入参数,默认情况下该操作符是 or 。我们可以将它修改成 and 让所有指定词项都必须匹配:

GET /my_index/my_type/_search
{
    "query": {
        "match": {
            "title": {      
                "query":    "BROWN DOG!",
                "operator": "and"
            }
        }
    }
}

match 查询的结构需要做稍许调整才能使用 operator 操作符参数。

 这个查询可以把文档 1 排除在外,因为它只包含两个词项中的一个。

 QueryBuilders.matchQuery("title", "BROWN DOG!").operator(Operator.AND)

控制精度

    在 所有 与 任意 间二选一有点过于非黑即白。 如果用户给定 5 个查询词项,想查找只包含其中 4 个的文档,该如何处理?将 operator 操作符参数设置成 and 只会将此文档排除。

    有时候这正是我们期望的,但在全文搜索的大多数应用场景下,我们既想包含那些可能相关的文档,同时又排除那些不太相关的。换句话说,我们想要处于中间某种结果。

   match 查询支持 minimum_should_match 最小匹配参数, 这让我们可以指定必须匹配的词项数用来表示一个文档是否相关。我们可以将其设置为某个具体数字,更常用的做法是将其设置为一个百分数,因为我们无法控制用户搜索时输入的单词数量:

GET /my_index/my_type/_search
{
 "query": {
   "match": {
     "title": {
       "query":                "quick brown dog",
       "minimum_should_match": "75%"
     }
   }
 }
}

当给定百分比的时候, minimum_should_match 会做合适的事情:在之前三词项的示例中, 75% 会自动被截断成 66.6% ,即三个里面两个词。无论这个值设置成什么,至少包含一个词项的文档才会被认为是匹配的。

QueryBuilders.matchQuery("title", "BROWN DOG!").operator(Operator.AND).minimumShouldMatch("75%")

资料:

    elasticsearch 查询(match和term)

发表评论