重寫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,如下圖
而使用哪些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