Elasticsearch实战高级搜索-二次排序
  ehiNR29K4ZW3 2023年11月02日 54 0

一、需求背景:

虽然上一章讲到了完全匹配,但是有些时候查询结果还是很多,关键字在结果中的位置是杂乱的,这样搜索还是不太容易快速定位到自己想要的结果,这一章就是讲搜索结果的相关度排序。

类似如电商搜索:

Elasticsearch实战高级搜索-二次排序_Elasticsearch

排序优先级为: 

  • 关键词在结果中出现的位置越靠前,排序越靠前;
  • 关键词在结果中的字符占比越高,排序越靠前。

再举个明显的例子,如下搜索“高”,先看“高”在左边的越靠前,然后看“高”字的文本长度占比越高越靠前:

Elasticsearch实战高级搜索-二次排序_数据_02

为了控制搜索结果的相关度,elasticsearch提供了多种方式,通过脚本实现自定义评分逻辑是终极方式。脚本返回一个评分值,该值再与原_score再进行加法等运算。脚本编写很简单,我们跟随一个例子(基于6.0版本)来看看如何通过脚本实现自定义排序。


二、磨刀霍霍:

搭建本地开发环境(此处省略一万字)。

mapping的定义:

{
	"es-type": {
		"properties": {
			"line_id": {
				"type": "long"
			},
			"line_name": {
				"type": "text",
				"analyzer": "ngram_analyzer",
				"fields": {
					"keyword": {
						"type": "keyword"
					}
				}
			}
		}
	}
}

line_name的text字段用于全文本搜索。keyword字段没有分词,用于把搜索结果作为参数传入脚本函数,实现自定义排序逻辑。

写入测试数据:

Elasticsearch实战高级搜索-二次排序_Painless_03

执行搜索关键字“ip”:

Elasticsearch实战高级搜索-二次排序_数据_04

  • unction_score查询:是用来控制评分过程的终极武器,它允许为每个与主查询匹配的文档应用一个函数,以达到改变甚至完全替换原始查询评分 _score 的目的。
  • script_score用自定义脚本完全控制评分计算,实现所需逻辑。
  • lang:指定脚本语言。
  • params:指定作为变量传递到脚本中的任何命名参数。

返回结果:

Elasticsearch实战高级搜索-二次排序_Painless_05


根据排序规则一(关键词在结果中出现的位置越靠前,排序越靠前),"ip流量数据"、"iphone"和"iphone 6s"排在"昨日iphone价格数据"。根据排序规则二(关键词在结果中的字符占比越高,排序越靠前),"ip流量数据"和"iphone"的字符长度是一样的,所以评分一样,而"iphone 6s"字符长度比"iphone"长,“ip”字符占比低,排在后面。


elasticsearch支持多种脚本语言,经历各版本演变后,从5.0开始实现了自己专用的语言:Painless。Groovy已弃用,这次示例使用Painless实现,Painless是内置支持的。下面详细看下脚本代码:

String result = doc['line_name.keyword'].value.toLowerCase();
String keyText = params.keyword.toLowerCase();
double MAX_VALUE = 1000;
double position_score = 0;
double init = 100;
double weight = 0.9;
int maxIndex = 0;
for (int x = 0; x < keyText.length(); x++) {
    char ch = keyText.charAt(x);
    int pos = result.indexOf(ch, maxIndex);
    if (pos >= 0) {
        maxIndex = pos;
        double score = init * (weight);
        for (int i = 0; i < pos; i++) {
            score = score * (1 - weight);
        }
        position_score = position_score + score;
    }
}
double similarity_score = 0;
double similarity = Math.abs(1.0 * result.length() - 1.0 * keyText.length());
similarity_score = 10.0 d - similarity;
if (similarity_score < 0) {
    similarity_score = 0;
}
double total = position_score * MAX_VALUE + similarity_score * 0.1;
return total;


获取文档值:doc['line_name.keyword']

传入排序的关键字:params.keyword

实现比较简单,根据位置和字符占比分别计算评分,将结果乘不同权重再相加。


【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月08日 0

暂无评论

推荐阅读
ehiNR29K4ZW3
最新推荐 更多

2024-05-31