Elasticsearch 4: 相关性检索和组合查询

Elasticsearch 1: 基本原理和概念
Elasticsearch 2: 管理索引和文档
Elasticsearch 3: 数据检索和分析
Elasticsearch 4: 相关性检索和组合查询
Elasticsearch 5: 聚集查询
Elasticsearch 6: 索引别名
Elasticsearch 集群
SpringBoot 整合 Elasticsearch

在全文检索中,检索结果与查询条件的相关性是一个极为重要的问题,优秀 的全文检索引擎应该将那些与查询条件相关性高的文档排在最前面。想象一下。 如果满足查询条件的文档成千上万,让用户在这些文档中再找出自己最满意的那 一条,这无异于再做一次人工检索。用户一般很少会有耐心在检索结果中翻到第 3 页,所以处理好检索结果的相关性对于检索引擎来说至关重要。
相关性问题有两方面问题要解决, 一是如何评价单个查询条件的相关性, 二是如何将多个查询条件的相关性组合起来。

文中使用的数据可以参考Elasticsearch 数据检索和分析导入数据相关内容

1. 相关性评分

  • 全文检索与数据库查询的一个显著区别, 就是它并不一定会根据查询条件 做完全精确的匹配。除了模糊查询以外,全文检索还会根据查询条件给文档的相 关性打分并排序,将那些与查询条件相关性高的文档排在最前面。相关性 ( Relevance)或相似性(Similarity)是指两个事物间相互关联的程度,在检索领城特指检索请求与检索结果之间的相关程度。在 Elaticsearch 返回的每条结果中都会 包含一个_ score 字段,这个字段的值就是当前文档匹配检索请求的相关性评分, 也可以称为相关度。
  • 解决相关性问题的核心是计算相关度的算法和模型,相关度算法和模型是全文检索最重要的技术之一。相关度算法和相关度模型并非完全相同的概念, 相关度模型可以认为是具有相同理论基础的算法集合。所以在实际应用时都是指 定到具体的相关度算法,相关度模型则是从理论层面对相关度算法的归类。

2. 相关度模型

  • Elasticsearch 支持多种相关度算法,它们通过类型名称来标识,包括 boolean、 BM25、DFR 等。这些算法分别归属于几种不同的理论模型,它们是布尔模型、向量空间模型、概率模型、语言模型等。

2.1 布尔模型

  • 布尔模型( Boolean Model)是最简单的相关度模型,最终的相关度只有 1 或 0 两种。如果检索中包含多个查询条件,则查询条件之间的相关度组合方式取决它们之间的逻辑运算符,即以逻辑运算中的与、或、非组合评分。文档的最终评分为 1 时会被添加到检索结果中,而评分为 0 时则不会出现在检索结果中。这与使用 SQL 语句查询数据库有些类似,完全根据查询条件决定结果,非此即彼。在 Elasticsearch 支持的相关度算法中,boolean 算法即采用布尔模型,有些地方也部分地采用了布尔模型。

2.2 向量空间模型

  • 向量空间模型(Vector Space Mode)组合多个相关度时采用的是基于向量的算法。在向量空间模型中,多个查询条件的相关度以向量的形式表示。向量实际上就是包含多个数的一维数组,例如[1, 2, 3, 4, 5, 6]就是一个 6 维向量,其中每个数字都代表一个查询条件的相关度。文档对于 n 个查询条件会形成一个 n 维的向量空间,如果定义 1 个查询条件最佳匹配的 n 维向量,那么与这个向量越接近则相关度越高。从向量的角度来看,就是两个向量之间的夹角越小相关度越高,所以 n 个相关度的组合就转换为向量之间夹角的计算。
  • 简单理解,可以只考虑一个二维向量,也就是查询条件只有两个,这样就可以将两个相关度映射到二维坐标图的X轴和Y轴上。假设两个查询条件权重相同, 那么最佳匹配值就可以设置为[1, 1]。如果某文档匹配了第一个条件,部分地匹配了第二个条件,则该文档的向量值为[1, 0.2]。将这两个向量绘制在二维坐标图中, 就得到了它们的夹角。

在这里插入图片描述

  • 对于多维向量来说,线性代数提供了余弦近似度算法,专门用于计算两个多维向量的夹角。(余弦相似性通过测量两个向量的夹角的余弦值来度量它们之间的相似性。 0 度角的余弦值是 1,而其他任何角度的余弦值都不大于 1;并且其最小值是-1。 从而两个向量之间的角度的余弦值确定两个向量是否大致指向相同的方向。两个向量有相同的指向时,余弦相似度的值为 1;两个向量夹角为 90°时,余弦相似度的值为 0;两个向量指向完全相反的方向时,余弦相似度的值为-1。这结果是与向量的长度无关的,仅仅与向量的指向方向相关。余弦相似度通常用于正空间, 因此给出的值为-1 到 1 之间。)
  • 注意这上下界对任何维度的向量空间中都适用,而且余弦相似性最常用于高维正空间。例如在信息检索中,每个词项被赋予不同的维度,而一个维度由一个向量表示,其各个维度上的值对应于该词项在文档中出现的频率。余弦相似度因此可以给出两篇文档在其主题方面的相似度。

2.3 概率模型

  • 概率模型是基于概率论构建的模型,BM25、 DFR、DFI 都属于概率模型中的一种实现算法, 背后有着非常严谨的概率理论依据。以其中最为流行的 BM25 为例,它背后的概率理论是贝叶斯定理,而这个定理在许多领城中都有广泛的应用。
  • BM25 法将检索出来的文档(D)分为相关文档®和不相关文档(NR)两类,使用 P(R|D)代表文档属于相关文档的概率,而使用 P(NR|D) 表示文档属于不相关文档 的概率,则当 P(R|D)>P(NR|D)时认为这个文档与用户查询相关。根据贝叶斯公式将 P(R|D)>P(NR|D)转换为对某个比值的计算。在此基础上再进行一此转换,就可以得到不同的相关度算法。

2.4 语言模型

  • 语言模型最早并不是应用于全文检索领域,而是应用于语音识别、机器翻译、 拼写检查等领域。在全文检索中,语言模型为每个文档建立不同的计算模型,用 以判断由文档生成某一查询条件的概率是多少,而这个概率的值就可以认为是相关度。可见,语言模型的与其他检索模型正好相反,其他检索模型都是从查询条件查找满足条件的文档,而语言模型则是根据文档推断可能的查询条件。

3. TF/IDF

  • 对于一篇几百字几千字的文章,如何生成足以准确表示该文章的特征向量呢?就像论文一样,摘要、关键词毫无疑问就是全篇最核心的内容,因此,我们要设法提取一篇文档的关键词,并对每个关键词计算其对应的特征权值,从而形成特征向量。这里涉及一个非常简单但又相当强大的算法,即 TF-IDF 算法。
  • TF/IDF 实际上两个影响相关度的因素,即 TF 和 IDF。其中,TF 是词项频率简称词频,指一个词项在当前文档中出现的次数,而 IDF 则是逆向文档频率,指词项在所有文档中出现的次数。
  • Elasticsearch 提供的几种算法中都或多或少有 TF/IDF 的思想,例如 BM25 算法虽然是通过概率论推导而来,但最终的计算公式与 TF/IDF 在本质上也是一致的。
  • TF/IDF 算法的核心思想是 TF 越高则相关度越高,而 IDF 越高相关度越低。 TF 对相关度的影响比较容易理解,但 IDF 为什么会在词项出现次数多的时候反而相关度低呢?
  • 举例来说,如果使用"elasticsearch 全文检索"两个词项做检索,文档中 “elasticsearch"出现次数高的文档比“全文检索”出现次数高的文档相关度要高。 这是因为“elasticsearch "是专业性比较强的词汇,更加专有,它在其他文档中出现的次数会比较少,也就是 IDF 低,而“全文检索”虽然也是专业性词汇,但它覆盖的面要比“elasticsearch " 更广泛,所以它在其他文档中出现的次数会比较高,也就是 IDF 高。换句话说,介绍 elasticsearch 的文章大概率会提到全文检索。 但介绍全文检索的文章则不一定会提到 elasticsearch,比如一篇介绍 MongoDB 的文章大概率会提到全文检索,但是这样的文章与“elasticsearch 全文检索”的 相关度不高。
  • 可见,在使用 TF/IDF 计算评分时必须要用到词项在文档中出现的频率,即词频。默认情况下文档 text 类型字段在编入索引时都会记录词频。
  • Elasticsearch 中的 classic 算法实际上是使用 Lucene 的实用评分函数( Practical Scoring Function) ,这个评分函数结合了布尔模型、TF/IDF 和向量空间模型来共同计算分值。该算法是早期 Elatisearch 运算相关度的算法,现在已经改为 BM25 了。

4. BM25

  • BM25 是 Best Match25 的简写,由于最早应用于一个名为 Okapi 的系统中, 所以很多文献中也称之为 Okapi BM25。BM25 算法被认为是当今最先进的相关度算法之 ,Elasticsearch 文档字段的默认相关度算法就是采用 BM25,它属于概率模型,依据贝叶斯公式,经过一系列的严格推导以后,得出了一个关于 IDF 的公式

在这里插入图片描述

  • 同时在这个基础上,最终的公式上加入了对 TF、当前文档的长度、词频饱和度、长度归一化等因素的考虑
    在这里插入图片描述

4.1 词频饱和度

  • 所谓词频饱和度指的是当词频超过一定数量之后,它对相关度的影响将趋于饱和。换句话说,词频 10 次的相关度比词频 1 次的分值要大很多,但 100 次与 10 次之间差距就不会那么明显了。在 BM25 算法中,控制词频饱和度的参数是 k1,默认值为 1.2。参数 k1 的值越小词频对相关度的影响就会越快趋于饱和,而值越大词频饱和度变化越慢。
  • 举例来说,如果将 k1 设置为 1,词频达到 10 时就会趋于饱和;而当 k1 设置为 100 时词频在 100 时才会趋于饱和。一般来说 k1 的取值范围为[1.2, 2.0]

4.2 长度归一化

  • 一般来说, 查询条件中的词项出现在较短的文本中,比出现在较长的文本中对结果的相关性影响更大。
  • 举例来说,如果一篇文章的标题中包含 elasticsearch,那么这篇文章是专门介绍elasticsearch的可能性比只在文章内容中出现elasticsearch的可能要高很多。 但这种比较其实是建立在两个不同的字段上,而在实际检索时往往是针对相同的字段做比较。
  • 比如在两篇文章的标题中都出现了 elasticsearch, 那么哪一篇文章的相关度更高呢?
  • BM25 针对这种情况对文本长度做了所谓的归一化处理,即考虑当前文档字段的文本长度与所有文档的字段平均长度的比值,而这个比值就是长度归一化因素。
  • 为了控制长度归一化对相关度的影响,在长度归一化中加了一个控制参数 b。 这个值的取值范围为[0.0, 1.0],取值 0.0 时会禁用归一化,而取值 1.0 则会完全启用归一化,默认值为 0. 75。

5. 相关度解释

  • 相关度算法可通过 text 或 keyword 类型字段的 similarity 参数修改,也就是说相关度算法不针对整个文档而是针对单个字段,它的默认值是 BM25。
  • 当然 Elasticsearch 相关度评分比这里介绍的内容要复杂得多,可以通过在查询时添加 explain 参数查看评分解释。例如:
POST /kibana_sample_data_logs/_search
{
    "query": {
        "match": {
            "message": "chrome"
        }
    },
    "explain": true
}

在这里插入图片描述

  • 除此之外,通过_explain 接口也可以实现类似的功能,不同的是_explain 接口查看的是单个文档与检索条件的相关度评分解释。例如:
POST /kibana_sample_data_logs/_explain/To9w9H4Bo-eQ1KgLEh-h
{
    "query": {
        "match": {
            "message": "chrome"
        }
    }
}
  • 我们可以大致了解下 Elasticsearch 是如何进行评分的:

在这里插入图片描述

6. 相关度权重

  • 在一些情况下需要将某些字段的相关度权重提升,以增加这些字段对检索结果相关性评分的影响。比如,同时使用对文章标题 tile 字段和文章内容 content 字段做检索,tile 字段化相关性评分中的权重应该比 content 字段高一些, 这时就可以将 tile 字段的相关度评分权重提高。
  • 所以相关度权重提升一般都是在多个查询条件时设置。可以通过boost 参数提升相关度权重。
  • boost 参数可以在创建索引时直接设置给字段,也可以在执行检索时动态更 改。如果不做更改,boost 参数的默认值为 1。但并非所有类型的字段都可以设 置 boost,在创建索引时设置 boost 参数并不是一个好的方法,因为这个参数在索引创建以后就不能再更改而降低了灵活性,所以在 Elasticsearch 版本 5 中就已经被废止。
  • 所以更好的方式是检索时提升查询条件的相关度权重,几乎所有 DSL 查询都支持通过 boost 参数设置查询条件的相关度权重。例如:
POST /kibana_sample_data_flights/_search
{
    "query": {
        "range": {
            "AvgTicketPrice": {
                "gte": 1000,
                "lte": 1200,
                "boost": 2
            }
        }
    }
}

在这里插入图片描述

  • 通过boost参数设置查询条件的相关度权重主要用于多查询条件时调整每个查询条件在相关度计算中的权重。

7. 组合查询与相关度组合

  • 相关性问题不仅要解决单个查询条件的相关度计算,还要考虑如何将多个查询条件产生的相关度组合起来。而相关度组合问题主要出现在组合查询中,所以就要了解组合查询及相关度组合问题
  • 组合查询可以将通过某种逻辑将子查询组合起来,实现对多个字段与多个查询条件的任意组合。组合查询组合的子查询不仅可以是基于词项或基于全文的子查询,也可以是另一个组合查询。
  • 单纯从组合查询的使用上来看,组合查询并不复杂,复杂的是组合多个子查询相关度的逻辑,这也是它们的核心区别之一。

7.1 bool 组合查询

  • bool 组合查询将一组布尔类型子句组合起来, 形成个大的布尔条件。 通过 SQL 语言查询数据时,如果一条数据不满足 where 子句的查询条件,这条记录将不会作为结果返回。
  • 但 Elasticsearch 的 bool 组合查询则不同,在它的子句中,一些子句的确会决定文档是否会作为结果返回,而另一些子句则不决定文档是否可以作为结果,但会影响到结果的相关度。
  • bool组合查询可用的布尔类型子句包括must、filter、should 和must _not 四 种,它们接收参数值的类型为数组。
    must 查询结果中必须要包含的内容,影响相关度
    filter 查询结果中必须要包含的内容,不会影响相关度
    should 查询结果非必须包含项,包含了会提高分数,影响相关度
    must_not 查询结果中不能包含的内容,不会影响相关度
  • 可见,filter 和 must _not 单纯只用于过滤文档,而它们对文档相关度没有任何影响。换句话说,这两种子句对查询结果的排序没有作用。在这四种子句中, should 子句的情况有些复杂。首先它的执行结果影响相关度,但在是否过滤结果上则取决于上下文。当 should 子句与 must 子句或 filter 子句同时出现在子句中时,should 子句将不会过滤结果。也就是说,在这种情况下,即使 should 子句不满足,结果也会返回。例如:
POST /kibana_sample_data_logs/_search
{
    "query": {
        "bool": {
            "must": [
                {
                    "match": {
                        "message": "firefox"
                    }
                }
            ],
            "should": [
                {
                    "term": {
                        "geo. src": "CN"
                    }
                },
                {
                    "term": {
                        "geo. dest": "CN"
                    }
                }
            ]
        }
    }
}

在这里插入图片描述

  • 只有 message 字段包含 firefox 词项的日志文档才会被返回,而 geo 的 src 字 段和 dest 字段是否为 CN 只影响相关度,当然相关度越高的肯定排在前面,可以通过下面的例子观察到:
POST /kibana_sample_data_logs/_search
{
    "query": {
        "bool": {
            "must": [
                {
                    "match": {
                        "message": "firefox"
                    }
                }
            ],
            "should": [
                {
                    "term": {
                        "geo.src": "CN"
                    }
                },
                {
                    "term": {
                        "geo.dest": "CN"
                    }
                }
            ]
        }
    },
    "sort": [
        {
            "_score": {
                "order": "asc"
            }
        }
    ]
}

在这里插入图片描述

  • 但是如果在查询条件中将 must 子句删除,那么 should 子句就至少要满足有 一条。should 子句需要满足的个数由query的minimum_ should_match参数决定, 默认情况下它的值为 1。
  • 布尔查询在计算相关性得分时,采取了匹配越多分值越高的策略。由于 filter 和 must_not 不参与分值运算,所以文档的最后得分是 must 和 should 子句的相关性分值相加后返回给用户。同时还可以试试:
POST /kibana_sample_data_logs/_search
{
    "query": {
        "bool": {
            "must": [
                {
                    "match": {
                        "message": "firefox"
                    }
                }
            ],
            "should": [
                {
                    "term": {
                        "geo. src": "CN"
                    }
                },
                {
                    "term": {
                        "geo. dest": "CN"
                    }
                }
            ],
            "filter": {
                "term": {
                    "extension": "zip"
                }
            }
        }
    }
}

在这里插入图片描述

  • 可以发现,最终返回的结果中又过滤了一次,extension 的值为 zip 的才会返回。

7.2 dis_max 组合查询

  • dis_max 查询也是种组合查询, 只是它在计算相关性度时与 bool 查询不同。 dis_max 查询在计算相关性分值时,会在子查询中取最大相关性分值为最终相关性分值结果,而忽略其他子查询的相关性得分。dis_max 查询通过 queries 参数接收对象的数组。例如:
POST /kibana_sample_data_logs/_search
{
    "query": {
        "dis_max": {
            "queries": [
                {
                    "match": {
                        "message": "firefox"
                    }
                },
                {
                    "term": {
                        "geo. src": "CN"
                    }
                },
                {
                    "term": {
                        "geo. dest": "CN"
                    }
                }
            ]
        }
    }
}

在这里插入图片描述

  • 在多数情况下,完全不考虑其他字段的相关度可能并不合适,所以可以使用 tie_ breaker 参数设置其他字段参与相关度运算的系数。这个系数会在运算最终相关度时乘以其他字段的相关度,再加上最大得分就得到最终的相关度了。所以一般来说,tie_ breaker 应该小于 1,默认值为 0。例如在示例的返回结果中,即使文档 message 和 geo 字段都满足查询条件它也不定会排在最前面。可添加 tie_breaker 参数并设置为 0.7。
POST /kibana_sample_data_logs/_search
{
    "query": {
        "dis_max": {
            "queries": [
                {
                    "match": {
                        "message": "firefox"
                    }
                },
                {
                    "term": {
                        "geo. src": "CN"
                    }
                },
                {
                    "term": {
                        "geo. dest": "CN"
                    }
                }
            ],
            "tie_breaker": 0.7
        }
    }
}

在这里插入图片描述

  • 在添加了 tie _breaker 参数后,相关度非最高值字段在参与最終相关度结果 时的权重就降低为 0.7。但它们对结果排序会产生影响,完全满足条件的文档将排在结果最前面。

7.3 constant_score 查询

  • constant_score 查询返回结果中文档的相关度为固定值,这个固定值由 boost 参数设置,默认值为 1.0。constant score 查询只有两个参数 filter 和 boost, 前者与 bool 组合查询中的 filter 完全相同,仅用于过滤结果而不影响分值。
POST /kibana_sample_data_logs/_search
{
    "query": {
        "constant_score": {
            "filter": {
                "match": {
                    "geo.src": "CN"
                }
            },
            "boost": 1.3
        }
    }
}

在这里插入图片描述

  • 由于示例中通过 boost 参数设置了相关度,所以满足查询条件文档的 score 值将都是 1.3,match_all 查询也可以当成是一种特殊类型的 constant_score 查询, 它会返回索引中所有文档,里面每个文档的相关度都是 1.0。它的作用和 boost 参数类似,组合查询时调整子查询在相关度计算中的权重

7.4 boosting 查询

  • boosting 查询通过 positive 子句设置满足条件的文档,这类似于 bool 查询中的 must 子句,只有满足 positive 条件的文档才会被返回。boosting 查询通过 negative 子句设置需要排除文档的条件,这类似于 bool 查询中的 must _not 子句。但与 bool 查询不同的是,boosting 查询不会将满足 negative 条件的文档从返回结果中排除,而只是会拉低它们的相关性
POST /kibana_sample_data_logs/_search
{
    "query": {
        "boosting": {
            "positive": {
                "term": {
                    "geo.src": "CN"
                }
            },
            "negative": {
                "term": {
                    "geo. dest": "CN"
                }
            },
            "negative_boost": 0.2
        }
    },
    "sort": [
        {
            "_score": "asc"
        }
    ]
}

在这里插入图片描述

  • 在示例中,参数 negative_ boost 设置了一个系数,当满足 negative 条件时 相关度会乘以这个系数作为最终分值,所以这个值应该小于 1 而大于等于 0。如果 geo_src 为 CN 的文档相关度为 1.6,那么 geo. Dest 字段也是 CN 的文档相关度就需要再乘以 0.2,所以最终相关度为 0. 32。

7.5 function_score 查询

  • function_score 查询提供了一组计算查询结果相关度的不同函数,通过为查询条件定义不同打分函数实现文档检索相关性的自定义打分机制。查询条件通过 function_score 的 query 参数设置,而使用的打分函数则使用 functions 参数设置 例如:
POST /kibana_sample_data_logs/_search
{
    "query": {
        "function_score": {
            "query": {
                "query_string": {
                    "fields": [
                        "message"
                    ],
                    "query": "(firefox 6.0a1) OR (chrome 11.0.696.50)"
                }
            },
            "functions": [
                {
                    "weight": 2
                },
                {
                    "random_score": {
                        
                    }
                }
            ],
            "score_mode": "max",
            "boost_mode": "avg"
        }
    }
}

在这里插入图片描述

  • function_score 查询在运算相关度时,首先会通过 functions 指定的打分函数算出每份文档的得分。如果指定了多个打分函数,它们打分的结果会根据 score_mode 参数定义的模式组合起来。
  • 以示例为例,functions 参数定义了两个打分函数,random_score 函数会在 0-1 之间产生一个随机数, 而 weight 函数则会以指定的值为相关性分值。由于 score_mode 参数设置的值为 max,即从所有评分函数运算结果中取最大值,而 weight 值为 2,它将永远大于 random_ score 产生的值,所以评分函数最终给出的分值也将永远是 2。
  • score_mode 包括以下几个选项 multiply、sum、avg、first、 max、min,通过名称很容易判断它们的含义,分别是在所有评分函数的运算结果中取它们的乘积、 和、平均值、首个值、最大值和最小值。
  • 打分函数运算的相关性评分会与 query 参数中查询条件的相关度组合起来,组合的方式通过 boost_mode 参数指定,它的默认值与 score_mode 一样都是 multiply。boost_mode 参数的可选值与 score_mode 也基本一致,但没有 first 而多了一个 replace,代表使用评分函数计算结果代替查询分值。
  • 可见 function_score 是一种在运算相关度上非常灵活的组合查询,这种灵活性主要体现在它提供了一组打分函数,以及组合这些打分函数的灵活方式。打分函数包括 weight、 random_score、field_value_factor 以及衰减函数等等。

7.5.1 field_value_factor 函数

  • field_value_factor 函数在计算相关度时允许加入某一字段作为干扰因子,比如:
POST /kibana_sample_data_flights/_search
{
    "query": {
        "function_score": {
            "query": {
                "bool": {
                    "must": [
                        {
                            "match": {
                                "OriginCountry": "CN"
                            }
                        },
                        {
                            "match": {
                                "DestCountry": "US"
                            }
                        }
                    ]
                }
            },
            "field_value_factor": {
                "field": "AvgTicketPrice",
                "factor": 0.001,
                "modifier": "reciprocal",
                "missing": 1000
            }
        }
    }
}

在这里插入图片描述

  • 这个示例实际上是将相关度按票价由低到高的次序做了权重的提升,票价越低最终的相关度越高。这相当是找出所有从中国到美国的航班,并按票价由低到高的次序排序。
  • 在示例中,field_value_factor 打分函数通过 field 参数设置了干扰字段为 AvgTicketPrice,而 factor 则是为干扰字段设置的调整因子,它会与字段值相乘后再参与接下来的运算。Missing 表示如果字段值是丢失的,默认被使用的值。 modifier 参数就有些复杂了,它代表了干扰字段与调整因子相乘的结果如何参与相关度运算。在示例中给出的是 reciprocal,代表取倒数 1/x。
  • modifier 可用运算方法除了 reciprocal 以外还有很多:
    none:不做其他运算
    log:取对数
    log1p:加 1 后再取对数,目的是为了防止字段值为 0-1 时计算结果为负数
    log2p:加 2 后再取对教 ln:自然对数
    ln1p:加 1 后取自然对数
    ln2p:加 2 后取自然对数
    square:取平方
    sqrt:取平方根
    reciprocal:代表取倒数。

7.5.2 衰减函数

  • 衰减函数是一组通过递减方式计算相关度的函数,它们会从指定的原始点开始对相关度做衰减,离原始点距离越远相关度就越低。衰减函数中的原始点是指某字段的具体值,由于要计算其他文档与该字段值的距离,所以要求衰减函数原始点的字段类型必须是数值、日期或地理坐标中的一种。
  • 举例来说,如果在 2022 年 2 月 24 日前后系统运行出现异常,所以对这个日期前后的数据比较感兴趣,就可以按如下形式发送请求:
POST /kibana_sample_data_flights/_search
{
    "query": {
        "function_score": {
            "query": {
                "match": {
                    "OriginCityName": "Beijing"
                }
            },
            "gauss": {
                "timestamp": {
                    "origin": "2022-02-24",
                    "scale": "7d",
                    "offset": "1d",
                    "decay": 0.3
                }
            }
        }
    }
}

在这里插入图片描述

  • 在示例中使用的衰减函数为高斯函数,定义原始点使用的字段为 timestmp, 而具体的原始点则通过 origin 参数定义在了 2022 年 2 月 24 日。offset 参教定义 了在 1 天的范围内相关度不衰减,也就是说 2022 年 2 月 23-25 日相关度不衰减。 scale 参数和 decay 参数则共同决定了衰减的程度,前者定义了衰减的跨度范围, 而后者则定义了衰减多少。以示例中的设置为例,代表的含义是 7 天后的文档相 关度衰减至 0.3 倍。
  • 在衰减函数除了高斯函数 gauss 以外,还有线性函数 linear 和指数函数 exp 两种。它们在使用上与高斯函数完全相同。如果将这几种衰减函数以图形画出来就会发现,它们在衰减的平滑度上有着比较明显的区别。

在这里插入图片描述

8. 单查询条件下的相关度组合

  • 组合查询一般由多个查询条件组成,所以在计算相关度时都要考虑以何种方式组合相关度。而很多的查询都只针对一个字段设置查询条件,所以只有相关度权重提升问题而没有相关度组合问题。但有些查询,比如 multi_match 查询可以针对多个字段设置查询条件,所以它们在计算相关度时也需要考虑组合多个相关度的问题。
  • multi_match 查询具有一个 type 参数,用于指定针对多字段检索时的执行逻辑及相关度组合方法。type 参数有 5 个可选值,即 best_fields、 most_fields、 cross_ fields、phrase 和 phrase_prefix。 例如:
POST /kibana_sample_data_flights/_search
{
    "query": {
        "multi_match": {
            "query": "CN",
            "fields": [
                "OriginCountry^2",
                "DestCountry"
            ],
            "type": "best_fields"
        }
    }
}

8.1 best_fields、phrase 与 phrase_prefix 类型

  • best_fields 类型在执行时会将与字段匹配的文档都检索出来,但在计算相关度时会取得分最高的作为整个查询的相关度。
  • 例如在示例中,第一个查询通过"OrignCountny^2"的形式将 OriginCountry 字段的相关度权重提升到 2,所以这个字段相关度会高于 DestCountry 字段。
  • 在 best_fields 类型下执行检索时,DestCountry 字段对最终相关度就不会再有影响。通过查看返回结果也可以看到,OriginCountry 字段为 CN 的文档相关度都相同,即使 DestCountry 字段也是 CN,文档的相关度也不会提升。
  • best_fields 适用于用户希望匹配条件全部出现在一个字段中的情况,比如在文章标题和文章内容中同时检索 elasticseareh 和 logstash 时,如果在文章标题或是文章内容中同时出现了两个词项,该文章在相关度就会高于其他文章。比如说有两份文档:
  • 文档 1:
{
    "title": "Quick brown rabbits",
    "body": "Brown rabbits are commonly seen."
}
  • 文档 2:
{
    "title": "Keeping pets healthy",
    "body": "My quick brown fox eats rabbits on a regular basis."
}
  • 用户输入了"brown fox"来搜索文档,文档 2 匹配的更好一些,因为它的 body 字段同时包含了用户寻找的两个单词。
  • 在相关度计算上 dis_max 查询(在计算相关性分值时, 会在子查询中取最大相关性分值为最终相关性分值结果,而忽略其他子查询的相关性得分)与 best_fields 类型很像,事实上,best_fields 类型的查询在执行时会转化为 dis_max 查询,例如示例在执行时会转化为
POST /kibana_sample_data_flights/_search
{
    "query": {
        "dis_max": {
            "queries": [
                {
                    "match": {
                        "OriginCountry": {
                            "query": "CN",
                            "boost": 2
                        }
                    }
                },
                {
                    "match": {
                        "DestCountry": "CN"
                    }
                }
            ]
        }
    }
}
  • dis_max 有一个参数 tie_breaker, 可以设置非最高值相关度参与最终相关度运算的系数,multi_match 中使用 best_fields 类型时也可以使用这个参数。
  • phrase 与 phrase_ prefix 类型在执行逻辑上与 best_fields 完全相同,只是在 转换为 dis_max 时 queries 查询中的子查询会使用 phrase 或 phrase_prefix 而不是 match。

8.2 most_fields 类型

  • most_fields 类型在计算相关度时会将所有相关度累加起来,然后再除以相关度的个数以用到它们的平均值作为最终的相关度。
POST /kibana_sample_data_flights/_search
{
    "query": {
        "multi_match": {
            "query": "CN",
            "fields": [
                "OriginCountry^2",
                "DestCountry"
            ],
            "type": "most_fields"
        }
    }
}
  • 它会将 OriginCountry 和 DestCountry 两个字段匹配 CN 时计算出的相关度累加,然后再用累加和除以 2 作为最终的相关度。所以只有当两个字段都匹配了 CN,最终的相关度才会更高。这在效果上相当于将出发地和目的地都是中国的文档排在了最前面,所以适用于希望检索出多个字段中同时都包含相同词项的检索。在实现上,most_fields 类型的查询会被转化为 bool 查询的 should 子句,上 面的示例实际被转化为:
POST /kibana_sample_data_flights/_search
{
    "query": {
        "bool": {
            "should": [
                {
                    "match": {
                        "OriginCountry": {
                            "query": "CN",
                            "boost": 2
                        }
                    }
                },
                {
                    "match": {
                        "DestCountry": "CN"
                    }
                }
            ]
        }
    }
}

在这里插入图片描述

8.3 cross_fields 类型

  • 如果查询条件中设置了多个词项,best_fields 类型和 most_ fields 类型都支持通过 operator 参数设置词项之间的逻辑关系,即 and 和 or。 但它们在设置 operator 时是针对字段级别的面不是针对词项级别的
POST /kibana_sample_data_logs/_search
{
    "query": {
        "multi_match": {
            "query": "firefox success",
            "fields": [
                "message",
                "tags"
            ],
            "type": "best_fields",
            "operator": "and"
        }
    }
}

在这里插入图片描述

  • 示例设置的查询条件为 firefox 和 success 两个词项,而匹配字段也是两个 messape 和 tags。当 operator 设置为 and 时, 在 best_fields 类型下这意昧着两个字段中需要至少有一个同时包含 firefox 和 success 两个词项,而这样的日志文档并不存在。
  • 而在 cross_fields 类型则可以理解为将文档的中 message 和 tags 字段组合为 一个大的字段,然后在大的字段中搜索词条 firefox 和 success。这样的话只要 firefox 和 success 在大字段中均出现过,就认为这个文档符合条件。
POST /kibana_sample_data_logs/_search
{
    "query": {
        "multi_match": {
            "query": "firefox success",
            "fields": [
                "message",
                "tags"
            ],
            "type": "cross_fields",
            "operator": "and"
        }
    }
}

在这里插入图片描述

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值