背景:目前我们solr中存储的数据因为很多属性字段的不确定性,选择将数据在一个大字段中合并。如下格式中attr字段代表着一个不确定的属性字段,可能为身高、体重、性别等,也可能为页码、出版方、售价等
{
“id”:“1”
“attr”:“180,80,男”
“type”:“person”
},
{
“id”:“2”
“attr”:“50,人民出版社,50¥”
“type”:“book”
}
针对这种近期产品提出一个要针对人类按照身高排序,书籍类按照售价排序。这种需求怎么搞呢?身高属性已经跟其他属性混为一个字段,常规排序怕是行不通。
思路:既然常规思路行不通,我们想能不能通过函数截取的方式通过比对该字符串指定位置来实现排序呢?下面直接上代码
代码:首先需要定义两个类SubStrValueSourceParser
import org.apache.commons.lang.StringUtils; import org.apache.lucene.queries.function.ValueSource; import org.apache.solr.schema.SchemaField; import org.apache.solr.search.FunctionQParser; import org.apache.solr.search.SyntaxError; import org.apache.solr.search.ValueSourceParser; public class SubStrValueSourceParser extends ValueSourceParser { public SubStrValueSourceParser() { super(); } @Override public ValueSource parse(FunctionQParser fp) throws SyntaxError { String fieldStr = fp.parseArg();//第一个参数 String bgnLocation = fp.parseArg();//第二个参数数组下标 ValueSource location = getValueSource(fp, fieldStr);//取得函数调用时指定字段值 //将参数及需要的文档的值传给自定义的ValueSource方法,打分规则在自定义的ValueSource中定制 SubStrValueSource stringFieldSource = new SubStrValueSource(location, bgnLocation); return stringFieldSource; } //该方法是根据字段名,从FunctionQParser得到文档该字段的相关信息 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); } }
import org.apache.commons.lang.StringUtils; import org.apache.lucene.index.LeafReaderContext; import org.apache.lucene.queries.function.FunctionValues; import org.apache.lucene.queries.function.ValueSource; import org.apache.lucene.queries.function.docvalues.DoubleDocValues; import org.apache.lucene.queries.function.docvalues.FloatDocValues; import org.apache.lucene.queries.function.docvalues.IntDocValues; import org.apache.lucene.queries.function.docvalues.StrDocValues; import org.codehaus.jackson.map.ObjectMapper; import org.codehaus.jackson.type.TypeReference; import java.io.IOException; import java.util.List; import java.util.Map; public class SubStrValueSource extends ValueSource { private ValueSource attr13;//需要进行格式转换的字段 private String location; //通过构造方法获取字段 public SubStrValueSource(ValueSource attr13, String location) { this.attr13 = attr13; this.location = location; } @Override public FunctionValues getValues(Map map, LeafReaderContext leafReaderContext) throws IOException { final FunctionValues fieldValues = attr13.getValues(map, leafReaderContext);//获取attr13的值 return new DoubleDocValues(this) { @Override public double doubleVal(int i) throws IOException { String fieldStr = null; Double retValue = 0.00; //若要被转化的值为空或者为空字符串则返回0 if (fieldValues.strVal(i) == null || "".equals(fieldValues.strVal(i))) { } else { fieldStr = fieldValues.strVal(i); String[] vList = fieldStr.split(","); //若实际数组长度小于下标则不处理直接返回0,若大于或等于下标则截取对应位置数据 if (vList.length < Integer.parseInt(location) + 1) { } else { String content = vList[Integer.parseInt(location)]; retValue = Double.parseDouble(content); } } return retValue; } }; } @Override public boolean equals(Object o) { return true; } @Override public int hashCode() { return 0; } @Override public String description() { return name(); } public String name() { return "subStr"; } }
发布:代码逻辑编写完毕后打成jar包并按照如下步骤操作
- 在solr安装目录下dist目录中将打好的jar包放进去
- 在solrconfig.xml中新增以下配置
<lib dir="${solr.install.dir:../../../..}/dist/" regex="com.tl.solr.*\.jar" />
<valueSourceParser name="subStr"
class="SubStrValueSourceParser" />
搞定之后重启服务生效
使用效果: