需求
简要说明一下业务需求。在店铺搜索时,要根据查询的坐标值与店铺的距离以及每个店铺所打出的权重字段做自定义排序。排序规则为:以查询坐标为圆心,划分半径分别为5km,10km,15km的三个环,在每个环内按照权重字段的分值进行排序,并优先显示半径小的环内的店铺,即5公里内的店铺全部优先于10公里内的店铺,10公里内的店铺全部优先于15公里内的店铺。
代码设计
参考文章 http://qindongliang.iteye.com/blog/2297814中function query的介绍
引用
1,继承ValueSource类,重写getValues方法,并在返回的方法中,完成评分计算逻辑
2,继承ValueSourceParser类,并重写parser方法,返回1定义的类,
建议在parser方法里面,获取ValueSource然后传入自定义的ValueSource类里面复用,
不建议直接从DocValues里面读取,因为基于这个IndexSearch的打开的ValueSource耗费资源更少。至此,代码完成打包项目成一个jar,拷贝至server\solr-webapp\webapp\WEB-INF\lib中
3,在solrconfig.xml中,注册我们的组件:
参考上述链接,而且在solr中有一个既有的类叫GeoDistValueSourceParser,用于将给定的经纬度,计算搜索结果的经纬度距离,进行排序。经过百岁的提示,可以用组合模式,将其返回的距离直接得到,只需在返回分数的方法中加入我们的业务逻辑即可。
项目的uml图如下:
1:自定义一个MyValueSourceParser类继承自GeoDistValueSourceParser,在parse方法中,得到基类返回的ValueSource,并作为参数传入我们自己定义的MyValueSource中
@Override public ValueSource parse(FunctionQParser fp) throws SyntaxError { /**得到ValueSource为计算得到的两个坐标点之间的距离*/ ValueSource valueSource = super.parse(fp); if( !(valueSource instanceof HaversineConstFunction)){ throw new IllegalStateException("instance type must be HaversineConstFunction,but now is " + valueSource.getClass()); } return new ShopMultiCirclesValueSource((HaversineConstFunction) valueSource, params); }
2:在自定义的MyValueSource中,在getValue方法中,先调用基类返回的ValueSource的getValue方法,将返回的FunctionValue作为参数传入自定义的MyFunctionValue中
@Override public FunctionValues getValues(Map context, LeafReaderContext readerContext) throws IOException { FunctionValues functionValues = valuesource.getValues(context, readerContext); final NumericDocValues weightDcoValue = DocValues.getNumeric(readerContext.reader(),params.weightField); ShopMultiCirclesFunctionValue shopMultiCirclesFunctionValue = new ShopMultiCirclesFunctionValue (this,functionValues,weightDcoValue,params); return shopMultiCirclesFunctionValue; }
3:在自定义的MyFunctionValue中,在doubleVal方法中,确定我们自定义的评分逻辑即可。
/**利用得到的距离,做三个环的距离评分,再用得到的距离分加上权重,作为最后的得分*/ @Override public double doubleVal(int doc) { Double distance = functionValues.doubleVal(doc); double weight = (double) weightDocValues.get(doc); double x = (distance >= 0 && distance <= params.firstCircle) ? FIRST_CIRCLE_SCORE : (distance > params .firstCircle && distance <= params.secondCircle) ? SECOND_CIRCLE_SCORE : (distance > params .secondCircle && distance <= params.thirdCircle) ? THIRD_CIRCLE_SCORE : OUTSIDE_CIRCLE_SCORE; double finalScore = x + weight; log.info("the score details:id " + doc + " weight " + weight + " distance " + distance + " x " + x + " final score " + finalScore); return finalScore; }
4:最后要再solrconfig.xml中注册我们的组件
5 10 15 period_score