Elasticsearch搜索之explain评分分析

摘要:
Lucene的IndexSearcher提供了一种解释方法,可以解释如何获得文档分数。可以详细打印每个部分的具体分数。然后是输出。注意,它具有表示每个级别的缩进,以测试环境数据为例:{“value”:0.7271681,“description”:“maxof:”,“details”:[{”value“:0.7271683,“description”:“summary:”,“details”:[{“value”:5.0118747,“description”:“idf”},{“值”:0.06858513,“description”:“queryNorm”}]},{“value”:1.2529687,“description:”fieldWeightin787,productof:“,”details“:[{”value“:1.0,”description“:”tf,withfreq:“,“details“:[{”value“:1.0,”description“:”termFreq=1.0“}]},{”值“:5.0118745,”description:“idf“}”value”:0.25,“description”:“fieldNorm”}]}]},{“value”:0.29647252,“description”:“weight[PerFieldSimilarity],resultof:”,“details”:[{“value:0.296472,”description“:”score,productof:“,”details:[{”value“:0.2851919,”description:“queryWeight,productof:”,”details“:[{”value”:4.158218,”description“:”idf“},{”value:0.06858513,“description:”queryNorm“}]},{“value”:1.0395545,“description”:“fieldWeightin787,productof:”,“details”:[{”value“:1.0,”description“:”tf,withfreq:“,”details“:[{”value”:1.0,“description”:“termFreq=1.0”}]},{”value“:4.158218,”description“:”idf“},”value“:0.25,”description:“fieldNorm”}]}]}]}这看起来很头疼。尝试解释一下:首先,您需要学习Lucene的分数计算公式——分数计算方法是查询语句q和文档d中每个项目t的匹配分数之和,以及权重因子。idf的计算是倒置文档中项目的频率。计算方法为/**Implementedas<code>log+1</code>

     

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的评分计算公式——

Elasticsearch搜索之explain评分分析第1张

分值计算方式为查询语句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得出最终的结果。

免责声明:文章转载自《Elasticsearch搜索之explain评分分析》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇autoit 用命令启动控制面板和使用RUNDLL32大全Android学习——移植tr069程序到Android平台下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

ElasticSearch 高级DSL查询语句

ElasticSearch 高级DSL查询语句 1. 准备测试索引和映射关系 向ES集群中发送Put请求,用以创建索引和映射关系:http://127.0.0.1:9200/student 请求Body携带json参数信息如下 { "settings": {}, "mappings": { "properties": {...

分布式/微服务必配APM系统,SkyWalking让你不迷路

APM系统当然不仅仅只是链路追踪,还可以根据各种性能指标分析应用程序及其运行环境,以便在发生故障的时候能快速定位及解决问题 前言 如今分布式、微服务盛行,面对拆分服务比较多的系统,如果线上出现异常,需要快速定位到异常服务节点,假如还用传统的方式排查肯定效率是极低的,因为服务之间的各种通信会让定位更加繁琐;所以就急需一个分布式链路追踪系统,方便快速定位异常节...

ElasticSearch索引与搜索

在系列的第一篇文章中我们介绍了ElasticSearch的基本概念和操作,本文将继续介绍ElasticSearch查询和索引功能。 目录: 查询 精确查询 term查询 terms查询 range查询 全文查询 match查询 multi_match查询 script查询 组合查询 bool查询 dis_max查询 function_sc...

Elasticsearch之深入理解

@ 目录 ES应用场景 ES应用场景举例 应用场景思考 ES和其他数据库对比 ES架构 Gateway Lucene 数据处理 发现机制与脚本 Transport REST full API ES中集群、节点、索引、分片、段等概念 集群 节点 索引 分片 段(segment) 文档(document) 字段(field) 词项(term)...

elasticsearch版本不同,批量查询也不相同

网上搜到批量查询可以通过TransportClient实现,但官方推荐使用RestHighLevelClient实现 注意: We plan on deprecating the TransportClient in Elasticsearch 7.0 and removing it completely in 8.0. Instead, you shou...

ElasticSearch(三):ES单机版本基本操作之删除,修改,插入

1. 创建索引 1.1 直接创建索引 可以直接使用head插件创建索引,指定分片数和备份数即可。如下图: 1.2 创建结构化索引 上图创建的索引,点开索引信息,mapping是空的,表示该索引的字段并没有指定,我们可以在创建索引的时候直接指定其字段名来创建。 使用POSTMAN进行创建(任何工具都行,习惯) 请求路径:localhost:9200/bo...