lucene4 spatial4j

    有次在一个项目中有人提出了一种基于LBS业务的搜索的技术,lucene spatial搜索。随后在网上进行了大搜索。只搜索出一些lucene3代的小例子。现在lucene已经发到4.6了,3代明显太落后了。所以,进行lucene4 spatial的例子搜索,很不幸:使用的人太少了,没有一个例子。就连官网上也没有太多的说明。
 
由于没有仔细观看官网关于spatial模块的说明,未发现有用东西。一次不经意找到一行。位于API_Javadocs中spatial search的index.html上“For some sample code showing how to use the API, see SpatialExample.java in the tests.”
 
终于找到了入门的法宝。
SpatialExample 
 
下面对其中使用到的核心类作下说明:
SpatialContext:空间模块上下文,们于spatial4j包中,一个全局单例对象。它是spatial4j的入口。它提供读写Shape对象的通道。
Shape:形状接口。
SpatialStrategy:lucene spatial模块提供创建查询Shape对象索途径。
package com.spatial.client;


import java.io.File;
import java.io.IOException;


import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.IntField;
import org.apache.lucene.document.StoredField;
import org.apache.lucene.document.TextField;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.index.IndexableField;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.Filter;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TermQuery;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.spatial.SpatialStrategy;
import org.apache.lucene.spatial.prefix.RecursivePrefixTreeStrategy;
import org.apache.lucene.spatial.prefix.tree.GeohashPrefixTree;
import org.apache.lucene.spatial.prefix.tree.SpatialPrefixTree;
import org.apache.lucene.spatial.query.SpatialArgs;
import org.apache.lucene.spatial.query.SpatialOperation;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.util.Version;


import com.chenlb.mmseg4j.analysis.SimpleAnalyzer;
import com.spatial4j.core.context.SpatialContext;
import com.spatial4j.core.distance.DistanceUtils;
import com.spatial4j.core.shape.Shape;


public class LuceneSpatialExample {




  public static void main(String[] args) throws IOException {
    new LuceneSpatialExample().test();
  }


  public void test() throws IOException {
    init();
    createIndex();
    search();
  }


  private SpatialContext ctx;//"ctx" is the conventional variable name


  private SpatialStrategy strategy;


  private Directory directory;
  
  private String indexPath = "E:\\index\\spatial\\";
  
  protected void init() {
    //Typical geospatial context
    //  These can also be constructed from SpatialContextFactory
    this.ctx = SpatialContext.GEO;


    int maxLevels = 11;//results in sub-meter precision for geohash
    //TODO demo lookup by detail distance
    //  This can also be constructed from SpatialPrefixTreeFactory
    SpatialPrefixTree grid = new GeohashPrefixTree(ctx, maxLevels);


    this.strategy = new RecursivePrefixTreeStrategy(grid, "myGeoField");


    try {
this.directory = FSDirectory.open(new File(indexPath));
} catch (IOException e) {
e.printStackTrace();
}
  }


  private void createIndex() throws IOException {
    IndexWriterConfig iwConfig = new IndexWriterConfig(Version.LUCENE_40,new SimpleAnalyzer());
    IndexWriter indexWriter = new IndexWriter(directory, iwConfig);
    //Spatial4j is x-y order for arguments|对于参数的顺序是Longitude,Latitude
    indexWriter.addDocument(newSampleDocument(0, "一星大饭店",ctx.makePoint(116.430360,39.939686)));
    indexWriter.addDocument(newSampleDocument(1, "二星大饭店",ctx.makePoint( 116.430319,39.939702)));
    indexWriter.addDocument(newSampleDocument(2, "三星大饭店",ctx.makePoint(116.430459,39.939802)));
    indexWriter.addDocument(newSampleDocument(3, "四星大饭店", ctx.makePoint(116.430449,39.939902)));
    indexWriter.addDocument(newSampleDocument(4, "六星大饭店",ctx.makePoint(116.430439,39.93402)));
    indexWriter.addDocument(newSampleDocument(5, "七星大饭店",ctx.makePoint(116.430419,39.934102)));
    indexWriter.addDocument(newSampleDocument(6, "五星大饭店",ctx.makePoint(116.430429,39.934202)));
    indexWriter.addDocument(newSampleDocument(6, "五星大酒店",ctx.makePoint(115.430429,39.934202)));
    indexWriter.commit();
    indexWriter.close();
  }


  private Document newSampleDocument(int id,String keyword, Shape... shapes) {
    Document doc = new Document();
    doc.add(new IntField("id", id, Field.Store.YES));
    //Potentially more than one shape in this field is supported by some
    // strategies; see the javadocs of the SpatialStrategy impl to see.
    for (Shape shape : shapes) {
      for (IndexableField f : strategy.createIndexableFields(shape)) {
        doc.add(f);
      }
      //store it too; the format is up to you
      doc.add(new StoredField(strategy.getFieldName(), ctx.toString(shape)));
    }
    doc.add(new TextField("keyword",keyword, Field.Store.YES));
    return doc;
  }


  private void search() throws IOException {
    IndexReader indexReader = DirectoryReader.open(directory);
    IndexSearcher indexSearcher = new IndexSearcher(indexReader);
    {
     //16米范围内
     SpatialArgs args = new SpatialArgs(SpatialOperation.Intersects, ctx.makeCircle(116.430459,39.939802, DistanceUtils.dist2Degrees(0.016,DistanceUtils.EARTH_MEAN_RADIUS_KM)));
     Filter filter = strategy.makeFilter(args);
Term term = new Term("keyword","饭店");
Query query001 = new TermQuery(term);


TopDocs docs = indexSearcher.search(query001,filter,1000);
System.out.println(docs.totalHits);


     ScoreDoc[] scoreDoc = docs.scoreDocs;
     if(docs.totalHits>0)
     for (int i = 0; i < scoreDoc.length; i++) {
int doc = scoreDoc[i].doc;
Document mydoc = indexReader.document(doc);
System.out.println(mydoc.get("myGeoField"));
String value = mydoc.get("myGeoField");
String []lonlat = value.split(" ");
double eLat = Double.valueOf(lonlat[1]);
double eLon = Double.valueOf(lonlat[0]);
System.out.println(SpatialUtils.getDistance(39.939802, 116.430459, eLat, eLon, false)+"|keyword: "+mydoc.get("keyword"));
}
    }
    indexReader.close();
  }


}
 
 
 
lucene spatial 模块本身就是一个Filter的实现。核心还是lucene的全文检索。真正理解学习新框架或新开源的东西就得从TestCase看起。
 

相关推荐