lucene-使用自定义排序方法

本文介绍如何使用Lucene实现基于地理位置距离的自定义排序,并通过示例代码展示了为不同地点类型计算距离并排序的过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

计算基点与索引库中每个地理位置的距离,按距离大小排序。基点为(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);//获得实际的文档 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值