lucene本身提供很多默认的排序方式,比如根据相关度,写入索引的顺序,域的值等等,提供正向或者逆向的排序,大部分情况下,这已经能满足我们的基本要求。但在实际的应用中,也有一些需求,需要搜索的结果,依据搜索内容进行排序,比如在搜跟当前位置相关的对象时,需要根据距离的远近,来对返回的结果进行排序。每一次搜索的坐标不一样,因此,在搜索的过程中,需要动态来计算匹配对象和搜索坐标的距离。lucene提供了自定义的排序的功能。需要继承FieldComparatorSource和FieldComparator这两个抽象类。
本文中所举的例子也比较常见,使用WildcardQuery进行通配符匹配搜索,在搜索多个对象的名称时,默认返回结果是依据写入索引的id由小到大返回的(打分都是1.0)。在匹配结果太多的时候,用户体检非常糟糕。当然,可以进行分词,然后计算得分的高低返回。这里换一个思路,依旧使用WildcardQuery进行通配符匹配搜索,但对搜索的结果,依据搜索字符串和匹配对象名称的字符串计算编辑距离,然后排序。实际上,这就是sql like功能的lucene实现。
StrDistanceComparatorSouce 是我们定义的类,继承FieldComparatorSource,保存了每一次搜索query的name,然后使用FieldComparator的继承类来实现搜索结果的排序。
StrFieldComparator是FieldComparator的继承类,同时,也是StrDistanceComparatorSouce 的内部类,这样就可以方便使用StrDistanceComparatorSouce 中的name,继承FieldComparator需要实现5个方法,具体功能查lucene文档,代码如下。
public class StrDistanceComparatorSource extends FieldComparatorSource {
private static final Logger logger = Logger
.getLogger(StrDistanceComparatorSource.class);
private static final long serialVersionUID = 1L;
private String name;
public StrDistanceComparatorSource(String name) {
this.name = name;
}
@Override
public FieldComparator<?> newComparator(String fieldName, int numHits,
int sortPost, boolean reversed) throws IOException {
return new StrFieldComparator(fieldName, numHits);
}
@SuppressWarnings("rawtypes")
private class StrFieldComparator extends FieldComparator {
private String[] nameDoc;
private int[] values;
private int bottom;
private final String field;
public StrFieldComparator(String field, int numHits) throws IOException {
values = new int[numHits];
this.field = field;
}
@Override
public int compare(int slot1, int slot2) {
if (values[slot1] > values[slot2])
return 1;
if (values[slot1] < values[slot2])
return -1;
return 0;
}
private int getDistance(int doc) {
if (name == null) {
logger.error("name is null");
name = "";
}
if (nameDoc[doc] == null) {
logger.error("name doc is null");
nameDoc[doc] = "";
}
return StringUtils.getLevenshteinDistance(name, nameDoc[doc]);
}
@Override
public int compareBottom(int doc) throws IOException {
int distance = getDistance(doc);
if (bottom < distance)
return -1;
if (bottom > distance)
return 1;
return 0;
}
@Override
public void copy(int slot, int doc) throws IOException {
values[slot] = getDistance(doc);
}
@Override
public void setBottom(int slot) {
bottom = values[slot];
}
@Override
public void setNextReader(IndexReader reader, int docBase)
throws IOException {
nameDoc = FieldCache.DEFAULT.getStrings(reader, this.field);
}
@Override
public Comparable value(int slot) {
return this.values[slot];
}
最后生成自定义的排序器,首先按照编辑距离排序,如果编辑距离相同,则按照索引的id从低到高进行排序。
Sort sort = new Sort(new SortField("name",
new StrDistanceComparatorSource(queryStr)),
SortField.FIELD_DOC);