1、  ElasticSearch的评分

在用ElasticSearch作为搜索引擎的时候,如果采用关键字进行查询,ElasticSearch会对每个符合查询条件的文档进行评分,在5.3.0的版本中,默认采用的是BM25的评分函数,关于BM25的评分函数,网络上有较多的讲解,这里就不进行详细说明,贴上几个连接如下:

http://luokr.com/p/7

https://en.wikipedia.org/wiki/Okapi_BM25

https://www.elastic.co/guide/en/elasticsearch/guide/2.x/pluggable-similarites.html#bm25

在ElasticSearch5.3.0中采用的函数计算如下:

N表示将查询关键字分词后得到的N个term。

IDF=log(1 + (docCount - docFreq + 0.5) / (docFreq + 0.5))

tfNorm=(freq * (k1 + 1)) / (freq + k1 * (1 - b + b * fieldLength / avgFieldLength))

docCount:查询中满足查询条件的所有文档

docFreq:满足本条term的查询文档数目

IDF反映的是term的影响因子,如果docCount很大,docFreq很小,标示该term在doc之间具有很好的分辨力,当然IDF值也就越大。

freq:查询term在本doc的field中出现的次数

K1:调优参数默认为1.2

b:调优参数,默认为0.75

fieldLength:是满足查询条件的doc的filed的长度

avgFieldLength:是满足查询条件的所有doc的filed的长度.

tfNorm反映的该term在所有满足条件的doc中field中的重要性,一般来说,相同的freq 下,field的长度越短,那么取值就越高。

2、  Lucene中BM25的评分研究

在索引中插入3条数据,采用默认的

Analyzer analyzer = new StandardAnalyzer();

数据如下:

"text", "this hour chiness my book"

"text", "this is chiness chiness japan amc set the right context"

"text", "this  book chiness jack1 the right context"

在程序中,用"text": "chiness"进行搜索,并且把把日志输出如下:

查找到的文档总共有:3

---------------

0.16786805 = weight(text:chiness in 1) [BM25Similarity], result of:

0.16786805 = score(doc=1,freq=2.0 = termFreq=2.0

), product of:

0.13353139 = idf, computed as log(1 + (docCount - docFreq + 0.5) / (docFreq + 0.5)) from:

3.0 = docFreq

3.0 = docCount

1.2571429 = tfNorm, computed as (freq * (k1 + 1)) / (freq + k1 * (1 - b + b * fieldLength / avgFieldLength)) from:

2.0 = termFreq=2.0

1.2 = parameter k1

0.75 = parameter b

   5.3333335 = avgFieldLength

      7.111111 = fieldLength

0.16786803

this is chiness chiness japan amc set the right context

---------------

0.14874382 = weight(text:chiness in 0) [BM25Similarity], result of:

0.14874382 = score(doc=0,freq=1.0 = termFreq=1.0

), product of:

0.13353139 = idf, computed as log(1 + (docCount - docFreq + 0.5) / (docFreq + 0.5)) from:

3.0 = docFreq

3.0 = docCount

1.113924 = tfNorm, computed as (freq * (k1 + 1)) / (freq + k1 * (1 - b + b * fieldLength / avgFieldLength)) from:

1.0 = termFreq=1.0

1.2 = parameter k1

0.75 = parameter b

      5.3333335 = avgFieldLength

      4.0 = fieldLength

0.14874382

this hour chiness my book

---------------

0.1346556 = weight(text:chiness in 2) [BM25Similarity], result of:

0.1346556 = score(doc=2,freq=1.0 = termFreq=1.0

), product of:

0.13353139 = idf, computed as log(1 + (docCount - docFreq + 0.5) / (docFreq + 0.5)) from:

3.0 = docFreq

3.0 = docCount

1.008419 = tfNorm, computed as (freq * (k1 + 1)) / (freq + k1 * (1 - b + b * fieldLength / avgFieldLength)) from:

1.0 = termFreq=1.0

1.2 = parameter k1

0.75 = parameter b

   5.3333335 = avgFieldLength

      5.2244897 = fieldLength

0.1346556

this  book chiness jack1 the right context

由日志可以知道,评分最高的为文档1,其次为文档0,最低的是文档2,原因是文档1中chiness出现了两次,文档0和2中都只出现了一次,但是由于文档0的text的fieldLength比文档2小,所以文档0的评分比文档2高。

avgFieldLength的长度可以知道是5.3333*3,约等于16,是因为this、is、the是停用词,去除后的短语是"hour chiness my book","chiness chiness japan amc set  right context"和"book chiness jack1  right context".terms数量一共是16个,除以3那么就是5.3333。

问题来了:

这边有个问题,就是每个文档的字段长度好像跟我们输入的不一致,分别是

文档1,长度是  7.111111

文档0,长度是  4.0

文档2,长度是  5.2244897

都不是整数,而且跟我们去除停用词后的长度不一致,按道理将应该是6、4、5才对。

lucene为了降低存储的空间,在存储field的长度时,没有存储实际长度,而是存储了一个byte类型的值(0-255),每个值对应了BM25Similarity有NORM_TABLE中的index,

在BM25Similarity有NORM_TABLE的float数组,实现了一个区间映射的功能。

/** Cache of decoded bytes. */

private static final float[] NORM_TABLE = new float[256];

static {

for (int i = 1; i < 256; i++) {

float f = SmallFloat.byte315ToFloat((byte) i);

NORM_TABLE[i] = 1.0f / (f * f);

}

NORM_TABLE[0] = 1.0f / NORM_TABLE[255]; // otherwise inf

}

输出内容如下:

0    5.6493154E19

1    2.95147899E18

2    2.04963825E18

3    1.50585663E18

4    1.1529215E18

………………….

112    64.0

113    40.96

114    28.444445

115    20.897959

116    16.0

117    10.24

118    7.111111

119    5.2244897

120    4.0

……………………

253    3.469447E-20

254    2.4093382E-20

255    1.770126E-20

反向操作吧,代码如下:

for (int i = 0; i < 100; i++) {

float x = 1.0f / i;

float y = (float) Math.sqrt(x);

System.out.println(i + "    " + SmallFloat.floatToByte315(y));

}

0    -1

1    124

2    121

3    120

4    120

5    119

6    118

7    118

8    117

9    117

10    117

11    116

12    116

13    116

14    116

15    116

16    116

17    115

18    115

19    115

如果长度是4,写入值是120,长度是5,写入值是119,长度为6和7,那么在存储的时候写入值是118,取值的时候,文档2的取值就是NORM_TABLE[119],文档1的取值是NORM_TABLE[118],文档0的取值是NORM_TABLE[120]。

3、  ElasticSearch的评分注意点

在使用ElasticSearch提供搜索服务的时候,会发现一个很有意思的现象,在ElasticSearch中新建索引并且插入数据,命令如下

curl -XPUT 'http://127.0.0.1:9200/scoretest'

curl -XPUT 'http://127.0.0.1:9200/scoretest/scoretest/_mapping' -d '{"scoretest":{"properties":{"text":{"type":"text"}}}}'

curl -XPUT 'http://127.0.0.1:9200/scoretest/scoretest/1' -d '{"text":"this hour chiness my book"}'

curl -XPUT 'http://127.0.0.1:9200/scoretest/scoretest/2' -d '{"text":"this is chiness chiness japan amc set the right context"}'

curl -XPUT 'http://127.0.0.1:9200/scoretest/scoretest/3' -d '{"text":"this  book chiness jack1 the right context"}'

执行查询命令

curl -XGET 'http://127.0.0.1:9200/scoretest/scoretest/_search' -d '{"query":{"match":{"text":"chiness"}}}'

结果如下:

问题来了:

chines出现两次的排名最靠前,chiness出现一次的,长度长的竟然比长度短的排名靠前,这个与我们想象中的不一致,

这次增加explain字段查看下分析过程。命令如下:

curl -XGET 'http://127.0.0.1:9200/scoretest/scoretest/_search' -d '{ "explain": true, "query":{"match":{"text":"chiness"}}}'

由于信息较多,就截取下主要的分析过程中的几个参数:

docFreq

docCount

avgFieldLength

fieldLength

1

1

1

5

5.2244897

2

1

1

10

10.24

3

1

1

7

7.11111

好像这个四个参数的取值与章节2中完全不一致,

原因是,ElasticSearch在建立index的时候,默认自动回建立5个分片,在插入数据的时候,会根据一致性算法将文档分配到某一个shard上,在进行搜索的时候,每个shard上独自进行搜索评分,然后汇总后,根据_score进行排序,然后在返回给前端,我们可以看下上述三个文档的分布,在我的实验中分布如下:

1

_shard 3

2

_shard 4

3

_shard 2

所以对于index下同一个type下面的数据,最好在插入的时候,数据存放到同一个shard上,这个采用系统默认评分的结果才会保持正确。这里就用到了ES的_routing参数,默认情况下ES是根据doc的_id作为hash的key,其官网描述如下:

https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping-routing-field.html

重新测试下,这回指定_routing:

curl -XPUT 'http://127.0.0.1:9200/scoretestrouting'

curl -XPUT 'http://127.0.0.1:9200/scoretestrouting/scoretestrouting/_mapping' -d '{"scoretestrouting":{"_routing":{"required":true},"properties":{"text":{"type":"text"}}}}'

curl -XPUT 'http://127.0.0.1:9200/scoretestrouting/scoretestrouting/1' -d '{"text":"this hour chiness my book"}'

curl -XPUT 'http://127.0.0.1:9200/scoretestrouting/scoretestrouting/1?routing=wang' -d '{"text":"this hour chiness my book"}'

curl -XPUT 'http://127.0.0.1:9200/scoretestrouting/scoretestrouting/2?routing=wang' -d '{"text":"this is chiness chiness japan amc set the right context"}'

curl -XPUT 'http://127.0.0.1:9200/scoretestrouting/scoretestrouting/3?routing=wang' -d '{"text":"this  book chiness jack1 the right context"}'

搜索命令如下:

curl -XGET 'http://127.0.0.1:9200/scoretestrouting/scoretestrouting/_search?routing=wang' -d '{ "explain": true, "query":{"match":{"text":"chiness"}}}'

查看结果如下:

在添加"explain": true,看下详细的评分计算过程:

docFreq

docCount

avgFieldLength

fieldLength

1

3

3

7.3333335

5.2244897

2

3

3

7.3333335

10.24

3

3

3

7.3333335

7.11111

在没有指定_routing参数的情况下,使用_id代替或者父级文档的_parent字段代替,

注意点,在使用了指定_routing的情况下,在同一个index的下面,如果使用了不同的_routing,那么有可能存在两个文档具有相同的_id,但是存放在两个不同的shard上。

elasticSearch(5.3.0)的评分机制的研究的更多相关文章

  1. Android 8.0/9.0 wifi 自动连接评分机制

    前言 Android N wifi auto connect流程分析 Android N selectQualifiedNetwork分析 Wifi自动连接时的评分机制 今天了解了一下Wifi自动连接 ...

  2. lucene 的评分机制

    lucene 的评分机制 elasticsearch是基于lucene的,所以他的评分机制也是基于lucene的.评分就是我们搜索的短语和索引中每篇文档的相关度打分. 如果没有干预评分算法的时候,每次 ...

  3. Elasticseach的评分机制

    lucene 的评分机制 elasticsearch是基于lucene的,所以他的评分机制也是基于lucene的.评分就是我们搜索的短语和索引中每篇文档的相关度打分. 如果没有干预评分算法的时候,每次 ...

  4. Solr In Action 笔记(2) 之 评分机制(相似性计算)

    Solr In Action 笔记(2) 之评分机制(相似性计算) 1 简述 我们对搜索引擎进行查询时候,很少会有人进行翻页操作.这就要求我们对索引的内容提取具有高度的匹配性,这就搜索引擎文档的相似性 ...

  5. Wifi 评分机制分析

    从android N开始,引入了wifi评分机制,选择wifi的时候会通过评分来选择. android O源码 frameworks\opt\net\wifi\service\java\com\and ...

  6. Lucene Scoring 评分机制

    原文出处:http://blog.chenlb.com/2009/08/lucene-scoring-architecture.html Lucene 评分体系/机制(lucene scoring)是 ...

  7. Lucene 的 Scoring 评分机制

    转自: http://www.oschina.net/question/5189_7707  Lucene 评分体系/机制(lucene scoring)是 Lucene 出名的一核心部分.它对用户来 ...

  8. Elasticsearch 7.4.0官方文档操作

    官方文档地址 https://www.elastic.co/guide/en/elasticsearch/reference/current/index.html 1.0.0 设置Elasticsea ...

  9. ELK——安装 logstash 2.2.0、elasticsearch 2.2.0 和 Kibana 3.0

    本文内容 Elasticsearch logstash Kibana 参考资料 本文介绍安装 logstash 2.2.0 和 elasticsearch 2.2.0,操作系统环境版本是 CentOS ...

随机推荐

  1. cctype学习

    #include <cctype>(转,归纳很好) 头文件描述: 这是一个拥有许多字符串处理函数声明的头文件,这些函数可以用来对单独字符串进行分类和转换: 其中的函数描述: 这些函数传入一 ...

  2. 走进javascript——类型

    ECMAScript语言类型对应于使用ECMAScript语言的ECMAScript程序员直接操作的值.ECMAScript语言类型有以下几种Undefined,Null,Boolean,String ...

  3. 基于Struts2的SpringMVC入门

    1.SpringMVC概述 (1)SpringMVC为表现层提供基础的基于MVC设计理念的优秀Web框架, (2)SpringMVC通过一套mvc的注解,让POJO成为处理请求的控制器,而无需任何接口 ...

  4. Telegram学习解析系列(一):认识一下Telegram的源码

    前言: Telegram不知道有多少同行听过这玩意,或者在看它的源码.我是出于工作原因才接触到这东西,看的真是的......变方了!一个月估计刚刚找到门,还没进去多深,把自己的心得和对源码的认识以及我 ...

  5. MyEclipse2016统一字符编码

    MyEclipse一般没做修改,默认的字符编码是GBK,但是在实际的开发中常用的是utf-8,为了避免出现乱码等情况,我们需要把开发工具的编码都统一设置成utf-8,修改步骤如下: 1.打开MyEcl ...

  6. Java学习笔记——序列化和反序列化

    寒雨连江夜入吴,平明送客楚山孤. 洛阳亲友如相问,一片冰心在玉壶. --芙蓉楼送辛渐 持久化数据的第一种方式.在序列化之前也可以把数据打散逐行存储在文件中,然后在逐行读取. 比如定Student类 用 ...

  7. Java线程安全 关于原子性与volatile的试验

    1. 变量递增试验 static /*volatile*/ int shared=0;//volatile也无法保证++操作的原子性 static synchronized int incrShare ...

  8. Springboot(一):入门篇

    什么是spring boot spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程.该框架使用了特定的方式来进行配置,从而使开发人员 ...

  9. Openstack(企业私有云)万里长征第一步——安装

    一.前言 单位新进了十几台服务器,建了一个高标准的一体化机房,状似刘姥姥进大观园的我,从机房规划到企业私有云搭建一一重头学来,除了机房泥墙其他基本都涉猎到了. 从企业私有云这个名字就能看出这是多么复杂 ...

  10. Samba文件共享服务

    Samba起源: 早期网络想要在不同主机之间共享文件大多要用FTP协议来传输,但FTP协议仅能做到传输文件却不能直接修改对方主机的资料数据,这样确实不太方便,于是便出现了NFS开源文件共享程序:NFS ...