美文网首页
Elasticsearch 7.x 深入【7】文档打分

Elasticsearch 7.x 深入【7】文档打分

作者: 孙瑞锴 | 来源:发表于2020-05-13 21:13 被阅读0次

1. 借鉴

极客时间 阮一鸣老师的Elasticsearch核心技术与实战
ElasticSearch7+Spark构建高相关性搜索服务&千人千面推荐系统
Lucene学习总结之六:Lucene打分公式的数学推导
【Elasticsearch】打分策略详解与explain手把手计算
官网 Theory Behind Relevance Scoring
对数计算器

2. 开始

数据准备:Elasticsearch 7.x 深入 数据准备

自定义打分

使用function score自定义打分

在查询结束后,对匹配的每个文档进行重新算分并排序

  • 如何来构建一个function_score的查询呢?其实就是将我们平时在query里面的部分移到function_score的query里面,加上一些余外的算法。我们来举个栗子,然后一步一步了解其算法规则,因为在functions中可以指定多个规则,我们先以一个规则的为例
GET /notes/_search
{
  "query": {
    "function_score": {
      "query": {
        "match": {
          "title": "elasticsearch hadoop canal"
        }
      },
      "functions": [
        {
          "field_value_factor": {
            "field": "grade"
          }
        }
      ]
    }
  }
}
  • 可以看到返回的结果中的分数已经被grade取代了
{
  "took" : 2,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 3,
      "relation" : "eq"
    },
    "max_score" : 98.08292,
    "hits" : [
      {
        "_index" : "notes",
        "_type" : "_doc",
        "_id" : "1",
        "_score" : 98.08292,
        "_source" : {
          "title" : "Elasticsearch",
          "content" : "About Elasticsearch",
          "grade" : 100
        }
      },
      {
        "_index" : "notes",
        "_type" : "_doc",
        "_id" : "2",
        "_score" : 3.9233167,
        "_source" : {
          "title" : "Hadoop",
          "content" : "About Hadoop",
          "grade" : 4.5
        }
      },
      {
        "_index" : "notes",
        "_type" : "_doc",
        "_id" : "3",
        "_score" : 3.9233167,
        "_source" : {
          "title" : "Canal",
          "content" : "About Canal",
          "grade" : 4.7
        }
      }
    ]
  }
}
  • 可以看到目前function_score的算分规则就是:match匹配的相关的分数*grade的数值

可以看到如果grade差距非常大时,分数差距特别大,有哪些参数可以调整它的离散程度呢?

变更分数1
  • 使用modifier:我们可以添加modifier属性来使分数更加平滑。它有以下备选值
释义
none 不使用
log 以10为底,新分值=老分值*log(filed的值)
log1p 以10为底,新分值=老分值*log(1 + filed的值)
log2p 以10为底,新分值=老分值*log(2 + filed的值)
ln 以e为底,新分值=老分值*ln( filed的值)
ln1p 以e为底,新分值=老分值*ln(1 + filed的值)
ln2p 以e为底,新分值=老分值*ln(2 + filed的值)
square 平方
sqrt 开方
reciprocal 倒数
  • 在说modifier之前,我们先说下上面所谓的新老分数。

    1. 老分数:说的是function_score的query部分的分数,也就是相关度分数,查看这一部分算分跟我们直接写query看到的分数是一样的。
    2. 新分数:就是说function_score返回的分数。
  • 我们使用modifier,来看看

GET /notes/_search
{
  "query": {
    "function_score": {
      "query": {
        "match": {
          "title": "elasticsearch hadoop canal"
        }
      },
      "functions": [
        {
          "field_value_factor": {
            "field": "grade",
            "modifier": "log1p"
          }
        }
      ]
    }
  }
}
  • 可以看到,就算grade的分支差别特别大,最后算的分数的离散程度不会特别高
{
  "took" : 0,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 3,
      "relation" : "eq"
    },
    "max_score" : 1.9658968,
    "hits" : [
      {
        "_index" : "notes",
        "_type" : "_doc",
        "_id" : "1",
        "_score" : 1.9658968,
        "_source" : {
          "title" : "Elasticsearch",
          "content" : "About Elasticsearch",
          "grade" : 100
        }
      },
      {
        "_index" : "notes",
        "_type" : "_doc",
        "_id" : "2",
        "_score" : 0.6855702,
        "_source" : {
          "title" : "Hadoop",
          "content" : "About Hadoop",
          "grade" : 4.5
        }
      },
      {
        "_index" : "notes",
        "_type" : "_doc",
        "_id" : "3",
        "_score" : 0.6855702,
        "_source" : {
          "title" : "Canal",
          "content" : "About Canal",
          "grade" : 4.7
        }
      }
    ]
  }
}
  • 嗯,我们来以这个例子的id为1,title是Elasticsearch的文档分数为例,手动计算一下分数到底是不是我们上面说的那一回事。
- - 补充说明
老分数 0.9808292 通过以下查询得来的分数:
GET /notes/_search
{
  "query": {
    "match": {
      "title": "elasticsearch hadoop canal"
      }
    }
}
计算公式为 新分值=老分值*log(1 + filed的值) 我们使用的modifier为log1p
我们计算的新分数的值: 0.9808292 * log(1 + 100) = 0.9808292 * 2.0043 = 1.96587597 -
es通过function_score查询出来的值: 1.9658968 -
  • 通过计算可以看到,跟我们计算的结果基本一致。(●´∀`●)ノ
变更分数2
  • 使用factor:我们可以添加factor属性来在modifier的基础上使其增加相应的权重,我们以modifier为log1p为例,同时设置factor为3,则最终得分的计算公式为:新分数=老分数*log(1 + factor * grade的分数)
GET /notes/_search
{
  "query": {
    "function_score": {
      "query": {
        "match": {
          "title": "elasticsearch hadoop canal"
        }
      },
      "functions": [
        {
          "field_value_factor": {
            "field": "grade",
            "modifier": "log1p",
            "factor": 2
          }
        }
      ]
    }
  }
}
  • 结果如下,ID为1的文档我计算的分数结果是2.25904581,可以看到分数也基本一致
{
  "took" : 0,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 3,
      "relation" : "eq"
    },
    "max_score" : 2.2590418,
    "hits" : [
      {
        "_index" : "notes",
        "_type" : "_doc",
        "_id" : "1",
        "_score" : 2.2590418,
        "_source" : {
          "title" : "Elasticsearch",
          "content" : "About Elasticsearch",
          "grade" : 100
        }
      },
      {
        "_index" : "notes",
        "_type" : "_doc",
        "_id" : "2",
        "_score" : 0.9359489,
        "_source" : {
          "title" : "Hadoop",
          "content" : "About Hadoop",
          "grade" : 4.5
        }
      },
      {
        "_index" : "notes",
        "_type" : "_doc",
        "_id" : "3",
        "_score" : 0.9359489,
        "_source" : {
          "title" : "Canal",
          "content" : "About Canal",
          "grade" : 4.7
        }
      }
    ]
  }
}
变更分数3
  • 使用score_mode: 控制functions内部的计算规则。这是什么意思呢?因为functions是一个数组,里面可以有多个函数。
释义 是否默认
Multiply match匹配的相关的分数*grade的数值
Sum match匹配的相关的分数+grade的数值
Max/Min 最大/最小;【取(match匹配的相关的分数)或(grade的数值)中的最大/最小值】
Replace 使用函数值【grade的数值】替换算分
  • 这里我们以sum为例
GET /notes/_search
{
  "_source": "", 
  "query": {
    "function_score": {
      "query": {
        "match": {
          "title": "elasticsearch hadoop canal"
        }
      },
      "functions": [
        {
          "field_value_factor": {
            "field": "grade",
            "modifier": "log2p",
            "factor": 10
          }
        },
        {
          "field_value_factor": {
            "field": "grade",
            "modifier": "log2p",
            "factor": 5
          }
        }
      ],
      "score_mode": "sum"
    }
  }
}
  • 返回结果:
{
  "took" : 1,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 3,
      "relation" : "eq"
    },
    "max_score" : 5.5922675,
    "hits" : [
      {
        "_index" : "notes",
        "_type" : "_doc",
        "_id" : "1",
        "_score" : 5.5922675,
        "_source" : { }
      },
      {
        "_index" : "notes",
        "_type" : "_doc",
        "_id" : "2",
        "_score" : 2.9088175,
        "_source" : { }
      },
      {
        "_index" : "notes",
        "_type" : "_doc",
        "_id" : "3",
        "_score" : 2.9088175,
        "_source" : { }
      }
    ]
  }
}
  • 嗯,我们来以这个例子的id为1,title是Elasticsearch的文档分数为例,我们再来手动计算一下。
- - 补充说明
老分数 0.9808292 通过以下查询得来的分数:
GET /notes/_search
{
  "query": {
    "match": {
      "title": "elasticsearch hadoop canal"
      }
    }
}
计算公式为 新分值=老分值 * (log(2 + 5* filed的值) + log(2 + 10* filed的值)) 我们使用的modifier为log2p
我们计算的新分数的值: 0.9808292 * (log(2 + 5 *100) + log(2 + 10 *100)) = 0.9808292 * (2.7007 + 3.0009) = 5.59229577 -
es通过function_score查询出来的值: 5.5922675 -
  • 通过计算可以看到,跟我们计算的结果基本一致。(●´∀`●)ノ
变更分数4
  • 使用boost_mode:控制query和functions部分的计算规则在上面我们看到分值的计算是使用相乘的方式:match匹配的相关的分数*grade的数值,使用boost mode则可以控制计算方式。
释义 是否默认
Multiply match匹配的相关的分数*grade的数值
Sum match匹配的相关的分数+grade的数值
Max/Min 最大/最小;【取(match匹配的相关的分数)或(grade的数值)中的最大/最小值】
Replace 使用函数值【grade的数值】替换算分
  • 这里我们以sum为例
GET /notes/_search
{
  "_source": "", 
  "query": {
    "function_score": {
      "query": {
        "match": {
          "title": "elasticsearch"
        }
      },
      "functions": [
        {
          "field_value_factor": {
            "field": "grade"
          }
        }
      ],
      "boost_mode": "sum"
    }
  }
}
  • 结果如下,可以看到score已经变为相加了:
{
  "took" : 0,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 1,
      "relation" : "eq"
    },
    "max_score" : 100.98083,
    "hits" : [
      {
        "_index" : "notes",
        "_type" : "_doc",
        "_id" : "1",
        "_score" : 100.98083,
        "_source" : { }
      }
    ]
  }
}
  • 这里我就不算了,如果不设置,默认为multiply,可以参照我们本篇前面的那个计算一下。

max_boost属性

  • 使用max_boost:设置分数在某个范围以内
GET /notes/_search
{
  "_source": "", 
  "query": {
    "function_score": {
      "query": {
        "match": {
          "title": "elasticsearch hadoop canal"
        }
      },
      "functions": [
        {
          "field_value_factor": {
            "field": "grade"
          }
        }
      ],
      "max_boost": 10
    }
  }
}
  • 结果如下,我们使用max_boost将分数规定在了10分以内
{
  "took" : 0,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 3,
      "relation" : "eq"
    },
    "max_score" : 9.808291,
    "hits" : [
      {
        "_index" : "notes",
        "_type" : "_doc",
        "_id" : "1",
        "_score" : 9.808291,
        "_source" : { }
      },
      {
        "_index" : "notes",
        "_type" : "_doc",
        "_id" : "2",
        "_score" : 3.9233167,
        "_source" : { }
      },
      {
        "_index" : "notes",
        "_type" : "_doc",
        "_id" : "3",
        "_score" : 3.9233167,
        "_source" : { }
      }
    ]
  }
}

random_score属性

  • random_score:一致性随机函数。设置seed属性,如果seed值保持一致,那查询出来的结果排序就是相同的。
GET /notes/_search
{
  "_source": "", 
  "query": {
    "function_score": {
      "query": {
        "match": {
          "title": "elasticsearch hadoop canal"
        }
      },
      "functions": [
        {
          "random_score": {
            "seed": 1231
          }
        }
      ]
    }
  }
}
  • 计算分值的函数
函数 释义
weight 设置权重
field value factor 使用该值来修改score
random 随机算分
衰减函数 以某个字段的值为标准,距离某个值越接近,得分越高
script 自定义脚本实现算分

查看打分详情

1. 在查询中添加_explain参数

GET /tmdb_movies/_search
{
  "explain": true, 
  "query": {
    "multi_match": {
      "query": "steve",
      "fields": ["title"]
    }
  }
}
  • 我们以查询结果的第一篇文档做例子


    得分分析

2. 使用_validate/query?explain

GET /tmdb_movies/_validate/query?explain
{
  "query": {
    "multi_match": {
      "query": "steve",
      "fields": ["title"]
    }
  }
}
  • 查询结果
{
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "failed" : 0
  },
  "valid" : true,
  "explanations" : [
    {
      "index" : "tmdb_movies",
      "valid" : true,
      "explanation" : "title:steve"
    }
  ]
}

3. 大功告成

相关文章

网友评论

      本文标题:Elasticsearch 7.x 深入【7】文档打分

      本文链接:https://www.haomeiwen.com/subject/kpgfghtx.html