Elasticsearch 入门教程 – match 组合查询

组合查询

     在 组合过滤器 中,我们讨论过如何使用 bool 过滤器通过 and 、 or 和 not 逻辑组合将多个过滤器进行组合。在查询中, bool 查询有类似的功能,只有一个重要的区别。

    过滤器做二元判断:文档是否应该出现在结果中?但查询更精妙,它除了决定一个文档是否应该被包括在结果中,还会计算文档的 相关程度 。

与过滤器一样, bool 查询也可以接受 must 、 must_not 和 should 参数下的多个查询语句。比如:

GET /my_index/my_type/_search
{
 "query": {
   "bool": {
     "must":     { "match": { "title": "quick" }},
     "must_not": { "match": { "title": "lazy"  }},
     "should": [
                 { "match": { "title": "brown" }},
                 { "match": { "title": "dog"   }}
     ]
   }
 }
}

   以上的查询结果返回 title 字段包含词项 quick 但不包含 lazy 的任意文档。目前为止,这与 bool 过滤器的工作方式非常相似。

   区别就在于两个 should 语句,也就是说:一个文档不必包含 brown 或 dog 这两个词项,但如果一旦包含,我们就认为它们 更相关 :

{
 "hits": [
    {
       "_id":      "3",
       "_score":   0.70134366,
       "_source": {
          "title": "The quick brown fox jumps over the quick dog"
       }
    },
    {
       "_id":      "1",
       "_score":   0.3312608,
       "_source": {
          "title": "The quick brown fox"
       }
    }
 ]
}

文档 3 会比文档 1 有更高评分是因为它同时包含 brown 和 dog 。

评分计算

    bool 查询会为每个文档计算相关度评分 _score , 再将所有匹配的 must 和 should 语句的分数 _score求和,最后除以 must 和 should 语句的总数。

    must_not 语句不会影响评分; 它的作用只是将不相关的文档排除。

控制精度

   所有 must 语句必须匹配,所有 must_not 语句都必须不匹配,但有多少 should 语句应该匹配呢? 默认情况下,没有 should 语句是必须匹配的,只有一个例外:那就是当没有 must 语句的时候,至少有一个 should 语句必须匹配。

    就像我们能控制 match 查询的精度 一样,我们可以通过 minimum_should_match 参数控制需要匹配的 should 语句的数量, 它既可以是一个绝对的数字,又可以是个百分比:

GET /my_index/my_type/_search
{
 "query": {
   "bool": {
     "should": [
       { "match": { "title": "brown" }},
       { "match": { "title": "fox"   }},
       { "match": { "title": "dog"   }}
     ],
     "minimum_should_match": 2
   }
 }
}

这也可以用百分比表示。

     这个查询结果会将所有满足以下条件的文档返回: title 字段包含 "brown" AND "fox" 、 "brown" AND "dog" 或 "fox" AND "dog" 。如果有文档包含所有三个条件,它会比只包含两个的文档更相关。

Java代码:

QueryBuilder queryBuilder=QueryBuilders.boolQuery()
        		 .should(QueryBuilders.matchQuery("title", "brown"))
        		 .should(QueryBuilders.matchQuery("title", "fox"))
        		 .should(QueryBuilders.matchQuery("title", "dog"))
        		 .minimumShouldMatch(2);

目前为止,可能已经意识到多词 match 查询只是简单地将生成的 term 查询包裹 在一个 bool 查询中。如果使用默认的 or 操作符,每个 term 查询都被当作 should 语句,这样就要求必须至少匹配一条语句。以下两个查询是等价的:

{
    "match": { "title": "brown fox"}
}

{
  "bool": {
    "should": [
      { "term": { "title": "brown" }},
      { "term": { "title": "fox"   }}
    ]
  }
}

如果使用 and 操作符,所有的 term 查询都被当作 must 语句,所以 所有(all) 语句都必须匹配。以下两个查询是等价的:

{
    "match": {
        "title": {
            "query":    "brown fox",
            "operator": "and"
        }
    }
}

{
  "bool": {
    "must": [
      { "term": { "title": "brown" }},
      { "term": { "title": "fox"   }}
    ]
  }
}

如果指定参数 minimum_should_match ,它可以通过 bool 查询直接传递,使以下两个查询等价:

{
    "match": {
        "title": {
            "query":                "quick brown fox",
            "minimum_should_match": "75%"
        }
    }
}

{
  "bool": {
    "should": [
      { "term": { "title": "brown" }},
      { "term": { "title": "fox"   }},
      { "term": { "title": "quick" }}
    ],
    "minimum_should_match": 2 
  }
}

 因为只有三条语句,match 查询的参数 minimum_should_match 值 75% 会被截断成 2 。即三条 should 语句中至少有两条必须匹配。

当然,我们通常将这些查询用 match 查询来表示,但是如果了解 match 内部的工作原理,我们就能根据自己的需要来控制查询过程。有些时候单个 match 查询无法满足需求,比如为某些查询条件分配更高的权重。我们会在下一小节中看到这个例子。

查询语句提升权重

当然 bool 查询不仅限于组合简单的单个词 match 查询, 它可以组合任意其他的查询,以及其他 bool 查询。 普遍的用法是通过汇总多个独立查询的分数,从而达到为每个文档微调其相关度评分 _score 的目的。

假设想要查询关于 “full-text search(全文搜索)” 的文档, 但我们希望为提及 “Elasticsearch” 或 “Lucene” 的文档给予更高的 权重 ,这里 更高权重 是指如果文档中出现 “Elasticsearch” 或 “Lucene” ,它们会比没有的出现这些词的文档获得更高的相关度评分 _score ,也就是说,它们会出现在结果集的更上面。

一个简单的 bool 查询 允许我们写出如下这种非常复杂的逻辑:

GET /_search
{
    "query": {
        "bool": {
            "must": {
                "match": {
                    "content": { 
                        "query":    "full text search",
                        "operator": "and"
                    }
                }
            },
            "should": [ 
                { "match": { "content": "Elasticsearch" }},
                { "match": { "content": "Lucene"        }}
            ]
        }
    }
}

content 字段必须包含 full 、 text 和 search 所有三个词。

如果 content 字段也包含 Elasticsearch 或 Lucene ,文档会获得更高的评分 _score 。

should 语句匹配得越多表示文档的相关度越高。目前为止还挺好。

但是如果我们想让包含 Lucene 的有更高的权重,并且包含 Elasticsearch 的语句比 Lucene 的权重更高,该如何处理?

我们可以通过指定 boost 来控制任何查询语句的相对的权重, boost 的默认值为 1 ,大于 1 会提升一个语句的相对权重。所以下面重写之前的查询:

GET /_search
{
    "query": {
        "bool": {
            "must": {
                "match": {  
                    "content": {
                        "query":    "full text search",
                        "operator": "and"
                    }
                }
            },
            "should": [
                { "match": {
                    "content": {
                        "query": "Elasticsearch",
                        "boost": 3 
                    }
                }},
                { "match": {
                    "content": {
                        "query": "Lucene",
                        "boost": 2 
                    }
                }}
            ]
        }
    }
}

这些语句使用默认的 boost 值 1 。

这条语句更为重要,因为它有最高的 boost 值。

这条语句比使用默认值的更重要,但它的重要性不及 Elasticsearch 语句。

   boost 参数被用来提升一个语句的相对权重( boost 值大于 1 )或降低相对权重( boost值处于 0 到 1 之间),但是这种提升或降低并不是线性的,换句话说,如果一个 boost 值为 2 ,并不能获得两倍的评分 _score 。

相反,新的评分 _score 会在应用权重提升之后被 归一化 ,每种类型的查询都有自己的归一算法,细节超出了本书的范围,所以不作介绍。简单的说,更高的 boost 值为我们带来更高的评分 _score 。

如果不基于 TF/IDF 要实现自己的评分模型,我们就需要对权重提升的过程能有更多控制,可以使用 function_score 查询操纵一个文档的权重提升方式而跳过归一化这一步骤。

     

    Java代码:

  QueryBuilders.matchQuery("title", "dog").boost(2)

  

发表评论