Elasticsearch搜索之explain评分分析

Lucene的IndexSearcher提供一个explain方法,能够解释Document的Score是怎么得来的,具体每一部分的得分都可以详细地打印出来。这里用一个中文实例来纯手工验算一遍Lucene的评分算法,并且结合Lucene的源码做一个解释。

首先是测试用例,我使用“北京东路”来检索一个含有address域的文档。

然后是是输出,注意它有缩进,代表一个个的层级,下面以测试环境数据作为举例:

{
        "value" : 0.7271681,
        "description" : "max of:",
        "details" : [ {
          "value" : 0.7271681,
          "description" : "sum of:",
          "details" : [ {
            "value" : 0.43069553,
            "description" : "weight(address:北京 in 787) [PerFieldSimilarity], result of:",
            "details" : [ {
              "value" : 0.43069553,
              "description" : "score(doc=787,freq=1.0), product of:",
              "details" : [ {
                "value" : 0.34374008,
                "description" : "queryWeight, product of:",
                "details" : [ {
                  "value" : 5.0118747,
                  "description" : "idf(docFreq=2104, maxDocs=116302)"
                }, {
                  "value" : 0.06858513,
                  "description" : "queryNorm"
                } ]
              }, {
                "value" : 1.2529687,
                "description" : "fieldWeight in 787, product of:",
                "details" : [ {
                  "value" : 1.0,
                  "description" : "tf(freq=1.0), with freq of:",
                  "details" : [ {
                    "value" : 1.0,
                    "description" : "termFreq=1.0"
                  } ]
                }, {
                  "value" : 5.0118747,
                  "description" : "idf(docFreq=2104, maxDocs=116302)"
                }, {
                  "value" : 0.25,
                  "description" : "fieldNorm(doc=787)"
                } ]
              } ]
            } ]
          }, {
            "value" : 0.29647252,
            "description" : "weight(address:东路 in 787) [PerFieldSimilarity], result of:",
            "details" : [ {
              "value" : 0.29647252,
              "description" : "score(doc=787,freq=1.0), product of:",
              "details" : [ {
                "value" : 0.2851919,
                "description" : "queryWeight, product of:",
                "details" : [ {
                  "value" : 4.158218,
                  "description" : "idf(docFreq=4942, maxDocs=116302)"
                }, {
                  "value" : 0.06858513,
                  "description" : "queryNorm"
                } ]
              }, {
                "value" : 1.0395545,
                "description" : "fieldWeight in 787, product of:",
                "details" : [ {
                  "value" : 1.0,
                  "description" : "tf(freq=1.0), with freq of:",
                  "details" : [ {
                    "value" : 1.0,
                    "description" : "termFreq=1.0"
                  } ]
                }, {
                  "value" : 4.158218,
                  "description" : "idf(docFreq=4942, maxDocs=116302)"
                }, {
                  "value" : 0.25,
                  "description" : "fieldNorm(doc=787)"
                } ]
              } ]
            } ]
          } ]
        } ]
      }

这个看起来可真是头疼,尝试解释一下:

首先,需要学习Lucene的评分计算公式——

分值计算方式为查询语句q中每个项t与文档d的匹配分值之和,当然还有权重的因素。其中每一项的意思如下表所示:


表3.5


评分公式中的因子


评分因子


描 述


tf(t in d)


项频率因子——文档(d)中出现项(t)的频率


idf(t)


项在倒排文档中出现的频率:它被用来衡量项的“唯一”性.出现频率较高的term具有较低的idf,出现较少的term具有较高的idf


boost(t.field in d)


域和文档的加权,在索引期间设置.你可以用该方法 对某个域或文档进行静态单独加权


lengthNorm(t.field in d)


域的归一化(Normalization)值,表示域中包含的项数量.该值在索引期间计算,并保存在索引norm中.对于该因子,更短的域(或更少的语汇单元)能获得更大的加权


coord(q,d)


协调因子(Coordination factor),基于文档中包含查询的项个数.该因子会对包含更多搜索项的文档进行类似AND的加权


queryNorm(q)


每个査询的归一化值,指毎个查询项权重的平方和

总匹配分值的计算

具体到上面的测试来讲,地址字段address匹配了二个词条,先分别计算每个词条对应的分值,然后相加,最后结果= ("北京") 0.43069553+ (“东路”)0.29647252=0.7271681 (结果舍入)。

查询语句在某个field匹配分值计算

这个0.43069553是如何来的呢?这是词条“北京”在field中的分值=查询权重queryWeight * 域权重fieldWeight  即   0.34374008*1.2529687=0.43069553。

同埋“东路”这个词条在field中的分值=查询权重queryWeight * 域权重fieldWeight  即   0.2851919*1.0395545=0.29647252

queryWeight的计算

queryWeight的计算可以在TermQuery$TermWeight.normalize(float)方法中看到计算的实现:

public void normalize(float queryNorm) {

              this.queryNorm = queryNorm;

             //原来queryWeight 为idf*t.getBoost(),现在为queryNorm*idf*t.getBoost()。

            queryWeight *= queryNorm;

            value = queryWeight * idf;

}

其实默认情况下,queryWeight = idf * queryNorm,因为Lucene中默认的boost = 1.0。

以“北京”这个词条为例,查询权重queryWeight = idf * queryNorm,即 0.34374008 = 5.0118747*0.06858513。

idf的计算

idf是项在倒排文档中出现的频率,计算方式为

/** Implemented as <code>log(numDocs/(docFreq+1)) + 1</code>. */

@Overrid

public float idf(long docFreq, long numDocs) {

return (float)(Math.log(numDocs/(double)(docFreq+1)) + 1.0);

docFreq是根据指定关键字进行检索,检索到的Document的数量,我们测试“北京”词条的docFreq=2104;

numDocs是指索引文件中总共的Document的数量,对应explain结果中的maxDocs,我们测试的maxDocs=116302。

用计算器验证一下,没有错误,这里就不啰嗦了。

fieldWeight的计算

fieldWeight = tf * idf * fieldNorm

tf和idf的计算参考前面的,fieldNorm的计算在索引的时候确定了,此时直接从索引文件中读取,这个方法并没有给出直接的计算。

如果使用DefaultSimilarity的话,它实际上就是lengthNorm,域越长的话Norm越小,在org/apache/lucene/search/similarities/DefaultSimilarity.java里面有关于它的计算:

public float lengthNorm(FieldInvertState state) {

final int numTerms;

if (discountOverlaps)

numTerms = state.getLength() - state.getNumOverlap();

else

numTerms = state.getLength();

return state.getBoost() * ((float) (1.0 / Math.sqrt(numTerms)));

}

这个我就不再验算了,每个域的Terms数量开方求倒数乘以该域的boost得出最终的结果。

时间: 2024-12-24 03:56:11

Elasticsearch搜索之explain评分分析的相关文章

ElasticSearch评分分析 explian 解释和一些查询理解

ElasticSearch评分分析 explian 解释和一些查询理解 按照es-ik分析器安装了ik分词器.然后创建了一个索引用来演示,创建索引:PUT /index_ik_test.索引的结构如下: GET index_ik_test/_mapping { "index_ik_test": { "mappings": { "fulltext": { "properties": { "content":

Elasticsearch系列---常见搜索方式与聚合分析

概要 本篇主要介绍常见的6种搜索方式.聚合分析语法,基本是上机实战,可以和关系型数据库作对比,如果之前了解关系型数据库,那本篇只需要了解搜索和聚合的语法规则就可以了. 搜索响应报文 以上篇建立的music索引为例,我们先看看搜索结果的属性都有哪些 { "took": 1, "timed_out": false, "_shards": { "total": 5, "successful": 5, "

Elasticsearch+logstash+kibana实现日志分析(实验)

Elasticsearch+logstash+kibana实现日志分析(实验) 一.前言 Elastic Stack(旧称ELK Stack),是一种能够从任意数据源抽取数据,并实时对数据进行搜索.分析和可视化展现的数据分析框架.(hadoop同一个开发人员) java 开发的开源的全文搜索引擎工具 基于lucence搜索引擎的 采用 restful - api 标准的 高可用.高扩展的分布式框架 实时数据分析的 官方网站: https://www.elastic.co/products 为什么

Elasticsearch搜索结果返回不一致问题

一.背景 这周在使用Elasticsearch搜索的时候遇到一个,对于同一个搜索请求,会出现top50返回结果和排序不一致的问题.那么为什么会出现这样的问题? 后来通过百度和google,发现这是因为Elastcisearch的分布式搜索特性导致.Elasticsearch在搜索时,会循环的选择主分片和其副本中的一个来计算和返回搜索结果,而由于主分片和副本中相关统计信息的不同,从而导致了同一个搜索串的评分的不一致,进而导致排序不一样.而造成这种主分片和副本统计信息不一致的具体原因,是因为文档删除

通过HTTP RESTful API 操作elasticsearch搜索数据

通过HTTP RESTful API 操作elasticsearch搜索数据

elasticsearch搜索提示

elasticsearch搜索提示(补全)接口需要新增suggest字段并设type为:completion,结合到scrapy,修改es_types.py文件: from datetime import datetime from elasticsearch_dsl import DocType, Date, Nested, Boolean, analyzer, InnerObjectWrapper, Completion, Keyword, Text, Integer from elasti

网站基于ElasticSearch搜索的优化笔记 PHP

基本情况就是,媒体.试题.分类,媒体可能有多个试题,一个试题可能有多个分类,分类为三级分类加上一个综合属性.通过试题名称.分类等搜索查询媒体. 现在的问题为,搜索结果不精确,部分搜索无结果,ES的数据结构不满足搜索需求.解决方案就是,重构ES数据结构,采用父子关系的方式,建立media和question两个type. 全程使用https://github.com/mobz/elasticsearch-head,这个进行ES的管理和查看,很方便. 从ES的说明可以看出,ES是面向文档,其实所有的数

ElasticSearch搜索介绍四

ElasticSearch搜索 最基础的搜索: curl -XGET http://localhost:9200/_search 返回的结果为: { "took": 2, "timed_out": false, "_shards": { "total": 16, "successful": 16, "failed": 0 }, "hits": { "tota

Elasticsearch搜索之most_fields分析

顾名思义,most_field就是匹配词干的字段数越多,分数越高,也可设置权重boost. 下面是简易公式(详细评分算法请参考:http://m.blog.csdn.net/article/details?id=50623948): score=match_field1_score*boost+match_field2_score*boost+...match_fieldN_score*boost. 在很多情况下,这种搜索很有效,但存在一个弱点,就是当文档中的字段冗余信息过多,将会影响那些文档比