栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 前沿技术 > 大数据 > 大数据系统

深耕ElasticSearch - 基于best

深耕ElasticSearch - 基于best

文章目录
    • 1. 多字符串查询
    • 2. 单字符串查询
    • 3. 最佳字段策略实现多字段查询
    • 4. 最佳字段查询调优
    • 5. multi_match实现最佳字段查询

1. 多字符串查询
GET /_search
{
  "query": {
    "bool": {
      "should": [
        { "match": { "title":  "War and Peace" }},
        { "match": { "author": "Leo Tolstoy"   }}
      ]
    }
  }
}

bool 查询采取more-matches-is-better匹配越多越好的方式,所以每条 match 语句的评分结果会被加在一起,从而为每个文档提供最终的分数 _score 。能与两条语句同时匹配的文档比只与一条语句匹配的文档得分要高。

2. 单字符串查询

bool 查询是多语句查询的主干。它的适用场景很多,特别是当需要将不同查询字符串映射到不同字段的时候。问题在于,目前有些用户期望将所有的搜索项堆积到单个字段中,并期望应用程序能为他们提供正确的结果。

对于多词(multiword)、多字段(multifield)查询来说,不存在简单的万能方案。当用户输入了单个字符串查询的时候,通常会遇到以下3种情形:

1、最佳字段(best_fields):

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

2、 多数字段(most_fields):

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

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

3、混合字段(cross_fields):

对于某些实体,我们需要在多个字段中确定其信息,单个字段都只能作为整体的一部分:

  • Person: first_name 和 last_name (人:名和姓)
  • Book: title 、 author 和 description (书:标题、作者、描述)
  • Address: street 、 city 、 country 和 postcode (地址:街道、市、国家和邮政编码)

在这种情况下,我们希望在任何这些列出的字段中找到尽可能多的词,这有如在一个大字段中进行搜索,这个大字段包括了所有列出的字段。

上述所有都是多词、多字段查询,但每个具体查询都要求使用不同策略。

3. 最佳字段策略实现多字段查询

3.1 最佳字段策略

假设有个网站允许用户搜索博客的内容,以下面两篇博客内容文档为例:

PUT /blog/_doc/1
{
    "title": "Quick brown rabbits",
    "content":  "Brown rabbits are commonly seen."
}

PUT /blog/_doc/2
{
    "title": "Keeping pets healthy",
    "content":  "My quick brown fox eats rabbits on a regular basis."
}

用户输入词组Brown fox然后点击搜索按钮。事先,我们并不知道用户的搜索项是会在 title 还是在 content 字段中被找到,但是,用户很有可能是想搜索相关的词组。用肉眼判断,文档 2 的匹配度更高,因为它同时包括要查找的两个词。现在运行以下 bool 查询:

GET /blog/_search
{
    "query": {
        "bool": {
            "should": [
                { "match": { "title": "Brown fox" }},
                { "match": { "content":  "Brown fox" }}
            ]
        }
    }
}
"hits" : [
    {
        "_index" : "blog",
        "_type" : "_doc",
        "_id" : "1",
        "_score" : 0.90425634,
        "_source" : {
            "title" : "Quick brown rabbits",
            "content" : "Brown rabbits are commonly seen."
        }
    },
    {
        "_index" : "blog",
        "_type" : "_doc",
        "_id" : "2",
        "_score" : 0.77041256,
        "_source" : {
            "title" : "Keeping pets healthy",
            "content" : "My quick brown fox eats rabbits on a regular basis."
        }
    }
]

但是我们发现查询的结果是文档 1 的评分更高,为了理解导致这样的原因,需要回想一下 bool 是如何计算评分的:

  1. 它会执行 should 语句中的两个查询。
  2. 加和两个查询的评分。
  3. 乘以匹配语句的总数。
  4. 除以所有语句总数(这里为:2)。

① 简单计算文档1的评分:

文档 1 的两个字段都包含 brown 这个词,所以两个 match 语句都能成功匹配并且有一个评分:

 { "match": { "title": "Brown fox" }}     //假如评分为1.2
 { "match": { "content":"Brown fox" }}    //假如评分为1.1

两个分数的和为1.1 + 1.2 = 2.3,match query的总数为2,query的总数为2,因此文档1的评分为:2.3*2/2=2.3

② 简单计算文档2的评分:

文档 2 的 content 字段同时包含 brown 和 fox 这两个词,但 title 字段没有包含任何词:

 { "match": { "title": "Brown fox" }}     //评分为0
 { "match": { "content":"Brown fox" }}    //假如评分为2.4

两个分数的和为0 + 2.3 = 2.3,match query的总数为1,query的总数为2,因此文档1的评分为:2.4*1/2=1.2因此文档2比文档1有更低的评分。

如果不是简单将每个字段的评分结果加在一起,而是将最佳匹配字段的评分作为查询的整体评分,结果会怎样?

这样返回的结果可能是:同时包含 brown 和 fox 的单个字段比反复出现相同词语的多个不同字段有更高的相关度。比如文档2中content字段的评分最高为2.4,而文档1中title字段的评分最高为1.2,此时就会使得文档2的整体评分为2.4比文档1的整体评分1.2要高。要想解决这个问题就需要使用dis_max 查询。

3.2 dis_max 查询

不使用 bool 查询,可以使用 dis_max 即分离最大化查询 。分离最大化查询指的是:将任何与任一查询匹配的文档作为结果返回,但只将最佳匹配的评分作为查询的评分结果返回:

GET /blog/_search
{
    "query": {
        "dis_max": {
            "queries": [
                { "match": { "title": "Brown fox" }},
                { "match": { "content":  "Brown fox" }}
            ]
        }
    }
}

得到我们想要的结果为:

"hits" : [
    {
        "_index" : "blog",
        "_type" : "_doc",
        "_id" : "2",
        "_score" : 0.77041256,
        "_source" : {
            "title" : "Keeping pets healthy",
            "content" : "My quick brown fox eats rabbits on a regular basis."
        }
    },
    {
        "_index" : "blog",
        "_type" : "_doc",
        "_id" : "1",
        "_score" : 0.6931472,
        "_source" : {
            "title" : "Quick brown rabbits",
            "content" : "Brown rabbits are commonly seen."
        }
    }
]
4. 最佳字段查询调优

4.1 最佳字段策略查询调优

PUT /blog/_doc/1
{
    "title": "Quick brown rabbits",
    "content":  "Brown rabbits are commonly seen."
}
PUT /blog/_doc/2
{
    "title": "Keeping pets healthy",
    "content":  "My quick brown fox eats rabbits on a regular basis."
}

当用户搜索 quick pets 时会发生什么呢?在前面的例子中,两个文档都包含词 quick ,但是只有文档 2 包含词 pets ,两个文档中都不具有同时包含两个词的相同字段。

如下,一个简单的 dis_max 查询会采用单个最佳匹配字段,而忽略其他的匹配:

"hits" : [
    {
        "_index" : "blog",
        "_type" : "_doc",
        "_id" : "1",
        "_score" : 0.6931472,                     // 分数 0.6931472
        "_source" : {
            "title" : "Quick brown rabbits",
            "content" : "Brown rabbits are commonly seen."
        }
    },
    {
        "_index" : "blog",
        "_type" : "_doc",
        "_id" : "2",
        "_score" : 0.6931472,                    // 分数 0.6931472
        "_source" : {
            "title" : "Keeping pets healthy",
            "content" : "My quick brown fox eats rabbits on a regular basis."
        }
    }
]

文档2同时匹配 title 和 content 字段,而文档1只有一个字段title匹配,但两个文档的评分是完全相同的。

我们可能期望同时匹配 title 和 content 字段的文档比只与一个字段匹配的文档的相关度更高,但事实并非如此,因为 dis_max 查询只会简单地使用单个最佳匹配语句的评分 _score 作为整体评分。为了解决这个问题,可以指定 tie_breaker 这个参数。

4.2 tie_breaker 参数

可以通过指定 tie_breaker 这个参数将其他匹配语句的评分也考虑其中:

GET /blog/_search
{
  "query": {
    "dis_max": {
      "queries": [
         { "match": { "title": "Quick pets" }},
         { "match": { "content": "Quick pets"}}
      ],
      "tie_breaker": 0.3
    }
  }
}

结果如下:

"hits" : [
    {
        "_index" : "blog",
        "_type" : "_doc",
        "_id" : "2",
        "_score" : 0.87613803,      // 文档2的评分增加了
        "_source" : {
            "title" : "Keeping pets healthy",
            "content" : "My quick brown fox eats rabbits on a regular basis."
        }
    },
    {
        "_index" : "blog",
        "_type" : "_doc",
        "_id" : "1",
        "_score" : 0.6931472,       // 文档1的评分仍然为 0.6931472
        "_source" : {
            "title" : "Quick brown rabbits",
            "content" : "Brown rabbits are commonly seen."
        }
    }
]

tie_breaker 参数提供了一种 dis_max 和 bool 之间的折中选择,它的评分方式如下:

  1. 获得最佳匹配语句的评分 _score 。
  2. 将其他匹配语句的评分结果与 tie_breaker 相乘。
  3. 对以上评分求和并规范化。

有了 tie_breaker ,会考虑所有匹配语句,但最佳匹配语句依然占最终结果里的很大一部分。

tie_breaker 可以是 0 到 1 之间的浮点数,其中 0 代表使用 dis_max 最佳匹配语句的普通逻辑, 1 表示所有匹配语句同等重要。最佳的精确值需要根据数据与查询调试得出,但是合理值应该与零接近(处于 0.1 - 0.4 之间),这样就不会颠覆 dis_max 最佳匹配性质的根本。

5. multi_match实现最佳字段查询

5.1 multi_match查询

multi_match 查询为能在多个字段上反复执行相同查询提供了一种便捷方式。

multi_match 多匹配查询的类型有多种,其中的三种与我们介绍的三个场景对应,即: best_fields 、 most_fields 和 cross_fields (最佳字段、多数字段、跨字段)。

默认情况下,查询的类型是 best_fields ,这表示它会为每个字段生成一个 match 查询,然后将它们组合到 dis_max 查询的内部,如下:

GET /blog/_search
{
  "query": {
    "dis_max": {
      "queries":  [
        {
          "match": {
            "title": {
              "query": "Quick brown fox",
              "minimum_should_match": "30%"   // 查询关键词中必须匹配30%才可
            }
          }
        },
        {
          "match": {
            "content": {
              "query": "Quick brown fox",
              "minimum_should_match": "30%"
            }
          }
        }
      ],
      "tie_breaker": 0.3
    }
  }
}

minimum_should_match,主要是用来去长尾的,比如你搜索5个关键词,但是很多结果是只匹配1个关键词的,其实跟你想要的结果相差甚远,这些结果就是长尾。minimum_should_match可以控制搜索结果的精准度,只有匹配一定数量的关键词的数据,才能返回。

上面这个查询用 multi_match 重写成更简洁的形式:

GET /blog/_search
{
  "query": {
      "multi_match": {
      "query":                "Quick brown fox",
      "type":                 "best_fields", 
      "fields":               [ "title", "content" ],
      "tie_breaker":          0.3,
      "minimum_should_match": "30%" 
    }
  }
}

5.2 查询字段名称的模糊匹配

字段名称可以用模糊匹配的方式给出:任何与模糊模式正则匹配的字段都会被包括在搜索条件中,例如可以使用以下方式同时匹配 book_title 、 chapter_title 和 section_title (书名、章名、节名)这三个字段:

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

5.3 提升单个字段的权重

可以使用 ^ 字符语法为单个字段提升权重,在字段名称的末尾添加 ^boost ,其中 boost 是一个浮点数:

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

chapter_title 这个字段的 boost 值为 2 ,而其他两个字段 book_title 和 section_title 字段的默认 boost 值为 1

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/674166.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号