概述
Solr提供ValueSourceParser来实现自定义评分函数,在自定义评分函数中封装评分逻辑,然后根据自定义评分函数计算的值进行排序。
准备数据
将数据导入solr
原始查询
q=sdes:共和国
fq=spopulation:[ 500000000 TO * ]
fl=*,score
排序默认使用score得分排序,可发现巴西的评分(0.15021087)比中国评分(0.1201687)高,那么可以利用自定义评分函数的值,来实现重新排序
自定义评分逻辑
//默认得分都是1.0分
float boot = 1.0f;
//如果sarea不存在或者为小,则减去0.2分
if (StringUtils.isBlank(sarea) || "小".equals(sarea)) {
boot -= 0.2f;
}
//如果sname存在中,则增加0.2分
if (sname.contains("中")) {
boot += 0.2f;
}
//如果spopulation大于500000000,则增加0.5分
if (spopulation > 500000000) {
boot += 0.5f;
}
根据新的评分逻辑计算,巴西的评分(1.0),中国的评分(1.7)
编写代码
public class MyValueSourceParser extends ValueSourceParser {
private final Logger log = LoggerFactory.getLogger(MyValueSourceParser.class);
public MyValueSourceParser() {
super();
}
@Override
public ValueSource parse(FunctionQParser fp) throws SyntaxError {
/**
* 按顺序取2个参数
*/
String arg1 = fp.parseArg(); //取到第一个参数
String arg2 = fp.parseArg(); //取到第二个参数
log.info("MyFilterQParserPlugin arg1=" + arg1);
log.info("MyFilterQParserPlugin arg2=" + arg2);
/**
* 获取LocalParams
*/
SolrParams solrParams = fp.getLocalParams();
if (null != solrParams) {
Iterator<String> it = solrParams.getParameterNamesIterator();
while (it.hasNext()) {
final String name = it.next();
final String[] values = solrParams.getParams(name);
for (String v : values) {
log.info("LocalParams name:" + v);
}
}
}
/**
* 获取Params
*/
solrParams = fp.getParams();
if (null != solrParams) {
Iterator<String> it = solrParams.getParameterNamesIterator();
while (it.hasNext()) {
final String name = it.next();
final String[] values = solrParams.getParams(name);
for (String v : values) {
log.info("Params " + name + ":" + v);
}
}
}
/**
*
* 获取三个字段的信息
*/
ValueSource sareavs = getValueSource(fp, "id");
ValueSource spopulationvs = getValueSource(fp, "spopulation");
ValueSource snamevs = getValueSource(fp, "sname");
/**
* 将相关文档的值传给自定义的ValueSource方法,打分规则在自定义的ValueSource中定制
*/
MyValueSource myValueSource = new MyValueSource(sareavs, spopulationvs, snamevs);
return myValueSource;
}
/**
* 该方法是根据字段名,从FunctionQParser得到文档该字段的相关信息
* @param fp
* @param arg
* @return
*/
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);
}
}
public class MyValueSource extends ValueSource {
private final Logger log = LoggerFactory.getLogger(MyValueSource.class);
private ValueSource sareavs;
private ValueSource spopulationvs;
private ValueSource snamevs;
public MyValueSource(ValueSource sareavs, ValueSource spopulationvs, ValueSource snamevs) {
this.sareavs = sareavs;
this.spopulationvs = spopulationvs;
this.snamevs = snamevs;
}
@Override
public FunctionValues getValues(Map context,AtomicReaderContext readerContext) throws IOException {
final FunctionValues sareafv = sareavs.getValues(context,readerContext);
final FunctionValues spopulationfv = spopulationvs.getValues(context,readerContext);
final FunctionValues snamefv = snamevs.getValues(context,readerContext);
return new FloatDocValues(this) {
/**
* 重写floatVal方法,自定义打分排序规则
* @param doc
* @return
*/
@Override
public float floatVal(int doc) {
String sarea = sareafv.strVal(doc);
long spopulation = spopulationfv.longVal(doc);
String sname = snamefv.strVal(doc);
log.info(toString());
float boot = 1.0f;
if (StringUtils.isBlank(sarea) || "小".equals(sarea)) {
boot -= 0.2f;
}
if (sname.contains("中")) {
boot += 0.2f;
}
if (spopulation > 500000000) {
boot += 0.5f;
}
return boot;
}
@Override
public String toString(int doc) {
return name() + '(' + sareafv.strVal(doc) + ',' + spopulationfv.strVal(doc) + ',' + snamefv.strVal(doc) + ')';
}
};
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
} else if (obj == null) {
return false;
} else if (this.getClass() != obj.getClass()) {
return false;
} else {
MyValueSource other = (MyValueSource) obj;
if (this.sareavs == null) {
if (other.sareavs != null) {
return false;
}
} else if (!this.sareavs.equals(other.sareavs)) {
return false;
}
if (this.spopulationvs == null) {
if (other.spopulationvs != null) {
return false;
}
} else if (!this.spopulationvs.equals(other.spopulationvs)) {
return false;
}
if (this.snamevs == null) {
if (other.snamevs != null) {
return false;
}
} else if (!this.snamevs.equals(other.snamevs)) {
return false;
}
return true;
}
}
@Override
public int hashCode() {
int result = 1;
result = 31 * result + (this.sareavs == null ? 0 : this.sareavs.hashCode());
result = 31 * result + (this.spopulationvs == null ? 0 : this.spopulationvs.hashCode());
result = 31 * result + (this.snamevs == null ? 0 : this.snamevs.hashCode());
return result;
}
@Override
public String description() {
return name();
}
public String name() {
return "MyValueSource";
}
}
solrconfig配置
配置插件名称和关联类
<valueSourceParser name="myValueSource" class="cn.sing.plugin.valuesource.MyValueSourceParser" />
使用方式:
- sort={!func}myValueSource("1","2") asc
- fl=*,score,_val_:myValueSource("1","2")
lib路径
将上面编写的2个类打包放到lib引用的目录中,lib也编写在solrconfig配置中
<lib dir="D:\Soft\solr-cloud\solorconfig\testindex\javalib" regex=".*\.jar" />
重新查询
q=sdes:共和国
fq=spopulation:[ 500000000 TO * ]
fl=*,score,_val_:myValueSource("1","2")
sort={!func}myValueSource("1","2") desc
执行日志
INFO - 2020-02-18 16:02:01.755; cn.sing.plugin.valuesource.MyValueSourceParser; MyFilterQParserPlugin arg1=1
INFO - 2020-02-18 16:02:01.755; cn.sing.plugin.valuesource.MyValueSourceParser; MyFilterQParserPlugin arg2=2
INFO - 2020-02-18 16:02:01.755; cn.sing.plugin.valuesource.MyValueSourceParser; LocalParams v:myValueSource("1","2") desc
INFO - 2020-02-18 16:02:01.756; cn.sing.plugin.valuesource.MyValueSourceParser; LocalParams type:func
INFO - 2020-02-18 16:02:01.756; cn.sing.plugin.valuesource.MyValueSourceParser; Params defType:lucene
INFO - 2020-02-18 16:02:01.756; cn.sing.plugin.valuesource.MyValueSourceParser; Params rows:10
INFO - 2020-02-18 16:02:01.756; cn.sing.plugin.valuesource.MyValueSourceParser; Params distrib:false
INFO - 2020-02-18 16:02:01.756; cn.sing.plugin.valuesource.MyValueSourceParser; Params fl:id,score
INFO - 2020-02-18 16:02:01.756; cn.sing.plugin.valuesource.MyValueSourceParser; Params start:0
INFO - 2020-02-18 16:02:01.756; cn.sing.plugin.valuesource.MyValueSourceParser; Params fsv:true
INFO - 2020-02-18 16:02:01.757; cn.sing.plugin.valuesource.MyValueSourceParser; Params fq:spopulation:[ 500000000 TO * ]
INFO - 2020-02-18 16:02:01.757; cn.sing.plugin.valuesource.MyValueSourceParser; Params sort:{!func}myValueSource("1","2") desc
INFO - 2020-02-18 16:02:01.757; cn.sing.plugin.valuesource.MyValueSourceParser; Params shard.url:http://127.0.0.1:8891/solr/testindex_shard2_replica1/
INFO - 2020-02-18 16:02:01.757; cn.sing.plugin.valuesource.MyValueSourceParser; Params rows:10
INFO - 2020-02-18 16:02:01.757; cn.sing.plugin.valuesource.MyValueSourceParser; Params version:2
INFO - 2020-02-18 16:02:01.757; cn.sing.plugin.valuesource.MyValueSourceParser; Params q:sdes:����
INFO - 2020-02-18 16:02:01.757; cn.sing.plugin.valuesource.MyValueSourceParser; Params defType:lucene
INFO - 2020-02-18 16:02:01.758; cn.sing.plugin.valuesource.MyValueSourceParser; Params NOW:1582012921733
INFO - 2020-02-18 16:02:01.758; cn.sing.plugin.valuesource.MyValueSourceParser; Params isShard:true
INFO - 2020-02-18 16:02:01.758; cn.sing.plugin.valuesource.MyValueSourceParser; Params wt:javabin
INFO - 2020-02-18 16:02:01.759; org.apache.solr.core.SolrCore; [testindex_shard2_replica1] webapp=/solr path=/select params={distrib=false&fl=id,score&start=0&fsv=true&fq=spopulation:[+500000000+TO+*+]&sort={!func}myValueSource("1","2")+desc&shard.url=http://127.0.0.1:8891/solr/testindex_shard2_replica1/&rows=10&version=2&q=sdes:����&defType=lucene&NOW=1582012921733&isShard=true&wt=javabin} hits=0 status=0 QTime=6
INFO - 2020-02-18 16:02:01.790; org.apache.solr.core.SolrCore; [testindex_shard2_replica1] webapp=/solr path=/select params={q=sdes:����&indent=true&fl=*,score,_val_:myValueSource("1","2")&fq=spopulation:[+500000000+TO+*+]&sort={!func}myValueSource("1","2")+desc&wt=json} hits=2 status=0 QTime=57