说明版本:历史遗留项目使用的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,如下图
而使用哪些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