跳到主要內容

Lucene(一):Lucene4.3.1、Tika、IKAnalyzer中文分詞

Lucene是一個很強大的搜尋引擎(Lucene wiki),重點是Open Source,還有很多子專案很好用喔,這次來介紹一下Tika這個子專案,它是一個文件擷取內容及文件metadata的函式庫,支援的檔案格式可以參考一下這裡,加上Lucene Core及中文IKAnalyzer就可以組合文件的中文全文檢索喔。

先下載
Lucene 4.3.1
Tika
IKAnalyzer



Lucene 4.3.1

  • 下載後解壓縮,分別複製以下的jar
    • lucene-queryparser-4.3.1.jar
    • lucene-queries-4.3.1.jar
    • lucene-core-4.3.1.jar
    • lucene-analyzers-common-4.3.1.jar
Tika 1.4
  • 請下載Source檔案,另外請在下載maven,因為Tika建議重新編譯一份出來,編譯方式參這裡,分別複製以下的jar
    • tika-xmp-1.4.jar
    • tika-server-1.4.jar
    • tika-parsers-1.4.jar
    • tika-core-1.4.jar
    • tika-bundle-1.4.jar
    • tika-app-1.4.jar
    • original-tika-app-1.4.jar
IKAnalyzer
  • 下載後解壓縮,複製以下的jar
    • IKAnalyzer2012FF_ul.jar

TvDocumentVo.java:儲存 Tika解析文件內容vo。
public class TvDocumentVo { 
   private Metadata metadata;
    private String content;
    public Metadata getMetadata() {
        return metadata;
    }
    public void setMetadata(Metadata metadata) {
        this.metadata = metadata;
    }
    public String getContent() {
        return content;
    }
    public void setContent(String content) {
        this.content = content;
    }
}
TvDocmentExtract.jar:使用Tika將解析出來的文件內容轉成List。P.S這裡需要調整當檔案數量及大小過大時。

public class TvDocmentExtract {
    public List parseAllFilesInDirectory(File directory) throws IOException, SAXException, TikaException {
        List result = new ArrayList();
        for (File file : directory.listFiles()) {
            if (file.isDirectory()) {
                parseAllFilesInDirectory(file);
            } else {
                Parser parser = new AutoDetectParser();
                Metadata metadata = new Metadata();
                ParseContext parseContext = new ParseContext();
                ContentHandler handler = new BodyContentHandler(100*100*1024);//若是檔案過大可以放大這裡的參數
                parser.parse(new FileInputStream(file), handler, metadata, parseContext);
                TvDocumentVo vo = new TvDocumentVo();
                metadata.set("filename",file.getAbsolutePath());//增加檔案絕對路徑屬性
                vo.setMetadata(metadata);
                vo.setContent(handler.toString());
                result.add(vo);
            }
        }
        return result;
    }
}
TvIndexManagement.java:建立索引

public class TvIndexManagement {
    
    public static void main(String[] arg) throws ParseException, IOException{
        TvIndexManagement tim = new TvIndexManagement();
        tim.createIndex("D:\\index", "D:\\document");//參數一為建立索引目錄,參數二為要進行文件擷取目錄
       
    }
    
    public void createIndex(String indexDir,String filesPath){
        this.ikanalyzerIndex(indexDir, filesPath);
    }
    public RAMDirectory readfsIndexToRam(String indexDir) throws IOException{
        Directory fsDir = FSDirectory.open(new File(indexDir));
         IOContext ioContext = new IOContext(Context.DEFAULT);
        return new RAMDirectory(fsDir, ioContext);
    }
    
    private void ikanalyzerIndex(String indexDir,String filesPath){
        Analyzer analyzer = new IKAnalyzer(true);//可參IKAnalyzer說明
        try {
            Directory index = FSDirectory.open(new File(indexDir));
            IOContext ioContext = new IOContext(Context.DEFAULT);
           // Directory index = new RAMDirectory(fsDir, ioContext);
            IndexWriterConfig config = new IndexWriterConfig(Version.LUCENE_43,analyzer);
            IndexWriter w = new IndexWriter(index, config);
            config.setMaxBufferedDocs(1000);//純粹測試效能調教
            config.setRAMBufferSizeMB(40);//純粹測試效能調教
           TvDocmentExtract tde = new TvDocmentExtract();
            try {
                List lists=tde.parseAllFilesInDirectory(new File(filesPath));
                for(TvDocumentVo vo :lists){
                    this.addDocument(w,vo.getMetadata().get("filename"),vo.getContent());
                }
                w.close();
            } catch (SAXException ex) {
                Logger.getLogger(TvIndexManagement.class.getName()).log(Level.SEVERE, null, ex);
            } catch (TikaException ex) {
                Logger.getLogger(TvIndexManagement.class.getName()).log(Level.SEVERE, null, ex);
            }            
        } catch (IOException ex) {
            Logger.getLogger(TvIndexManagement.class.getName()).log(Level.SEVERE, null, ex);
        }      
    }
    private void addDocument(IndexWriter writer,String filename,String content) throws IOException{
        Document doc = new Document();
        //System.out.println(filename);
        doc.add(new TextField("filename", filename, Field.Store.YES));
         doc.add(new TextField("content", content, Field.Store.YES));
         writer.addDocument(doc);
    }    
}
全文檢索範例程式碼

  String querystr = "合法";
        Directory index = FSDirectory.open(new File("D:\\index"));
        Analyzer analyzer = new IKAnalyzer(true);
        QueryParser qp = new QueryParser(Version.LUCENE_43, "content", analyzer);
        qp.setDefaultOperator(QueryParser.Operator.AND);
        Query q = qp.parse(querystr);
        int hitsPerPage = 10;
        IndexReader reader = DirectoryReader.open(index);
        IndexSearcher searcher = new IndexSearcher(reader);
        TopScoreDocCollector collector = TopScoreDocCollector.create(hitsPerPage, true);
        searcher.search(q, collector);
        ScoreDoc[] hits = collector.topDocs().scoreDocs;
        collector.getTotalHits();
           System.out.println("Found " + hits.length + " hits ==>" + collector.getTotalHits());
        for (int i = 0; i < hits.length; ++i) {
            int docId = hits[i].doc;
            Document d = searcher.doc(docId);
            System.out.println((i + 1) + ". " + d.get("filename"));
        }
        reader.close();
目前正在試試看,不使用Hadoop方式加快建立索引時間,基本上會用Queue方式進行,下次再介紹囉。

留言

這個網誌中的熱門文章

Spring boot v1.5 (六) spring data jpa 基本操作

最近天氣好熱,做甚麼事都覺得很懶,想要寫個spring data jpa也是懶懶的,不過這部分卻也是滿重要的一部分,前一篇介紹 JDBCTemplate ,已經覺得跟以前寫SQL方式有所差異了,JPA帶來的是物件導向的設計面思考,說到JPA不得不提提 ORM ,Object-relational mapping主要想法為簡化及物件導向的設計,讓RDB更貼近Object,在設計上可以更加便利,甚至透過一些設計可以讓Table具有物件導向的特性如繼承等等,以往要使用ORM的框架,都會先以 Hibernate 進行,不過近來慢慢地轉向JPA,主要還是在減少程式碼、增加彈性等等,大體的功能沒有差異很大,所以從Hibernate轉到JPA問題不大,JPA要介紹的東西還滿多的,所以我這裡會再分成三個章節來介紹。 SPRING DATA JPA基本操作 JPQL & Named SQL & Native SQL Cache & DB Design Pattern SPRING DATA JPA更加簡化的程式撰寫,只需要一個 Interface內寫一些查詢 method就可以操作JPA,因為利用 method 組合查詢條件,確實很方便也很容易理解,若是都沒有辦法符合需求當然也可以自己實作一個來用當然沒有問題。 學習目的 :SPRING DATA JPA基本操作。 學習時數 :3.5hr 教學影片: pom.xml 說明 spring-boot-starter-web:配置 Web Project所需的函式庫。 spring-boot-starter-test:配置 unit or mock test 所需的函式庫。 spring-boot-starter-actuator:配置監控spring boot所需的函式庫,後續spring cloud會使用到,所以一開就導入。 spring-boot-starter-jdbc:配置使用jdbc所需的函式庫。 postgresql:配置postgresql連接Driver所需的函式庫。 jasypt-spring-boot-starter:加解密所需的函式庫。 spring-boot-starter-data-jpa:配置Spring data jpa所需的函式庫。 ...

Pentaho kettle取指定目錄下的所有檔案

最近開始玩 BI的東西,而之前專案有使用到Pentaho Open Source這個好物.... Pentaho裡面很多東西,跟 Jasperreport一樣東西很多,我最先接觸到的是kettle ETL的工具,玩了一陣子,開始有空就把它寫下來,以免忘記。 Scan一個目錄下所有檔案,然後塞進去資料庫 1.先拉兩個 Input,一個Get File Names,一個是CSV file input,再拉一個 output 中的 Table output,然後把他連起來。 2.點開 Get File Names,File or directory設定你的指定目錄,Regular Expression則是輸入.*\.*$則是所有檔案,若是CSV則可.*\.torrent$這可以了,可以按一下 Preview rows看看是否正確。

IReport中的條碼類別BcImage

最近有一個繳費單的專案,需要列印條碼,因為IReport本身就有提供列印條碼的功能,所以就用IReport設計繳費單然後再用Jasperreport API寫批次程式去產生PDF,資料量大概3000多筆(頁)拆成幾個檔案,可是就發生了一個問題居然發生部分繳費單的條碼有問題,看了一下程式研判是Race Condition因為程式採用的是多執行緒,若是依序個別產生是不會有問題,但是同時執行的時候就會亂掉,而且都錯在條碼,令我覺得很奇怪,後來我查了一下發現it.businesslogic.ireport.barcode.BcImage 類別我猜是這裡的問題,就先加上synchronized重新編譯在去執行,嘿嘿條碼就對了,做了一下壓測也正常,程式碼如下,不過這樣事不是對了可能還要仔細查查看。 public class BcImage { private static net.sourceforge.barbecue.Barcode bc = null; public synchronized static net.sourceforge.barbecue.Barcode getBarcode() { return bc; } public synchronized static BufferedImage getBarcodeImage(int type, Object aText, boolean showText, boolean checkSum) { return getBarcodeImage(type, aText, showText, checkSum,"",0,0); } public synchronized static BufferedImage getBarcodeImage(int type, Object aText, boolean showText, boolean checkSum, String applicationIdentifier, int width, int height) { // 2of7, 3of9, Bookland, Codabar, Code128,...