[Elasticsearch] 多字段搜索 (三) - multi_match查询和多数字段

multi_match查询

multi_match查询提供了一个简便的方法用来对多个字段执行相同的查询。html

NOTEjson

存在几种类型的multi_match查询,其中的3种正好和在"了解你的数据"一节中提到的几种类型相同:best_fields,most_fields以及cross_fields。app

默认状况下,该查询以best_fields类型执行,它会为每一个字段生成一个match查询,而后将这些查询包含在一个dis_max查询中。下面的dis_max查询:elasticsearch

{
  "dis_max": {
    "queries":  [
      {
        "match": {
          "title": {
            "query": "Quick brown fox",
            "minimum_should_match": "30%"
          }
        }
      },
      {
        "match": {
          "body": {
            "query": "Quick brown fox",
            "minimum_should_match": "30%"
          }
        }
      },
    ],
    "tie_breaker": 0.3
  }
}

能够经过multi_match简单地重写以下:ide

{
    "multi_match": {
        "query":                "Quick brown fox",
        "type":                 "best_fields", 
        "fields":               [ "title", "body" ],
        "tie_breaker":          0.3,
        "minimum_should_match": "30%" 
    }
}

注意到以上的type属性为best_fields。 minimum_should_match和operator参数会被传入到生成的match查询中。ui

在字段名中使用通配符

字段名能够经过通配符指定:任何匹配了通配符的字段都会被包含在搜索中。你能够经过下面的查询来匹配book_title,chapter_title以及section_title字段:spa

{
    "multi_match": {
        "query":  "Quick brown fox",
        "fields": "*_title"
    }
}

提高个别字段

个别字段能够经过caret语法(^)进行提高:仅须要在字段名后添加^boost,其中的boost是一个浮点数:code

{
    "multi_match": {
        "query":  "Quick brown fox",
        "fields": [ "*_title", "chapter_title^2" ] 
    }
}

chapter_title字段的boost值为2,而book_title和section_title字段的boost值为默认的1。htm

多数字段(Most Fields)

全文搜索是一场召回率(Recall) - 返回全部相关的文档,以及准确率(Precision) - 不返回无关文档,之间的战斗。目标是在结果的第一页给用户呈现最相关的文档。排序

为了提升召回率,咱们会广撒网 - 不只包括精确匹配了用户搜索词条的文档,还包括了那些咱们认为和查询相关的文档。若是一个用户搜索了"quick brown fox",一份含有fast foxes的文档也能够做为一个合理的返回结果。

若是咱们拥有的相关文档仅仅是含有fast foxes的文档,那么它会出如今结果列表的顶部。可是若是咱们有100份含有quick brown fox的文档,那么含有fast foxes的文档的相关性就会变低,咱们但愿它出如今结果列表的后面。在包含了许多可能的匹配后,咱们须要确保相关度高的文档出如今顶部。

一个用来调优全文搜索相关性的经常使用技术是将一样的文本以多种方式索引,每一种索引方式都提供了不一样相关度的信号(Signal)。主要字段(Main field)中含有的词条的形式是最宽泛的(Broadest-matching),用来尽量多的匹配文档。好比,咱们能够这样作:

  • 使用一个词干提取器来将jumps,jumping和jumped索引成它们的词根:jump。而后当用户搜索的是jumped时,咱们仍然可以匹配含有jumping的文档。
  • 包含同义词,好比jump,leap和hop。
  • 移除变音符号或者声调符号:好比,ésta,está和esta都会以esta被索引。

可是,若是咱们有两份文档,其中之一含有jumped,而另外一份含有jumping,那么用户会但愿第一份文档的排序会靠前,由于它含有用户输入的精确值

咱们能够经过将相同的文本索引到其它字段来提供更加精确的匹配。一个字段能够包含未被提取词干的版本,另外一个则是含有变音符号的原始单词,而后第三个使用了shingles,用来提供和单词邻近度相关的信息。这些其它字段扮演的角色就是信号(Signals),它们用来增长每一个匹配文档的相关度分值。可以匹配的字段越多,相关度就越高。

若是一份文档可以匹配具备最宽泛形式的主要字段(Main field),那么它就会被包含到结果列表中。若是它同时也匹配了信号字段,它会获得一些额外的分值用来将它移动到结果列表的前面。

咱们会在本书的后面讨论同义词,单词邻近度,部分匹配以及其余可能的信号,可是咱们会使用提取了词干和未提取词干的字段的简单例子来解释这个技术。

多字段映射(Multifield Mapping)

第一件事就是将咱们的字段索引两次:一次是提取了词干的形式,一次是未提取词干的形式。为了实现它,咱们会使用多字段(Multifields),在字符串排序和多字段中咱们介绍过:

DELETE /my_index

PUT /my_index
{
    "settings": { "number_of_shards": 1 }, 
    "mappings": {
        "my_type": {
            "properties": {
                "title": { 
                    "type":     "string",
                    "analyzer": "english",
                    "fields": {
                        "std":   { 
                            "type":     "string",
                            "analyzer": "standard"
                        }
                    }
                }
            }
        }
    }
}

title字段使用了english解析器进行词干提取。 title.std字段则使用的是standard解析器,所以它没有进行词干提取。

下一步,咱们会索引一些文档:

PUT /my_index/my_type/1
{ "title": "My rabbit jumps" }

PUT /my_index/my_type/2
{ "title": "Jumping jack rabbits" }

如下是一个简单的针对title字段的match查询,它查询jumping rabbits:

GET /my_index/_search
{
   "query": {
        "match": {
            "title": "jumping rabbits"
        }
    }
}

它会变成一个针对两个提干后的词条jump和rabbit的查询,这要得益于english解析器。两份文档的title字段都包含了以上两个词条,所以两份文档的分值是相同的

{
  "hits": [
     {
        "_id": "1",
        "_score": 0.42039964,
        "_source": {
           "title": "My rabbit jumps"
        }
     },
     {
        "_id": "2",
        "_score": 0.42039964,
        "_source": {
           "title": "Jumping jack rabbits"
        }
     }
  ]
}

若是咱们只查询title.std字段,那么只有文档2会匹配。可是,当咱们查询两个字段并将它们的分值经过bool查询进行合并的话,两份文档都可以匹配(title字段也匹配了),而文档2的分值会更高一些(匹配了title.std字段):

GET /my_index/_search
{
   "query": {
        "multi_match": {
            "query":  "jumping rabbits",
            "type":   "most_fields", 
            "fields": [ "title", "title.std" ]
        }
    }
}

在上述查询中,因为咱们想合并全部匹配字段的分值,所以使用的类型为most_fields。这会让multi_match查询将针对两个字段的查询子句包含在一个bool查询中,而不是包含在一个dis_max查询中

{
  "hits": [
     {
        "_id": "2",
        "_score": 0.8226396, 
        "_source": {
           "title": "Jumping jack rabbits"
        }
     },
     {
        "_id": "1",
        "_score": 0.10741998, 
        "_source": {
           "title": "My rabbit jumps"
        }
     }
  ]
}

此时,文档2的分值比文档1的高许多。

咱们使用了拥有宽泛形式的title字段来匹配尽量多的文档 - 来增长召回率(Recall),同时也使用了title.std字段做为信号来让最相关的文档可以拥有更靠前的排序(译注:增长了准确率(Precision))。

每一个字段对最终分值的贡献能够经过指定boost值进行控制。好比,咱们能够提高title字段来让该字段更加剧要,这也减少了其它信号字段的影响:

GET /my_index/_search
{
   "query": {
        "multi_match": {
            "query":       "jumping rabbits",
            "type":        "most_fields",
            "fields":      [ "title^10", "title.std" ] 
        }
    }
}
相关文章
相关标签/搜索