1. 程式人生 > >使用Lucene索引和檢索POI資料

使用Lucene索引和檢索POI資料

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 Name
    private 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 search private 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 SpatialContextFactory ctx = SpatialContext.GEO; int maxLevels = 11; // results in sub-meter precision for geohash // This can also be constructed from SpatialPrefixTreeFactory SpatialPrefixTree 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 dist
            distSort = 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強大的資料索引和檢索能力可以為一些帶有經緯度和需要分詞檢索的資料提供搜尋功能。

相關推薦

使用Lucene索引檢索POI資料

1、簡介 關於空間資料搜尋,以前寫過《使用Solr進行空間搜尋》這篇文章,是基於Solr的GIS資料的索引和檢索。 Solr和ElasticSearch這兩者都是基於Lucene實現的,兩者都可以進行空間搜尋(Spatial Search),在有些場景,我們需要把Lucene嵌入到已有的系統提供資料索引和

Lucene&&Solr】Lucene索引搜尋流程

  全文檢索:先建立索引,在對索引進行搜尋的過程   使用Lucene實現全文檢索,其流程包含兩個過程,索引建立過程和索引查詢過程        建立索引:     1.獲取文件     2.建立文件物件     3.分析文件     4.建立索引   把建立好的索引

Lucene筆記23-Lucene的使用-簡單複習索引檢索分詞

一、索引 索引過程中的核心類:IndexWriter、Directory、Analyzer、Document、Field。 IndexWriter用來寫索引。 Directory用來定義索引的目標位置是硬碟上還是記憶體中。 Analyzer用來分詞,常用的分詞器有:SimpleA

Lucene建立索引索引的基本檢索

Author: 百知教育 gaozhy 注:演示程式碼所使用jar包版本為 lucene-xxx-5.2.0.jar lucene索引操作 建立索引程式碼 try {

Lucene實現索引查詢

ont termquery 文件夾 移植 指定 安裝過程 buffer upd 遇到 0引言   隨著萬維網的發展和大數據時代的到來,每天都有大量的數字化信息在生產、存儲、傳遞和轉化,如何從大量的信息中以一定的方式找到滿足自己需求的信息,使之有序化並加以利用成為一大難題。全

*lucene索引_的刪除更新

【刪除】   【恢復刪除】   【強制刪除】   【優化和合並】   【更新索引】   附:   程式碼: IndexUtil.java: 1 package cn.hk.index;

MongoDB find命令匹配資料,匹配內容檢索條件不一致

  最後一位隨便輸一個數,就匹配這一條。 問題描述:今天計劃將tweet_id設定為集合的唯一索引,出現一條資料報錯。報錯對應的tweet_id為“255837612277911555”,在pymongo查詢這條資料發現出現的結果和查詢內容最後一位不一致。於是修改最後一位

Atitit 現代資訊檢索 Atitit 重要章節 息檢索建模 檢索評價 第8章 文字分類 Line 210: 第9章 索引搜尋 第11章 Web檢索 第13章 結構化文字檢索 目錄 L

Atitit 現代資訊檢索   Atitit 重要章節 息檢索建模 檢索評價  第8章 文字分類 Line 210: 第9章 索引和搜尋 第11章 Web檢索 第13章 結構化文字檢索   目錄   Line 1

poi資料抓取下載

  網際網路或者企業獲取:直接從一些專業類服務網站上抓取或者購買(例如大眾點評,攜程),或者直接從大家在其公開的地圖服務上的標註中進行篩選和獲取。這就是google,百度,高德自己免費向社會開放其地圖服務所能夠獲得的利益。尤其對於開放API免費企業客戶的使用,這種獲取是很有價值的。

javaEE Lucene,全文檢索,站內搜尋,入門程式。索引庫的新增

注意:搜尋使用的分析器(分詞器)要和建立索引時使用的分析器一致。 Field類(域物件): Test.java(入門程式 測試類): package com.xxx.lucene; import static org.junit.Assert.*; im

【手把手教你全文檢索Lucene索引的【增、刪、改、查】

前言   搞檢索的,應該多少都會了解Lucene一些,它開源而且簡單上手,官方API足夠編寫些小DEMO。並且根據倒排索引,實現快速檢索。本文就簡單的實現增量新增索引,刪除索引,通過關鍵字查詢,以及更新索引等操作。   目前博豬使用的不爽的地方就是,讀取檔案內容進行全文檢索時,需要自己編寫讀取過程(這

Java讀取解析Excel資料:基於Apache POI(二)

Java讀取和解析Excel資料:基於Apache POI(二) 假設附錄1文章中的test.xls是對員工的考勤記錄表。需要根據這張excel表統計員工的加班時間,那麼需要重點關注第五列的下班時候的打卡記錄時間。我寫一個Java程式,根據附錄1中那樣的excel表中的下班打卡時間統計員工在

使用poi只能判斷20032007進行資料讀取

import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.text.SimpleDateFormat; import java.ut

MySQLLucene索引對比分析

MySQL和Lucene都可以對資料構建索引並通過索引查詢資料,一個是關係型資料庫,一個是構建搜尋引擎(Solr、ElasticSearch)的核心類庫。兩者的索引(index)有什麼區別呢?以前寫過一篇《Solr與MySQL查詢效能對比》,只是簡單的對比了下查詢效能,對於內部原理卻沒有解釋,本文簡單分析下兩

T-Sql(八)欄位索引資料加密

1 open symmetric key PwdKey --開啟對稱金鑰 2 decryption by certificate AdminPwdCert 3 4 insert into dbo.Wrokers(WrokerID,Name,Sex,M

Solr索引基本資料操作

1. 介紹 Solr索引可以接收不同的資料來源,包括XML檔案,逗號分隔值(CSV)檔案,從資料庫提取的資料,常見的檔案格式如MS Word或PDF. 有三種常用的方法載入資料到Solr索引: * 使用Apache Tika的Solr Cell框架,處理二進位制或結構化檔案

Lucene的記憶體索引磁碟索引

package HelloLucene; import java.io.File; import java.io.IOException; import java.text.ParseException; import org.apache.lucene.analysis.standard.StandardA

Lucene 6.2.1入門教程(一) 建立索引基本搜尋索引

  簡單說兩句,Lucene現在高版本的教程很少,網上基本是4.0以下的,目前最新版是6.2.1,所以我試著來寫寫這個版本的教程。   至於那些概念什麼的,我就不多說了,大家可以參考以前的舊教程來了解Lucene的體系結構和基本原理。大致說一下Lucene就是通過建立索引這

關於Lucene以及索引搜尋的流程

Lucene的普及和成功的背後是因為它的簡單。   因此,你不需要深入理解Lucene的資訊索引和檢索工作方面的知識就可以開始使用。   Lucene提供了簡單但是強大的核心API去實現全文索引和檢索,你只需要掌握少數的類就能將Lucene整合到應用中。

SpringCloud學習筆記024---SpringBoot整合Lucene實現全文檢索_分詞_索引_更新_刪除文件_詞條搜尋_多條件查詢

先看程式碼實現,下面有lucene介紹: 測試用例 Github 程式碼 程式碼我已放到 Github ,匯入spring-boot-lucene-demo 專案 新增依賴 <!--對分詞索引查詢解析--> <dependency>