计算基点与索引库中每个地理位置的距离,按距离大小排序。基点为(0,0),计算从(0,0)到地图中各个地方的距离,然后依此排序,但地方进行了分类,包括restaurant、school、shop。这些类放在一个type域中,
1、通过实现SortComparatorSource接口来完成自定义排序。对每个类分别完成计算基点到这些类的地方的距离
public class DistanceComparatorSource implementsSortComparatorSource{
private int x;
private int y;
publicDistanceComparatorSource(int x,int y){
this.x=x;
this.y=y;
}
//SortComparatorSource唯一需要实现的方法是newComparator
publicScoreDocComparator newComparator(IndexReader reader,Stringfieldname)
throws IOException
{
//返回一个自定义比较器
return newDistanceScoreDocLookupComparator(reader,fieldname,x,y);
}
//lucene会负责对ScoreDocComparator对象存储,每个基点对应于一个比较器
privatestatic class DistanceScoreDocLookupComparator implements ScoreDocComparator{
final TermEnum enumerator=reader.terms(newTerm(filedname,""));//定义一个项枚举器
//,对reader的所有文档的坐标项进行迭代
distances=newfloat[reader.maxDoc()]; //定义该基点与所有文档坐标项的距离数组
if (distances.length>0) {//保证至少有一个文档
TermDocstermDocs=reader.termDocs(); //取出所有文档的项
try{
if (enumerator.term()==null){
throw new RuntimeException("no terms infield"+fieldname);
}
do{//开始计算距离数组
Term term=enumerator.term();
if (term.field()!=fieldname) break;
termDocs.seek(enumeartor);
while (termDocs.next()){
String[] xy=term.text().split(",");//从项值得出坐标
//计算,存储基点与该项坐标之间的距离
intdeltax=Integer.parseInt(xy[0])-x;
int deltay=Integer.parseInt(xy[1])-y;
distances[termDocs.doc()]=
(float) Math.sqrt(deltax*deltax_delay*deltay);
}//完成包含当前项的文档的遍历完成距离计算
}while(enumerator.next());//对指定域内的所有项进行遍历,笔者认为fieldname
//是指query中指定的域名,即这些地点的类型,因为地方可能至少要几种类
//型,所以要对域内的项进行遍历。
}fianlly{
termDocs.close();
}
}
}
public intcompare(){//调用完成排序
if (distances[i.doc]<distances[j.doc]) return-1;
if (distances[i.doc]>distances[j.doc]) return1;
return0;
}
publicComparable sortValue(ScoreDoc i){//输出确切的实际距离值
returnnew Float(distances[i.doc]);
}
public intsortType(){
return SortField.FLOAT;
}
}
public String toString(){
return "distance from("x","+y+")";
}
2、我们为每个地点都指定了三个域,即一个地名,一个用X和Y坐标表示的位置。
public class DistanceSortingTest extends tetstcase{
privateRAMDirectory directory;
privateIndexSearcher searcher;
privateQuery query;
protectedvoid setUp() throws Exception{
directory=new RAMDirectory();
IndexWriter writer=new IndexWriter(directory,newWhitespaceAnalyzer(),true);
addPoint(writer,"E1 Charro","restaurant",1,2);
addPoint(writer,"cafe poca cosa","restaurant",5,9);
addPoint(writer,"os betos","restaurant",9,6);
addPoint(writer,"nico's tacoshop","restaurant",3,8);
writer.close();
searcher=newIndexSearcher(directory);
query=new TermQuery(new Term("type","restaurant"));
}
privatevoid addPoint(IndexWriter writer,String name,String type,int x,inty)
throws IOExceptoin{
Document doc=new Document();
doc.add(Field.Keyword("name",name));
doc.add(Field.Keyword("type",type));
doc.add(Field.Keyword("location",x+","+y));
}
}
下面进行测试
Sort sort=new Sort(newSortField("location",new DistanceComparatorSource(0,0)));
Hits hits=searcher.search(query,sort);
3、访问自定义排序的值
使用IndexSearcher的重载的search方法:
pulbic TopFieldDocs search(Query query,Filter filter,final intnDocs,Sort sort)
1)TopFieldDocs类中包括了HIts对象的总数,用来排序的SortField数组、FieldDoc对象的集合。FieldDoc封装了已经计算出来的原始评分、文档ID及Comparables集合,Comparable的值被每个SortField对象调用。
2)如果没有使用与排序相关的类,lucene为我们提供了类似的底层API,返回一个包含ScoreDoc对象的TopDocs对象。
3)
Sort sort=new Sort(newSortField("location",new DistanceComparatorSource(0,0)));
//指定返回的hit对象上限为3
TopFieldDocs docs=searcher.search(query,null,3,sort);
//hits对象的总数
assertEquals(4,docs.totalHits);//总数为4,因为要对所有hits进行评估找出3个最优的命中结果
assertEquals(3,docs.scoreDocs.length);//返回文档总数
FieldDoc fieldDoc=(FieldDoc)docs.scoreDocs[0];//获得排序值
assertEquals("(10,10)->(9,6)=sqrt(17)",newFloat(Math.sqrt(17)),fieldDoc.fields[0]);//断言距离最近的餐厅
Documentdocument=searcher.doc(fieldDoc.doc);//获得实际的文档