博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Solr reRankQuery加自定义函数实现搜索二次排序
阅读量:4647 次
发布时间:2019-06-09

本文共 6063 字,大约阅读时间需要 20 分钟。

最近用到solr排序的复杂排序,系统最开始的排序时重写了文本相关分计算部分,增加新的排序逻辑后性能下降十分明显,考虑到用reRank和自定义函数的方法来解决,实际操作中碰到一些问题,自定义函数参考了

然后再rerank测试通过,确实只会对头部指定的docs进行重新计算分数。

首先创建一个maven项目,pom依赖项如下,solr core使用6.6版本

org.apache.solr
solr-core
6.6.0
org.restlet.jee
org.restlet.ext.servlet
org.restlet.jee
org.restlet
org.apache.lucene
lucene-core
6.6.0

项目中增加自己的package,增加两个类文件,分别实现 ValueSourceParser和ValueSource

项目文件结构如下图

首先是继承ValueSourceParser的MyValueSourceParser 类,重写parse方法,return MyScoreSource类的实例。

MyValueSourceParser 类代码如下:

package com.wmf.customfunc;/** * Created by wangmf on 2019/7/19. */import org.apache.lucene.queries.function.ValueSource;import org.apache.solr.schema.SchemaField;import org.apache.solr.search.FunctionQParser;import org.apache.solr.search.SyntaxError;import org.apache.solr.search.ValueSourceParser;//需要继承ValueSourceParser,重写parse方法public class MyValueSourceParser extends ValueSourceParser {    public MyValueSourceParser() {        super();    }    @Override    public ValueSource parse(FunctionQParser fp) throws SyntaxError {//        String sortType = fp.parseArg(); //这里是从FunctionQParser取到第一个参数,也就是我们自定义函数里的第一个参数 sortType//        String latitudeStr = fp.parseArg();//第二个参数latitude//        String longitudeStr = fp.parseArg();//第三个参数longitude        //本例没用到参数      //获取三个字段的信息        ValueSource intval1 = getValueSource(fp,"intval1");        ValueSource intval2 = getValueSource(fp,"intval2");        ValueSource floatval1 = getValueSource(fp,"floatval1");        //将参数及需要的文档的值传给自定义的ValueSource方法,打分规则在自定义的ValueSource中定制        MyScoreSource stringFieldSource = new MyScoreSource(intval1,intval2,floatval1);        return stringFieldSource;    }    //该方法是根据字段名,从FunctionQParser得到文档该字段的相关信息    public ValueSource getValueSource(FunctionQParser fp, String arg) {        if (arg == null)            return null;        SchemaField f = fp.getReq().getSchema().getField(arg);        return f.getType().getValueSource(f, fp);    }}

MyScoreSource类实现评分计算,代码如下

package com.wmf.customfunc;import java.io.IOException;import java.util.Map;import org.apache.lucene.index.LeafReaderContext;import org.apache.lucene.queries.function.FunctionValues;import org.apache.lucene.queries.function.ValueSource;import org.apache.lucene.queries.function.docvalues.FloatDocValues;/** * Created by wangmf on 2019/7/19. */public class MyScoreSource extends ValueSource {
//需要继承ValueSource ,重写getValues方法 private ValueSource intval1; private ValueSource intval2; private ValueSource floatval1; //通过构造方法的参数传递取得filed中的值 public MyScoreSource(ValueSource intVal1, ValueSource intVal2, ValueSource floatVal1) { this.intval1 = intVal1; this.intval2 = intVal2; this.floatval1 = floatVal1; } @Override public FunctionValues getValues(Map context, LeafReaderContext readerContext) throws IOException { final FunctionValues IntValue1 = intval1.getValues(context,readerContext); final FunctionValues IntValue2 = intval2.getValues(context,readerContext); final FunctionValues FloatValue1 = floatval1.getValues(context,readerContext); return new FloatDocValues(this) { //重写floatVal方法,此处为打分排序规则 @Override public float floatVal(int doc) { String strInt1 = IntValue1.strVal(doc); String strInt2 = IntValue2.strVal(doc); String strFloat1 = FloatValue1.strVal(doc); float sc1 = Float.parseFloat(strInt1); float sc2 = Float.parseFloat(strInt2); float sc3 = Float.parseFloat(strFloat1); System.out.println("Int1:" + strInt1 + "," + "Int2:" + strInt2 + "," + "Float1:" + strFloat1); return sc1 + sc2 + sc3; //返回自己计算出的得分值 } @Override public String toString(int doc) { return name() + '(' + IntValue1.strVal(doc) + ',' + IntValue2.strVal(doc) + ',' + FloatValue1.strVal(doc) + ')'; } }; } @Override public boolean equals(Object o) { return true; } @Override public int hashCode() { return 0; } @Override public String description() { return name(); } public String name() { return "Myfunction"; }}

测试用的逻辑,实际很简单,将三个字段:intval1,intval2,floatval1的值相加作为新的分数,实际业务中逻辑会更复杂,但逻辑都可以在实例new FloatDocValues(this)写,根据不同的返回,重写floatVal方法,或者用其他继承了FloatDocValues类或者DoubleDocValues、StrDocValues等类实现逻辑。

代码完成后打包,将生成的包复制到solr中,如果是在使用solr自带的jetty作为web容器,则将打好的jar包复制到 server/solr-webapp/webapp/WEB-INF/lib/目录下,然后修改对应对应core的solrconfig.xml

修改内容如下:

    name就是我们再调用时需要使用的函数名,通常我们在查询时想要得到函数计算值,可以直接在fl中增加该函数

http://solrserver/solr/core_demo/select?fl=*,_val_:myfunc()&indent=on&q=*:*&wt=json

    得到结果如下

    结合solr的reRankQuery可以实现在文本相关的头部docs中进行排序,注意,在rerank查询时{}中的内容大小写敏感

http://solrserver/solr/core_demo/select?q=*:*&wt=json&sort=intval1 desc&rq={!rerank reRankQuery=$rqq reRankDocs=2 reRankWeight=1.0}&rqq={!func}myfunc()&
fl=*,score,_val_:myfunc()&indent=on

该查询实现:查询所有文档,并按照intval1字段进行倒序排序,再在前面的两个doc,按照我们自定义的函数myfunc计算的分数,重新进行年排序,返回结果如下图

    这里的逻辑是在reRankQuery中计算的分数按照reRankWeight的权重和第一次查询的score相加后计算新的分数,计算式只针对reRankDocs的指定的头部数量的doc进行,得到新的分数score。从上图可以看到前面两个doc的score是函数计算的分数*1(权重是1.0)+1(原有的分数)。从第三条开始score只有第一次查询的1.0。从而在进行第二次查询是实现自定义的逻辑。

    另外如果要按照计算的分数排序,使用sort={!func}myfunc() desc,进行过滤,应该使用solr的frange命令{!frange l=13}myfunc() 过滤小于13分的doc。

注:除了在reRank中使用外,其他查询、排序、过滤都会在召回的所有doc中进行计算操作,可能对性能影响比较大。

 

获取三个字段的信息

转载于:https://www.cnblogs.com/wfox111/p/11215062.html

你可能感兴趣的文章
教你实现图片的惰性加载
查看>>
win7 64 位用VMware安装Ubantu 20130908
查看>>
开发环境切换
查看>>
报表控件NCreport教程:子查询系统设计
查看>>
ContextLoader,ContextLoaderListener解读
查看>>
Java内存泄露的理解与解决
查看>>
JAVA 搭建基于SPRINGBOOT的SSM(SPRING + SPRINGMVC + MYBATIS)的MAVEN项目
查看>>
element-UI ,Table组件实现拖拽效果
查看>>
单个pdf提取测试
查看>>
JS_完美轮播图_可加上各种运动
查看>>
HttpUtility.UrlEncode 方法 (String) 对 URL 字符串进行编码 NET Framework 4.6 and 4.5
查看>>
C#方法解析
查看>>
设计模式(五):命令模式
查看>>
[ NOIP 2014 ] TG
查看>>
Linux 免密登录和配置环境变量
查看>>
<kafka><应用场景><Kafka VS Flume>
查看>>
[Java] [Lock] [Synchronized VS ReentrantLock]
查看>>
windbg学习----r?
查看>>
JAVA面试题,比较经典的面试题
查看>>
Selenium学习之==>Switch与SelectApi接口详解
查看>>