重写solr的积分计算来满足自定义的积分计算、排序的策略

说明版本:历史遗留项目使用的solr5.3,高版本原理类似

第一步,

简单粗暴,把solr目录下lib下的jar包都copy到一个新的项目

幽雅,使用maven引入solr

我这里使用的方式一...

第二步,(需要参考 solrconfig.xml),通过注册一个ParserPlugin或者Funtion来控制

<!-- Query Parsers

       http://wiki.apache.org/solr/SolrQuerySyntax

       Multiple QParserPlugins can be registered by name, and then
       used in either the "defType" param for the QueryComponent (used
       by SearchHandler) or in LocalParams
    -->
  <!-- example of registering a query parser -->
  <!--
     <queryParser name="myparser" class="com.mycompany.MyQParserPlugin"/>
    -->

  <!-- Function Parsers

       http://wiki.apache.org/solr/FunctionQuery

       Multiple ValueSourceParsers can be registered by name, and then
       used as function names when using the "func" QParser.
    -->
  <!-- example of registering a custom function parser  -->
  <!--
     <valueSourceParser name="myfunc" 
                        class="com.mycompany.MyValueSourceParser" />
    -->

通过这里的两种方式都可以实现,一个是registering a query parser  ,一个是 registering a custom function parser

方式一:registering a query parser

原理:通过defType指定一个自定义的Parser,通过Parser创建自定的query ,然后提供provider的customScore 方法来干预评分

public class HmkxQueryParserPlugin extends QParserPlugin {
    final static Logger log= LoggerFactory.getLogger(HmkxQueryParserPlugin.class);

    public void init(NamedList args) {

    }

    @Override
    public QParser createParser(String qstr, SolrParams localParams, SolrParams params, SolrQueryRequest req) {
        return  new HmkxParser(qstr,localParams,params,req);
    }
}
public class HmkxParser extends QParser {
    final static Logger log= LoggerFactory.getLogger(HmkxParser.class);
    private Query innerQuery;

    @Override
    public Query parse() throws SyntaxError {
        return new HmkxQuery(innerQuery);
    }

    public HmkxParser(String qstr, SolrParams localParams, SolrParams params, SolrQueryRequest req) {
        super(qstr, localParams, params, req);
        try {
            //这里比较重要,把你实际使用的 原query 放在这里 我们原来使用的 具体使用什么参考 QParserPlugin 的standardPlugins
            QParser parser = getParser(qstr, "edismax", getReq());
            this.innerQuery = parser.parse();
        } catch (SyntaxError ex) {
            throw new RuntimeException("error parsing query", ex);
        }
    }
}
public class HmkxQuery extends CustomScoreQuery {

    public HmkxQuery(Query subQuery) {
        super(subQuery);
    }

    @Override
    protected CustomScoreProvider getCustomScoreProvider(LeafReaderContext context) throws IOException {
        //此处返回,定义的Provider
        HmkxProvider provider=new HmkxProvider(context);
        return provider;
    }
}
public class HmkxProvider extends CustomScoreProvider {

    final static Logger log= LoggerFactory.getLogger(HmkxProvider.class);
    private SortedDocValues titleValues;


    public HmkxProvider(LeafReaderContext context) {
        super(context);
    }

    @Override
    public float customScore(int doc, float subQueryScore, float valSrcScore) throws IOException {
        float myScore = 1f;
        //补充额外的业务逻辑  可以获取doc中的某个字段,这里获取的是发布时间
        LeafReader lr = context.reader();
        Date pubDate = new Date(lr.getNumericDocValues("pubdate").get(doc));
        Document document = lr.document(doc);
        int diffMonth = getMonthDiff(new Date(),pubDate);
//        log.error("id:"+doc+",pudate:"+pubDate);
//        log.error("月份查:"+diffMonth);
        myScore = myScore-(diffMonth*0.05f);
        if(myScore>1){
            myScore = 1;
        }else if(myScore<0.5){
            myScore = 0.5f;
        }
//        log.error("时间系数:"+myScore);
        return  subQueryScore*valSrcScore *myScore;
    }

    /**
     *  获取两个日期相差的月数
     * @param d1    较大的日期
     * @param d2    较小的日期
     * @return  如果d1>d2返回 月数差 否则返回0
     */
    public static int getMonthDiff(Date d1, Date d2) {
        Calendar c1 = Calendar.getInstance();
        Calendar c2 = Calendar.getInstance();
        c1.setTime(d1);
        c2.setTime(d2);
        if(c1.getTimeInMillis() < c2.getTimeInMillis()) return 0;
        int year1 = c1.get(Calendar.YEAR);
        int year2 = c2.get(Calendar.YEAR);
        int month1 = c1.get(Calendar.MONTH);
        int month2 = c2.get(Calendar.MONTH);
        int day1 = c1.get(Calendar.DAY_OF_MONTH);
        int day2 = c2.get(Calendar.DAY_OF_MONTH);
        // 获取年的差值 假设 d1 = 2015-8-16  d2 = 2011-9-30
        int yearInterval = year1 - year2;
        // 如果 d1的 月-日 小于 d2的 月-日 那么 yearInterval-- 这样就得到了相差的年数
        if(month1 < month2 || month1 == month2 && day1 < day2) yearInterval --;
        // 获取月数差值
        int monthInterval =  (month1 + 12) - month2  ;
        if(day1 < day2) monthInterval --;
        monthInterval %= 12;
        return yearInterval * 12 + monthInterval;
    }

}

新建4个类,通过idea获取其它工具生成可引入的jar包,放到solr/webapp/WEB-INF/lib目录下面,然后修改第一步提到的配置文件

<queryParser name="hmkxParser" class="com.hmkx.solr.query.HmkxQueryParserPlugin" /> 

然后重点来了,在调用select接口的时候,记得指定 defType=hmkxParser

方式二:registering a custom function parser

原理简述:积分计算的时候会通过多个,valueSource获取doc的一个分值(系数),solr定义了一些列的valueSouce,如下图

391b37edb73a68ca37aacd3eba8f666f87d.jpg

而使用哪些valueSource通过valueSourceParser决定,

ValueSourceParser 在这个类里面初始化了很多 Parser到 standardValueSourceParsers 里面
public static Map<String, ValueSourceParser> standardValueSourceParsers = new HashMap();
static {
    addParser("testfunc", new ValueSourceParser() {
        public ValueSource parse(FunctionQParser fp) throws SyntaxError {
            ValueSource source = fp.parseValueSource();
            return new TestValueSource(source);
        }
    });
    ....省略
}

原理结束,下面直接写方法了

定义Funtion插件

public class HmkxValueParser extends ValueSourceParser {
    final static Logger log= LoggerFactory.getLogger(HmkxValueParser.class);

    @Override
    public ValueSource parse(FunctionQParser fq) throws SyntaxError {
        return new FunctionValueSource(fq.parseValueSourceList());
    }
}
public class FunctionValueSource extends ValueSource {
    final static Logger log= LoggerFactory.getLogger(FunctionValueSource.class);
    private  List<ValueSource>  valueSources;

    public FunctionValueSource(List<ValueSource> source) {
        this.valueSources=source;
    }

    @Override
    public FunctionValues getValues(Map map, final LeafReaderContext leafReaderContext) throws IOException {
        final FunctionValues y=this.valueSources.get(0).getValues(map,leafReaderContext);
        final FunctionValues m=this.valueSources.get(1).getValues(map,leafReaderContext);
        return new FloatDocValues(this) {
            @Override
            public float floatVal(int docId) {
                String title = y.strVal(docId);
                String des =m.strVal(docId);
                //这里加入你的逻辑计算
                return 2l;
            }
        };
    }

    @Override
    public boolean equals(Object o) {
        return false;
    }

    @Override
    public int hashCode() {
        return 0;
    }

    @Override
    public String description() {
        return null;
    }
}

配置文件修改

<valueSourceParser name="myfunc"  class="com.hmkx.solr.funtion.HmkxValueSourceParser" />

重要的地方:查询需要指定参数 {!boost b=myfunc(title,description)}id:I517648

总结:个人更喜欢用第一种方式,使用上更简单

代码地址 https://gitee.com/huhaitao/solr2test/tree/master/solr2score

 

 

 

 

转载于:https://my.oschina.net/haitaohu/blog/3079260

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值