Relevance

控制相关度

ES使用布尔模型(Boolean model)查找匹配文档,并用一个实用评分函数(practical scoring function)的公式来计算相关度。这个公式借鉴了词频/逆向文档频率(term frequency/inverse document frequency)和向量空间模型(vector space model),同时加入了一些现代的新特性,如协调因子(coordination factor),字段长度归一化(field length normalization),以及词或查询语句权重提升。
1、布尔模型
只是在查询中使用AND/OR/NOT这样的条件来插好匹配的文档

full AND text AND search AND (elasticsearch OR lucene)

会将所有包括词 full 、 text 和 search ,以及 elasticsearch 或 lucene 的文档作为结果集。
2、词频/逆向文档频率(TF/IDF)
词的权重由三个因素决定
--词频,词在文档中出现的“频度”越高,权重越高
如果不在意刺在某个字段中出现的频度,只在意是否出现过,可以禁用词频统计。(index_options设置为docs,not_analyzed的默认值就是这个)
--逆向文档频率,词在集合所用文档里出现的“频率”是多少,频次越高,权重越低。
--字段长度归一值,字段的长度是多少?字段越短,字段的权重越高。
("norms": { "enabled": false }禁用归一值,not_analyzed的默认值就是这个)
以上三个因素,是在索引时计算并存储的
3、向量空间模型
查询通常不止一个词,所以需要一种合并多词权重的方式,也就是向量空间模型
向量空间模型提供一种比较多词查询的方式,单个评分代表文档与查询的匹配程度,为了做到这点,这个模型将文档和查询都以向量的形式表示:
向量实际上就是包含多个数的一堆数组(每个数都代表一个词的权重),例如:

[1,2,5,22,3,8]

4、查询归一化
试图将查询“归一化”,这样就能将两个不同的查询结果相比较
5、查询协调
协调因子(coord),可以为那些查询词包含度高的文档提供奖励,文档里出现的查询词越多,越有机会成为好的匹配结果。
例如查询 quick brown fox

文档里有 fox → 评分: 1.5
文档里有 quick fox → 评分: 3.0
文档里有 quick brown fox → 评分: 4.5

协调因子将评分与文档里匹配词的数量相乘,然后除以查询所有词的数量。

文档里有 fox → 评分: 1.5 * 1 / 3 = 0.5
文档里有 quick fox → 评分: 3.0 * 2 / 3 = 2.0
文档里有 quick brown fox → 评分: 4.5 * 3 / 3 = 4.5

6、索引时字段层权重提升
……

查询时权重提升

boost参数,用来影响权重
当在多个索引中搜索时,可以使用indices_boost来提升整个索引的权重

使用查询结构修改相关度

越深的查询层级,对相关度的影响就越小

Not Quite Not

假如我们检索Apple,是想搜索苹果公司,就需要把一些例如水果的东西排查掉(must_not),但是排除掉这些词之后,可能丢失了与苹果公司相关的文档。
权重提升查询能解决这个问题(包含结果,但是降低排名)

GET /_search
{
  "query": {
    "boosting": {
      "positive": {
        "match": {
          "text": "apple"
        }
      },
      "negative": {
        "match": {
          "text": "pie tart fruit crumble tree"
        }
      },
      "negative_boost": 0.5
    }
  }
}

boosting查询接受positive和negative查询。只有匹配positive查询的文档罗列出来,还匹配negative查询的文档通过文档的原始_score月negative_boost相乘的方式降级

忽略TF/IDF

有时候我们不关心TF/IDF,只想知道一个词是否在某个字段出现过。
constant_score查询可以包含查询或过滤,为任意一个匹配的文档指定评分1,也可以使用boost提升权重,但是协调因子和查询归一化因子仍然会被考虑在内。
--
默认情况下not_analyzed字段会禁用字段长度归一值,并将index_options设为docs,禁用词频。但是每个词的倒排文档频率仍然会被考虑。同样可以使用constant_score来解决这个问题

function_score查询

允许为每个与主查询匹配的文档应用一个函数,以达到改变甚至完全替换原始查询评分_score的目的。
也能用过滤器对结果的子集应用不同的函数,这样既能高效评分,又能利用过滤器缓存
ES预定义函数:
weight:当weight为2时,最终结果为2*_score;
field_value_factor:使用这个值来修改_score
random_score:为每个用户都使用一个不同的随机评分对结果排序,但对某一具体用户来说,看到的顺序始终是一致的。
衰减函数:linear、exp、gauss
将浮动值结合到_score中,例如结合publist_date
如果需求超出以上范围时,用自定义脚本可以完全控制评分计算。(script_score)

按受欢迎度提升权重

function_score可以与field_value_factor结合使用,例如:

GET /blogposts/post/_search
{
  "query": {
    "function_score": { (1)
      "query": { (2)
        "multi_match": {
          "query":    "popularity",
          "fields": [ "title", "content" ]
        }
      },
      "field_value_factor": { (3)
        "field": "votes" (4)
      }
    }
  }
}

1、function_score查询将主查询和函数包括在内。
2、主查询优先执行
3、field_value_factor函数会被应用到每个与主查询匹配的文档
4、每个文档的votes字段都必须有值供function_score计算。如果没有值就必须指定missing
新的评分:new_score = _score * votes
然而_score通常处于0到10之间,有votes为10的会掩盖掉全文评分,为0的评分会被置0
一种更好的方式是使用modifier,例如:

GET /blogposts/post/_search
{
  "query": {
    "function_score": {
      "query": {
        "multi_match": {
          "query":    "popularity",
          "fields": [ "title", "content" ]
        }
      },
      "field_value_factor": {
        "field":    "votes",
        "modifier": "log1p" (1)
      }
    }
  }
}

此时新的评分:new_score = old_score * log(1 + number_of_votes)
modifier可以为:none (默认状态)、 log 、 log1p 、 log2p 、 ln 、 ln1p 、 ln2p 、 square 、 sqrt 以及 reciprocal
还可以使用factor参数
new_score = old_score * log(1 + factor * number_of_votes)
boost_mode可以控制函数与查询评分_score合并后的结果
接受的参数值:

multiply、sum、min、max、replace

参数max_boost可以限制一个函数的最大效果

GET /blogposts/post/_search
{
  "query": {
    "function_score": {
      "query": {
        "multi_match": {
          "query":    "popularity",
          "fields": [ "title", "content" ]
        }
      },
      "field_value_factor": {
        "field":    "votes",
        "modifier": "log1p",
        "factor":   0.1
      },
      "boost_mode": "sum",
      "max_boost":  1.5 (1)
    }
  }
}

无论field_value_factor 函数的结果如何,最大就是1.5

过滤集提升权重

过滤器可以将结果划分为多个子集(每个特性一个过滤器),可以为每个子集使用不同的函数。

GET /_search
{
  "query": {
    "function_score": {
      "filter": { (1)
        "term": { "city": "Barcelona" }
      },
      "functions": [ (2)
        {
          "filter": { "term": { "features": "wifi" }}, (3)
          "weight": 1
        },
        {
          "filter": { "term": { "features": "garden" }}, (3)
          "weight": 1
        },
        {
          "filter": { "term": { "features": "pool" }}, (3)
          "weight": 2 (4)
        }
      ],
      "score_mode": "sum", (5)
    }
  }
}

1、function_score查询有个filter过滤器而不是query查询
2、functions关键字存储着一个将被应用的函数列表
3、函数会被应用于和filter过滤器匹配的文档
4、pool比其他特性更重要,所以它有更高weight
5、score_mode指定各个函数的值进行组合运算的方式。

随机评分

相同_score的文档每次都会以相同次序出现,此时需要引入一些随机性(只针对_score为整数这样的情况)。
random_score函数会输出一个0到1之间的数,当种子seed值相同时,生成的随机结果是一致的。

GET /_search
{
  "query": {
    "function_score": {
      "filter": {
        "term": { "city": "Barcelona" }
      },
      "functions": [
        {
          "filter": { "term": { "features": "wifi" }},
          "weight": 1
        },
        {
          "filter": { "term": { "features": "garden" }},
          "weight": 1
        },
        {
          "filter": { "term": { "features": "pool" }},
          "weight": 2
        },
        {
          "random_score": { (1)
            "seed":  "the users session id" (2)
          }
        }
      ],
      "score_mode": "sum"
    }
  }
}

1、random_score没用任何过滤器filter,所以会被应用到所有文档
2、将用户的ID作为种子seed,会让该用户的随机始终保持一致。

越近越好

function_score衰减函数linear、exp、gauss(线性、指数和高斯函数),他们可以操作数值、时间以及经纬度地理坐标点这样的字段,能接受以下参数:
origin:中心点或字段可能的最佳值,与origin相同的文档_score为满分1.0
scale:衰减率,即一个文档从origin下落时,_score改变的速度
decay:从origin衰减到scale所得的评分_score,默认0.5
offset:以origin为中心点,为其设置一个非0的偏移量offset覆盖一个范围,而不只是单个原点。在范围内origin±offset内的所有_score都是1.0
样例:户希望租一个离伦敦市中心近( { "lat": 51.50, "lon": 0.12} )且每晚不超过 £100 英镑的度假屋

GET /_search
{
  "query": {
    "function_score": {
      "functions": [
        {
          "gauss": {
            "location": { (1)
              "origin": { "lat": 51.5, "lon": 0.12 },
              "offset": "2km",
              "scale":  "3km"
            }
          }
        },
        {
          "gauss": {
            "price": { (2)
              "origin": "50", (3)
              "offset": "50",
              "scale":  "20"
            }
          },
          "weight": 2 (4)
        }
      ]
    }
  }
}

脚本评分

function_score内置函数无法满足应用场景,可以使用script_score函数自行实现逻辑
ES5之后推荐脚本语言:painless

可插拔的相似度算法

较为高深……无法直视

更改相似度

……

调试相关度是最后10%要做的事情

……

你可能感兴趣的:(Relevance)