内容纲要
前言
Lucene 和 ES 的得分机制是一个基于词频和逆文档词频的公式,简称为 TF-IDF 公式。
公式中将查询作为输入,使用不同的手段来确定每一篇文档的得分,将每一个因素最后通过公式综合起来,返回该文档的最终得分。这个综合考量的过程,就是我们希望相关的文档被优先返回的考量过程。在 Lucene 和 ES 中这种相关性称为得分。
考虑到查询内容和文档得关系比较复杂,所以公式中需要输入得参数和条件非常得多。但是其中比较重要得其实是两个算法机制
- TF (词频)
Term Frequency : 搜索文本中的各个词条(term)在查询文本中出现了多少次,
出现次数越多,就越相关,得分会比较高 - IDF(逆文档频率)
Inverse Document Frequency : 搜索文本中的各个词条(term)在整个索引的所有文档中出现了多少次,出现的次数越多,说明越不重要,也就越不相关,得分就比较低。
打分机制
接下来咱们用一个例子简单分析一下文档的打分机制:
1、首先,咱们先准备一个基础数据
# 创建索引
PUT /atguigu
# 增加文档数据
# 此时索引中只有这一条数据
PUT /atguigu/_doc/1
{
"text":"hello"
}
2、查询匹配条件的文档数据
GET /atguigu/_search
{
"query": {
"match": {
"text": "hello"
}
}
}
这里文档的得分为:0.2876821,很奇怪,此时索引中只有一个文档数据,且文档数据中可以直接匹配查询条件,为什么分值这么低?这就是公式的计算结果,咱们一起来看看
3、分析文档数据打分过程
GET后添加参数explain=true
# 增加分析参数
GET /atguigu/_search?explain=true
{
"query": {
"match": {
"text": "hello"
}
}
}
执行后,会发现打分机制中有 2 个重要阶段:计算 TF 值和 IDF 值
最后的分数为:
4、计算 TF 值
参数 | 含义 | 取值 |
---|---|---|
freq | 文档中出现词条的次数 | 1.0 |
k1 | 术语饱和参数 | 1.2(默认值) |
b | 长度规格化参数(单词长度对于整个文档的影响程度) | 0.75(默认值) |
dl | 当前文中分解的字段长度 | 1.0 |
avgdl | 查询文档中分解字段数量/查询文档数量 | 1.0 |
TF(词频) | 1.0/(1 + 1.2 (1 - 0.75 + 0.751.0/1.0)) | 0.454545 |
5、计算 IDF 值
参数 | 含义 | 取值 |
---|---|---|
N | 包含查询字段的文档总数(不一定包含查询词条) | 1 |
n | 包含查询词条的文档数 | 1 |
IDF(逆文档频率) | log(1 + (1 - 1 + 0.5) / (1 + 0.5)) | 0.2876821 |
注:这里的 log 是底数为 e 的对数
6、计算文档得分
参数 | 含义 | 取值 |
---|---|---|
boost | 词条权重 | 2.2(基础值)* 查询权重(1) |
idf | 逆文档频率 | 0.2876821 |
tf | 词频 | 0.454545 |
Score(得分) | 2.2 0.2876821 0.454545 | 0.2876821 |
7、增加新的文档,测试得分
-
增加一个毫无关系的文档
# 增加文档 PUT /atguigu/_doc/2 "text" : "spark" } # 因为新文档无词条相关信息,所以匹配的文档数据得分就应该较高: # 0.6931741 GET /atguigu/_search { "query": { "match": { "text": "hello" } } }
-
增加一个一模一样的文档
# 增加文档 PUT /atguigu/_doc/2 { "text" : "hello" } # 因为新文档含词条相关信息,且多个文件含有词条,所以显得不是很重要,得分会变低 # 0.18232156 GET /atguigu/_search { "query": { "match": { "text": "hello" } } }
-
增加一个含有词条,但是内容较多的文档
# 增加文档 PUT /atguigu/_doc/2 { "text" : "hello elasticsearch" } # 因为新文档含词条相关信息,但只是其中一部分,所以查询文档的分数会变得更低一些。 # 0.14874382 GET /atguigu/_search { "query": { "match": { "text": "hello" } } }
案例
需求:
查询文档标题中含有“Hadoop”,“Elasticsearch”,“Spark”的内容。
优先选择“Spark”的内容
1、准备数据
# 准备数据
PUT /testscore/_doc/1001
{
"title" : "Hadoop is a Framework",
"content" : "Hadoop 是一个大数据基础框架"
}
PUT /testscore/_doc/1002
{
"title" : "Hive is a SQL Tools",
"content" : "Hive 是一个 SQL 工具"
}
PUT /testscore/_doc/1003
{
"title" : "Spark is a Framework",
"content" : "Spark 是一个分布式计算引擎"
}
2、查询数据
# 查询文档标题中含有“Hadoop”,“Elasticsearch”,“Spark”的内容
GET /testscore/_search?explain=true
{
"query": {
"bool": {
"should": [
{
"match": {
"title": {"query": "Hadoop", "boost": 1}
}
},
{
"match": {
"title": {"query": "Hive", "boost": 1}
}
},
{
"match": {
"title": {"query": "Spark", "boost": 1}
}
}
]
}
}
}
此时,你会发现,Spark 的结果并不会放置在最前面
此时,咱们可以更改 Spark 查询的权重参数 boost.看看查询的结果有什么不同
# 查询文档标题中含有“Hadoop”,“Elasticsearch”,“Spark”的内容
GET /testscore/_search?explain=true
{
"query": {
"bool": {
"should": [
{
"match": {
"title": {"query": "Hadoop", "boost": 1}
}
},
{
"match": {
"title": {"query": "Hive", "boost": 1}
}
},
{
"match": {
"title": {"query": "Spark", "boost": 2}
}
}
]
}
}
}