你的位置:首页 > 信息动态 > 新闻中心
信息动态
联系我们

深耕ElasticSearch - 基于most_fields策略实现多字段查询

2021/12/19 21:48:39

1. 最佳字段和多数字段的区别

1、最佳字段(best_fields):

当搜索词语具体概念的时候,比如 “brown fox” ,词组比各自独立的单词更有意义。文档在相同字段中包含的词越多越好,评分也来自于最匹配字段。

2、 多数字段(most_fields):

为了对相关度进行微调,常用的一个技术就是将相同的数据索引到不同的字段,它们各自具有独立的分析链。一个字段可以包括未经词干提取过的原词,另一个字段包括其他词源、口音。

主字段可能包括它们的词源、同义词以及变音词或口音词,被用来匹配尽可能多的文档。相同的文本被索引到其他字段,以提供更精确的匹配。其他字段是作为匹配每个文档时提高相关度评分的信号, 匹配字段越多则越好。

2. 多数字段策略

全文搜索被称作是召回率与精确率的战场,目的是在结果的第一页中为用户呈现最为相关的文档:

  • 召回率:返回所有的相关文档
  • 精确率:不返回无关文档

为了提高召回率的效果,我们扩大搜索范围——不仅返回与用户搜索词精确匹配的文档,还会返回我们认为与查询相关的所有文档。如果一个用户搜索 “quick brown box” ,一个包含词语 fast foxes 的文档被认为是非常合理的返回结果。

提高全文相关性精度的常用方式是为同一文本建立多种方式的索引,每种方式都提供了一个不同的相关度信号 signal 。主字段会以尽可能多的形式的去匹配尽可能多的文档。举个例子,我们可以进行以下操作:

  • 使用词干提取来索引 jumpsjumpingjumped 样的词,将 jump 作为它们的词根形式。这样即使用户搜索 jumped ,也还是能找到包含 jumping 的匹配的文档。
  • 将同义词包括其中,如 jumpleaphop
  • 移除变音或口音词:如 éstaestáesta 都会以无变音形式 esta 来索引。

要实现多字段映射,首先要做的事情就是对我们的字段索引两次:一次使用词干模式以及一次非词干模式。为了做到这点,采用multifields来实现:

DELETE /blog

PUT /blog
{
  "settings": { "number_of_shards": 1 }, 
  "mappings": {
    "properties": {
        "title": { 
        "type":     "text",
        "analyzer": "english", // title 字段使用 english 英语分析器来提取词干。 	
        "fields": {
          "std":   { 
            "type":     "text",
            "analyzer": "standard" // title.std 字段使用 standard 标准分析器,所以没有词干提取。
          }
        }
      }
    }
  }
}

接着索引一些文档:

PUT /blog/_doc/1
{ "title": "My rabbit jumps" }

PUT /blog/_doc/2
{ "title": "Jumping jack rabbits" }

这里用一个简单 match 查询 title 标题字段是否包含 jumping rabbits

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

因为有了 english 分析器,这个查询是在查找以 jumprabbit 这两个被提取词的文档。两个文档的 title 字段都同时包括这两个词,所以两个文档得到的评分也相同:

"hits" : [
    {
        "_index" : "blog",
        "_type" : "_doc",
        "_id" : "1",
        "_score" : 0.36464313,
        "_source" : {
            "title" : "My rabbit jumps"
        }
    },
    {
        "_index" : "blog",
        "_type" : "_doc",
        "_id" : "2",
        "_score" : 0.36464313,
        "_source" : {
            "title" : "Jumping jack rabbits"
        }
    }
]

如果只是查询 title.std 字段,那么只有文档 2 是匹配的。尽管如此,如果同时查询两个字段,然后使用 bool 查询将评分结果合并,那么两个文档都是匹配的( title 字段的作用),而且文档 2 的相关度评分更高( title.std 字段的作用)。我们希望将所有匹配字段的评分合并起来,所以使用 most_fields 类型。这让 multi_match 查询用 bool 查询将两个字段语句包在里面,而不是使用 dis_max 查询。

GET /blog/_search
{
 "query": {
    "multi_match": {
      "query":  "jumping rabbits",
      "type":   "most_fields", 
      "fields": [ "title", "title.std" ]
    }
  }
}
"hits" : [
    {
        "_index" : "blog",
        "_type" : "_doc",
        "_id" : "2",
        "_score" : 1.7509375,
        "_source" : {
            "title" : "Jumping jack rabbits"
        }
    },
    {
        "_index" : "blog",
        "_type" : "_doc",
        "_id" : "1",
        "_score" : 0.36464313,
        "_source" : {
            "title" : "My rabbit jumps"
        }
    }
]

用广度匹配字段 title 包括尽可能多的文档——以提升召回率——同时又使用字段 title.std 作为信号将相关度更高的文档置于结果顶部。

3. 提高字段对于最终评分的贡献

每个字段对于最终评分的贡献可以通过自定义值 boost 来控制。比如,使 title 字段更为重要,这样同时也降低了其他信号字段的作用:

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

title 字段的 boost 的值为 10 使它比 title.std 更重要。