Lucene 8.x 使用 FunctionScoreQuery 实现自定义的文档评分

在使用不管是 Lucene 或者 ElasticSearch 进行全文搜索中,检索到文档很简单,但是把搜索者最想要的结果排在最前面,这是最具挑战性的。

我们举个最简单的例子:假设我们需要做一个博客搜索,当我们用 Lucene 默认的匹配度进行排序,那你可能会发现搜索出来的都是一堆垃圾文章。所以一般我们会把博客文章的其他因素作为排序方法来替代匹配度。例如会考虑 “阅读数、文章长度、是否编辑推荐” 等诸多扩展因素综合进行排序。

这种时候我们一般会构造一个包含多个 SortField 的 Sort 对象,例如前面提到的阅读数、长度、是否推荐等。

但是!这个排序对象在搜索前就已经固定下来的,这样可能会出现一种情况,一些相关度非常低,但它的阅读数很高,同时又是编辑推荐的博客文章会排在前面。

有没有这样一种可能,我想根据不同的内容相关度,再结合文章的一些扩展指标来进行排序。比如,当我在搜博客的时候,只有当相关度大于某个值的时候,我会通过这些文章扩展指标来加分,让这些被推荐的文章排在前面。

这里就要用到 lucene 的 FunctionScoreQuery 来实现了,在老的版本里,是一个叫 CustomScoreQuery 的类,在新版已经被废弃了。 Lucene 的 API 就是这么变态,新版本发布经常都会有 API 的大变更。

大概的使用过程是这样的:

0. 需要引用的类

1import org.apache.lucene.expressions.Expression; 2import org.apache.lucene.expressions.SimpleBindings; 3import org.apache.lucene.expressions.js.JavascriptCompiler; 4import org.apache.lucene.queries.function.FunctionScoreQuery; 5import org.apache.lucene.queryparser.classic.ParseException;

1. 首先定义一个变量表:

1SimpleBindings bindings = new SimpleBindings(); 2bindings.add("$score", DoubleValuesSource.SCORES); 3bindings.add("$recomm", DoubleValuesSource.fromIntField("recomm")); 4bindings.add("$viewcount", DoubleValuesSource.fromIntField("viewcount")); 5bindings.add("$wordcount", DoubleValuesSource.fromIntField("wordcount"));

2. 定义一个自定义排序的表达式:

1String sort_method= "mysort($score,$recomm,$viewcount,$wordcount)"; 2 3HashMap<String, Method> scoreMethods = new HashMap(); 4scoreMethods.put("mysort", getClass().getDeclaredMethod("mysort", double.class, double.class, double.class, double.class)); 5 6Expression expr = JavascriptCompiler.compile(sort_method, scoreMethods, getClassLoader());

注意这里的 scoreMethods 用来存放所有自定义函数对应的 Java 方法,如下所示:

3. 定义一个 mysort 方法,接受 double 参数,返回 double 参数

1public double mysort(double score, double recomm, double viewcount, double wordcount) { 2 if(score >= 40) { 3 if(recomm > 0) 4 score += 100; 5 if(viewcount >= 1000) 6 score += 100; 7 if(wordcount >= 200) 8 score += 20; 9 } 10 return score; 11}

4. 接下来你就可以使用 FunctionScoreQuery 

FunctionScoreQuery query = new FunctionScoreQuery(oldQuery, expr.getDoubleValuesSource(bindings));

这里的 oldQuery 就是你原先用来做全文搜索的 Query 实例。

你可以试试打印一下 query 看看,我这边一个真实打印的结果如下:

FunctionScoreQuery(+type:[2 TO 2] +fork:[0 TO 0] +(((+name:j2cache)^20.0 (+description:j2cache)^10.0 (+detail:j2cache)^1.0 (+tags:j2cache)^10.0 (+catalogs:j2cache)^10.0 (+owner.name:j2cache)^2.0)~1), scored by expr(repo_sort($score,$recomm,$stars,$gindex)))

最后

当使用 FunctionScoreQuery 后,排序的时候只需要直接用 Sort.RELEVANCE 即可。

尝试一下吧,不过当结果集很大的时候 FunctionScoreQuery 的效率会比较差,具体需要在实际的环境中进行验证。

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

Docker 简单部署 ElasticSearch

一、ElasticSearch是什么?Elasticsearch也使用Java开发并使用Lucene作为其核心来实现所有索引和搜索的功能,但是它的目的是通过简单的RESTfulAPI来隐藏Lucene的复杂性,从而让全文搜索变得简单。不过,

1. 初识 Lucene

在学习Lucene之前呢,我们当然首先要了解下什么是Lucene.0x01 什么是Lucene?Lucene是一套用于全文检索和搜索的开放源代码程序库,由Apache软件基金会支持和提供。Lucene提供了一个简单却强大的应用程序接口,能够做全文索引和搜索,在Java开发环境里Lucene是一个成熟