1、简介

关于空间数据搜索,以前写过《使用Solr进行空间搜索》这篇文章,是基于Solr的GIS数据的索引和检索。

Solr和ElasticSearch这两者都是基于Lucene实现的,两者都可以进行空间搜索(Spatial Search),在有些场景,我们需要把Lucene嵌入到已有的系统提供数据索引和检索的功能,这篇文章介绍下用Lucene如何索引带有经纬度的POI信息并进行检索。

2、环境数据

Lucene版本:5.3.1

POI数据库:Base_Station测试数据,每条数据主要是ID,经纬度和地址。

3、实现

基本变量定义,这里对“地址”信息进行了分词,分词使用了Lucene自带的smartcnSmartChineseAnalyzer。

    private String indexPath = "D:/IndexPoiData";private IndexWriter indexWriter = null;private SmartChineseAnalyzer analyzer = new SmartChineseAnalyzer(true);private IndexSearcher indexSearcher = null;// Field Nameprivate static final String IDFieldName = "id";private static final String AddressFieldName = "address";private static final String LatFieldName = "lat";private static final String LngFieldName = "lng";private static final String GeoFieldName = "geoField";// Spatial index and searchprivate SpatialContext ctx;private SpatialStrategy strategy;public PoiIndexService() throws IOException {init();}public PoiIndexService(String indexPath) throws IOException {this.indexPath = indexPath;init();}protected void init() throws IOException {Directory directory = new SimpleFSDirectory(Paths.get(indexPath));IndexWriterConfig config = new IndexWriterConfig(analyzer);indexWriter = new IndexWriter(directory, config);DirectoryReader ireader = DirectoryReader.open(directory);indexSearcher = new IndexSearcher(ireader);// Typical geospatial context// These can also be constructed from SpatialContextFactoryctx = SpatialContext.GEO;int maxLevels = 11; // results in sub-meter precision for geohash// This can also be constructed from SpatialPrefixTreeFactorySpatialPrefixTree grid = new GeohashPrefixTree(ctx, maxLevels);strategy = new RecursivePrefixTreeStrategy(grid, GeoFieldName);}

索引数据

    public boolean indexPoiDataList(List<PoiData> dataList) {try {if (dataList != null && dataList.size() > 0) {List<Document> docs = new ArrayList<>();for (PoiData data : dataList) {Document doc = new Document();doc.add(new LongField(IDFieldName, data.getId(), Field.Store.YES));doc.add(new DoubleField(LatFieldName, data.getLat(), Field.Store.YES));doc.add(new DoubleField(LngFieldName, data.getLng(), Field.Store.YES));doc.add(new TextField(AddressFieldName, data.getAddress(), Field.Store.YES));Point point = ctx.makePoint(data.getLng(),data.getLat());for (Field f : strategy.createIndexableFields(point)) {doc.add(f);}docs.add(doc);}indexWriter.addDocuments(docs);indexWriter.commit();return true;}return false;} catch (Exception e) {log.error(e.toString());return false;}}

这里的PoiData是个普通的POJO。

检索圆形范围内的数据,按距离从近到远排序:

    public List<PoiData> searchPoiInCircle(double lng, double lat, double radius){List<PoiData> results= new ArrayList<>();Shape circle = ctx.makeCircle(lng, lat, DistanceUtils.dist2Degrees(radius, DistanceUtils.EARTH_MEAN_RADIUS_KM));SpatialArgs args = new SpatialArgs(SpatialOperation.Intersects, circle);Query query = strategy.makeQuery(args);Point pt = ctx.makePoint(lng, lat);ValueSource valueSource = strategy.makeDistanceValueSource(pt, DistanceUtils.DEG_TO_KM);//the distance (in km)Sort distSort = null;TopDocs docs = null;try {//false = asc distdistSort = new Sort(valueSource.getSortField(false)).rewrite(indexSearcher);docs = indexSearcher.search(query, 10, distSort);} catch (IOException e) {log.error(e.toString());}if(docs!=null){ScoreDoc[] scoreDocs = docs.scoreDocs;printDocs(scoreDocs);results = getPoiDatasFromDoc(scoreDocs);}return results;}private List<PoiData> getPoiDatasFromDoc(ScoreDoc[] scoreDocs){List<PoiData> datas = new ArrayList<>();if (scoreDocs != null) {//System.out.println("总数:" + scoreDocs.length);for (int i = 0; i < scoreDocs.length; i++) {try {Document hitDoc = indexSearcher.doc(scoreDocs[i].doc);PoiData data = new PoiData();data.setId(Long.parseLong((hitDoc.get(IDFieldName))));data.setLng(Double.parseDouble(hitDoc.get(LngFieldName)));data.setLat(Double.parseDouble(hitDoc.get(LatFieldName)));data.setAddress(hitDoc.get(AddressFieldName));datas.add(data);} catch (IOException e) {log.error(e.toString());}}}return datas;}

搜索矩形范围内的数据:

    public List<PoiData> searchPoiInRectangle(double minLng, double minLat, double maxLng, double maxLat) {List<PoiData> results= new ArrayList<>();Point lowerLeftPoint = ctx.makePoint(minLng, minLat);Point upperRightPoint = ctx.makePoint(maxLng, maxLat);Shape rect = ctx.makeRectangle(lowerLeftPoint, upperRightPoint);SpatialArgs args = new SpatialArgs(SpatialOperation.Intersects, rect);Query query = strategy.makeQuery(args);TopDocs docs = null;try {docs = indexSearcher.search(query, 10);} catch (IOException e) {log.error(e.toString());}if(docs!=null){ScoreDoc[] scoreDocs = docs.scoreDocs;printDocs(scoreDocs);results = getPoiDatasFromDoc(scoreDocs);}return results;} 

搜索某个范围内并根据地址关键字信息来检索POI:

public List<PoiData>searchPoByRangeAndAddress(doublelng, doublelat, double range, String address){List<PoiData> results= newArrayList<>();SpatialArgsargs = newSpatialArgs(SpatialOperation.Intersects,ctx.makeCircle(lng, lat, DistanceUtils.dist2Degrees(range, DistanceUtils.EARTH_MEAN_RADIUS_KM)));Query geoQuery = strategy.makeQuery(args);QueryBuilder builder = newQueryBuilder(analyzer);Query addQuery = builder.createPhraseQuery(AddressFieldName, address);BooleanQuery.BuilderboolBuilder = newBooleanQuery.Builder();boolBuilder.add(addQuery, Occur.SHOULD);boolBuilder.add(geoQuery,Occur.MUST);Query query = boolBuilder.build();TopDocs docs = null;try {docs = indexSearcher.search(query, 10);} catch (IOException e) {log.error(e.toString());}if(docs!=null){ScoreDoc[] scoreDocs = docs.scoreDocs;printDocs(scoreDocs);results = getPoiDatasFromDoc(scoreDocs);}return results;}

4、关于分词

POI的地址属性和描述属性都需要做分词才能更好的进行检索和搜索。

简单对比了几种分词效果:

原文:

这是一个lucene中文分词的例子,你可以直接运行它!Chinese Analyer can analysis english text too.中国农业银行(农行)和建设银行(建行),江苏南京江宁上元大街12号。东南大学是一所985高校。

分词结果:

smartcn SmartChineseAnalyzer这\是\一个\lucen\中文\分\词\的\例子\你\可以\直接\运行\它\chines\analy\can\analysi\english\text\too\中国\农业\银行\农行\和\建设\银行\建行\江苏\南京\江\宁\上\元\大街\12\号\东南\大学\是\一\所\985\高校\MMSegAnalyzer ComplexAnalyzer这是\一个\lucene\中文\分词\的\例子\你\可以\直接\运行\它\chinese\analyer\can\analysis\english\text\too\中国农业\银行\农行\和\建设银行\建\行\江苏南京\江\宁\上\元\大街\12\号\东南大学\是一\所\985\高校\IKAnalyzer这是\一个\lucene\中文\分词\的\例子\你\可以\直接\运行\它\chinese\analyer\can\analysis\english\text\too.\中国农业银行\农行\和\建设银行\建行\江苏\南京\江宁\上元\大街\12号\东南大学\是\一所\985\高校\

分词效果对比:

1)Smartcn不能正确的分出有些英文单词,有些中文单词也被分成单个字。

2)MMSegAnalyzer能正确的分出英文和中文,但对于类似“江宁”这样的地名和“建行”等信息不是很准确。MMSegAnalyzer支持自定义词库,词库可以大大提高分词的准确性。

3)IKAnalyzer能正确的分出英文和中文,中文分词比较不错,但也有些小问题,比如单词too和最后的点号分在了一起。IKAnalyzer也支持自定义词库,但是要扩展一些源码。

总结:使用Lucene强大的数据索引和检索能力可以为一些带有经纬度和需要分词检索的数据提供搜索功能。

代码托管在GitHub上:https://github.com/luxiaoxun/Code4Java

使用Lucene索引和检索POI数据相关推荐

  1. lucene索引word/pdf/html/txt文件及检索(搜索引擎)

    2009-07-02 15:31 因为lucene索引的时候是将String型的信息建立索引的,所以这里必须是将word/pdf/html等文件的内容转化问字符型. lucene的jar包自己去下载. ...

  2. lucene: 索引建立完后无法查看索引文件中的数据

    索引建立时 1.对原有索引文件进行建立,是可以访问索引文件中的数据的 2.建立新索引文件,必须等建立完毕后,才可以访问,新建立的文件如果没有建立完是不可以被访问的 如果想建立200w的数据的索引又想立 ...

  3. 影响Lucene索引速度原因以及提高索引速度技巧

    在网上看了一篇外文文章,里面介绍了提高Lucene索引速度的技巧,分享给大家. 先来看下影响索引的主要因素: MaxMergeDocs 该参数决定写入内存索引文档个数,到达该数目后就把该内存索引写入硬 ...

  4. php lucene索引,用PHP调用Lucene包来实现全文检索_PHP教程

    由于工作需要,需要使用PHP实现对网站内大量数量进行全文检索, 而且目前最流行的全文检索的搜索引擎库就是Lucene了, 它是Apache Jakarta的一个子项目,并且提供了简单实用的API, 用 ...

  5. mysql lucene 索引区别_MySQL和Lucene索引对比分析

    MySQL和Lucene都可以对数据构建索引并通过索引查询数据,一个是关系型数据库,一个是构建搜索引擎(Solr.ElasticSearch)的核心类库.两者的索引(index)有什么区别呢?以前写过 ...

  6. 周边检索POI技术方案设计

    点击上方关注 "终端研发部" 设为"星标",和你一起掌握更多数据库知识 因公众号更改推送规则,请点"在看"并加"星标"第 ...

  7. Python采集全国各地百度地图上店铺POI数据(母婴、美食等)

    Python采集全国各地百度地图上店铺POI数据 1. 注册百度地图开放平台账号 先注册百度地图开发平台账号,创建应用,获得AK 2. 通过Python批量检索不同城市店铺POI数据 2.1 大致思路 ...

  8. Elasticsearch学习--索引快速检索

    如何快速检索? Elasticsearch是通过Lucene的倒排索引技术实现比关系型数据库更快的过滤.特别是它对多条件的过滤支持非常好,比如年龄在18和30之间,性别为女性这样的组合查询.倒排索引很 ...

  9. 数据检索---基于Lucene索引

    有了大量的数据之后,想要找到特定的数据,模糊查询,也是一个巨大的挑战.这里有来一起回顾下Lucene索引.(以下很多来自百度百科,算作是科普吧) 说起Lucene,它是apache软件基金会jakar ...

最新文章

  1. puppet report import
  2. decimal(C# 参考)
  3. php for 写入多行数据库,php – MYSQL意外插入多行
  4. hadoop学习;datajoin;chain签名;combine()
  5. Nginx端口占用问题
  6. WeChall_Training: Register Globals
  7. [.NET跨平台]Jexus独立版本的便利与过程中的一些坑
  8. 天津大学计算机预推免机试_2019预推免汇总 | 9.18New!
  9. Windows 7 设置devenv.exe启动版本
  10. orm2 中文文档 3.3 模型钩子
  11. 不懂算法的程序员不是好工程师!
  12. html在线查看ppt,网页设计———HTML基本标记.ppt
  13. 判断坐标位置C语言,〓求助〓谁能帮忙编个程序:输入N个点坐标,判断能否构成多边形....
  14. html网页头部图片,网页头图设计技巧
  15. linux pgm格式图片,pgm文件扩展名,pgm文件怎么打开?
  16. 记录Java Web The server encountered an internal error that prevented it from fulfilling报错及解决
  17. opencv +python采集识别填涂卡(答题卡)数据
  18. OLT基本操作及ONU的认证
  19. 安装cuda11.1
  20. Google Play Console beta 版全新发布

热门文章

  1. java 一一对应的替换_SpringMVC的Controller是如何将参数和前端传来的数据一一对应的...
  2. 控制台无法连接_指挥中心控制台整体解决方案,告别“信息孤岛”
  3. Postman测试接口传入List类型的参数以及数组类型参数
  4. LNMP一键安装包 PHP自动升级脚本
  5. Linux上的.NET框架Mono 2.0发布
  6. 七款最常用的PHP本地服务器
  7. 9:23 2009-7-23
  8. PHP7革新与性能优化
  9. jQuery - Bootstrap 3 and Youtube in Modal
  10. UIWebView / NSURL / NSBoundle 相关应用 (实例,加载完成前的背景, 默认safari打开链接地址等)...