教育行業(yè)A股IPO第一股(股票代碼 003032)

全國(guó)咨詢(xún)/投訴熱線(xiàn):400-618-4000

Java培訓(xùn)實(shí)戰(zhàn)教程之lucene初探

更新時(shí)間:2015年12月29日13時(shí)26分 來(lái)源:傳智播客Java培訓(xùn)學(xué)院 瀏覽次數(shù):

全文檢索場(chǎng)景
當(dāng)你在使用百度、Google搜索信息時(shí),當(dāng)你在淘寶、京東搜索商品時(shí)你知道這些都是使用的什么技術(shù)可以很快搜索你想要的東東嗎?正是全文檢索技術(shù)。
全文檢索概念
全文檢索是將整本書(shū)、整篇文章中的任意內(nèi)容信息查找出來(lái)的檢索。它可以根據(jù)需要獲得全文中有關(guān)章、節(jié)、段、句、詞等信息,計(jì)算機(jī)程序通過(guò)掃描文章中的每一個(gè)詞,對(duì)每一個(gè)詞建立一個(gè)索引,指明該詞在文章中出現(xiàn)的次數(shù)和位置,當(dāng)用戶(hù)查詢(xún)時(shí)根據(jù)建立的索引查找,類(lèi)似于通過(guò)字典的檢索字表查字的過(guò)程。
經(jīng)過(guò)幾年的發(fā)展,全文檢索從最初的字符串匹配程序已經(jīng)演進(jìn)到能對(duì)超大文本、語(yǔ)音、圖像、活動(dòng)影像等非結(jié)構(gòu)化數(shù)據(jù)進(jìn)行綜合管理的大型軟件。
什么是Lucene
Lucene是apache下的一個(gè)開(kāi)放源代碼的全文檢索引擎工具包。提供了完整的搜索引擎和索引引擎。Lucene的目的是為軟件開(kāi)發(fā)人員提供一個(gè)簡(jiǎn)單易用的工具包,以方便的在目標(biāo)系統(tǒng)中實(shí)現(xiàn)全文檢索的功能。
案例描述
我們以一個(gè)案例來(lái)研究全文檢索過(guò)程:實(shí)現(xiàn)一個(gè)資源管理器的搜索功能,通過(guò)關(guān)鍵字搜索文件,凡是文件名或文件內(nèi)容包括關(guān)鍵字的文件都需要找出來(lái)。
開(kāi)發(fā)環(huán)境
從Lucene官方網(wǎng)站(http://lucene.apache.org/)下載Lucene4.3.10,并解壓。
 
Lucene4.3.10要求Jdk使用1.7以上,本教程使用1.7.0_72版本。
開(kāi)發(fā)工具:eclipse indigo
 
Lucene包:
lucene-core-4.10.3.jar---Lucene核心包
lucene-analyzers-common-4.10.3.jar----Lucene分析包
lucene-queryparser-4.10.3.jar ---Lucene查詢(xún)包
 
其它:
commons-io-2.4.jar ---用于讀取磁盤(pán)文件內(nèi)容
junit-4.9.jar---用于單元測(cè)試
 
 
Lucene全文檢索過(guò)程
全文檢索包括索引和搜索兩個(gè)過(guò)程,先對(duì)要搜索的信息創(chuàng)建索引,再?gòu)乃饕兴阉餍畔ⅰ?br /> 如下圖:
1、黃色表示索引過(guò)程,對(duì)要搜索的原始內(nèi)容進(jìn)行索引構(gòu)建一個(gè)索引庫(kù),索引過(guò)程包括:
確定原始內(nèi)容即要搜索的內(nèi)容--》采集文檔--》創(chuàng)建文檔--》分析文檔--》索引文檔
        
2、藍(lán)色表示搜索過(guò)程,從索引庫(kù)中搜索內(nèi)容,搜索過(guò)程包括:
用戶(hù)通過(guò)搜索界面--》創(chuàng)建查詢(xún)--》執(zhí)行搜索,從索引庫(kù)搜索--》渲染搜索結(jié)果
 
 
第一步:確定原始內(nèi)容
原始內(nèi)容是指要索引和搜索的內(nèi)容。原始內(nèi)容包括互聯(lián)網(wǎng)上的網(wǎng)頁(yè)、數(shù)據(jù)庫(kù)中的數(shù)據(jù)、磁盤(pán)上的文件等。
本案例中的原始內(nèi)容就是磁盤(pán)上的文件(本教程只搜索.txt文件),如下圖:
第二步:獲取原始內(nèi)容
從互聯(lián)網(wǎng)上、數(shù)據(jù)庫(kù)、文件系統(tǒng)中等獲取需要搜索的原始信息,這個(gè)過(guò)程就是信息采集,信息采集的目的是為了對(duì)原始內(nèi)容進(jìn)行索引。
         Lucene本身不提供信息采集的功能,這里我們通過(guò)Java流程讀取磁盤(pán)文件的內(nèi)容。
 
        
第三步:創(chuàng)建文檔
獲取原始內(nèi)容的目的是為了索引,在索引前需要將原始內(nèi)容創(chuàng)建成文檔(Document),文檔中包括一個(gè)一個(gè)的域(Field),域中存儲(chǔ)內(nèi)容。
這里我們可以將磁盤(pán)上的一個(gè)文件當(dāng)成一個(gè)document,Document中包括一些Field(file_name文件名稱(chēng)、file_path文件路徑、file_size文件大小、file_content文件內(nèi)容),如下圖:
 
 
注意:每個(gè)Document可以有多個(gè)Field,不同的Document可以有不同的Field,同一個(gè)Document可以有相同的Field(域名和域值都相同)
 
下邊代碼實(shí)現(xiàn)了從磁盤(pán)讀取文件并創(chuàng)建文檔的過(guò)程:
 
// 從文件創(chuàng)建Document
   public static List<Document> file2Document(String folderPath)
           throws IOException {
 
       List<Document> list = new ArrayList<Document>();
 
       File folder = new File(folderPath);
       if (!folder.isDirectory()) {
           return null;
       }
       // 獲取目錄 中的所有文件
       File[] files = folder.listFiles();
       for (File file : files) {
           //文件名稱(chēng)
           String fileName = file.getName();
           System.out.println(fileName);
           if (fileName.lastIndexOf(".txt") > 0) {
 
              // 文件內(nèi)容
              String fileContent = FileUtils.readFileToString(file);
              //文件路徑
              String filePath = file.getAbsolutePath();
              //文件大小
              long fileSize = FileUtils.sizeOf(file);
             
              //創(chuàng)建文檔
              Document doc = new Document();
 
              //創(chuàng)建各各Field域
              //文件名
              Field field_fileName = new StringField("fileName", fileName, Store.YES);
              //文件內(nèi)容
              Field field_fileContent = new TextField("fileContent", fileContent, Store.NO);
                    
              //文件大小
              Field field_fileSize = new LongField("fileSize", fileSize, Store.YES);
              //文件路徑
              Field field_filePath = new StoredField("filePath", filePath, Store.YES);
 
             
              //將各各Field添加到文檔中
              doc.add(field_fileName);
              doc.add(field_fileContent);
              doc.add(field_fileSize);
              doc.add(field_filePath);
              list.add(doc);
             
           }
       }
 
       return list;
 
    }
 
 
第四步:分析文檔
 
將原始內(nèi)容創(chuàng)建為包含域(Field)的文檔(document),需要再對(duì)域中的內(nèi)容進(jìn)行分析,分析的過(guò)程是經(jīng)過(guò)對(duì)原始文檔提取單詞、將字母轉(zhuǎn)為小寫(xiě)、去除標(biāo)點(diǎn)符號(hào)、去除常用詞等過(guò)程生成最終的語(yǔ)匯單元,可以將語(yǔ)匯單元理解為一個(gè)一個(gè)的單詞。
比如下邊的文檔經(jīng)過(guò)分析如下:
原文檔內(nèi)容:
Lucene is a Java full-text search engine.  Lucene is not a complete
application, but rather a code library and API that can easily be used
to add search capabilities to applications.
 
分析后得到的語(yǔ)匯單元:
lucene、java、full、search、engine。。。。
 
第五步:創(chuàng)建索引
 
對(duì)所有文檔分析得出的語(yǔ)匯單元進(jìn)行索引,索引的目的是為了搜索,最終要實(shí)現(xiàn)只搜索被索引的語(yǔ)匯單元從而找到Document(文檔)。
注意:創(chuàng)建索引是對(duì)語(yǔ)匯單元索引,通過(guò)詞語(yǔ)找文檔,這種索引的結(jié)構(gòu)叫倒排索引結(jié)構(gòu)。
傳統(tǒng)方法是根據(jù)文件找到該文件的內(nèi)容,在文件內(nèi)容中匹配搜索關(guān)鍵字,這種方法是順序掃描方法,數(shù)據(jù)量大、搜索慢。
倒排索引結(jié)構(gòu)是根據(jù)內(nèi)容(詞語(yǔ))找文檔,如下圖:
 
根據(jù)左邊的索引詞典可以找到詞對(duì)應(yīng)的文檔。“springmvc.txt”這個(gè)詞在Document1(springmvc.txt) ,“web”和“spring”在Document1、Document2中都存在,詞是通過(guò)Field和Document文檔聯(lián)系起來(lái)的。
 
倒排索引結(jié)構(gòu)也叫反向索引結(jié)構(gòu),包括索引和文檔兩部分,索引即詞匯表,它的規(guī)模較小,而文檔集合較大。
 
使用分析器分析并創(chuàng)建索引過(guò)程代碼如下:
 
 
public class IndexTest {
 
    // 索引源,即源數(shù)據(jù)目錄
    private static String searchSource = "F:\\develop\\lucene\\searchsource";
 
    // 索引目標(biāo)地址
    private static String indexFolder = "F:\\develop\\lucene\\indexdata";
 
    @Test
    public void testCreateIndex() {
 
       try {
 
          //從目錄中讀取文件內(nèi)容并創(chuàng)建Document文檔
           List<Document> docs = IndexUtils.file2Document(searchSource);
           //創(chuàng)建分析器,standardAnalyzer標(biāo)準(zhǔn)分析器
           Analyzer standardAnalyzer = new IKAnalyzer();
           // 指定索引存儲(chǔ)目錄
           Directory directory = FSDirectory.open(new File(indexFolder));
           //創(chuàng)建索引操作配置對(duì)象
           IndexWriterConfig indexWriterConfig = new IndexWriterConfig(Version.LUCENE_4_10_3,
                  standardAnalyzer);
                    
           // 定義索引操作對(duì)象indexWriter
           IndexWriter indexWriter = new IndexWriter(directory,indexWriterConfig);
          
           // 遍歷目錄 下的文件生成的文檔,調(diào)用indexWriter方法創(chuàng)建索引
           for (Document document : docs) {
              indexWriter.addDocument(document);
           }
           // 索引操作流關(guān)閉
           indexWriter.close();
 
       } catch (IOException e) {
           e.printStackTrace();
       }
 
    }
}
 
第六步:搜索文件
根據(jù)文件名稱(chēng)搜索文件,需要經(jīng)過(guò)以下步驟:
1)指定索引目錄地址,搜索就是從索引中搜索匹配的詞語(yǔ)
// 指定索引目錄地址,
    private static String indexFolder = "F:\\develop\\lucene\\indexdata";
 
2)創(chuàng)建Query構(gòu)建查詢(xún)語(yǔ)法 (可以理解為和關(guān)系數(shù)據(jù)庫(kù)的Sql作用一樣)
// 創(chuàng)建查詢(xún)對(duì)象,根據(jù)文件名稱(chēng)域搜索匹配文件名稱(chēng)的文檔
       Query query = new TermQuery(new Term("fileName", "springmvc_test.txt"));
 
3)創(chuàng)建IndexReader讀取索引文件
// 指定索引目錄
       Directory directory = FSDirectory.open(new File(indexFolder));
 
       // 定義IndexReader
       IndexReader reader = DirectoryReader.open(directory);
 
 
4)創(chuàng)建IndexSearcher執(zhí)行搜索
// 創(chuàng)建indexSearcher
       IndexSearcher indexSearcher = new IndexSearcher(reader);
       // 執(zhí)行搜索
       TopDocs topDocs = indexSearcher.search(query, 100);
 
 
5)通過(guò)TopDocs獲取搜索結(jié)果
// 提取搜索結(jié)果
       ScoreDoc[] scoreDocs = topDocs.scoreDocs;
 
6)遍歷結(jié)果,從Document中獲取Field內(nèi)容
for (ScoreDoc scoreDoc : scoreDocs) {
           // 文檔id
           int docID = scoreDoc.doc;
           // 得到文檔
           Document doc = indexSearcher.doc(docID);
           // 輸出 文件內(nèi)容
           System.out.println("------------------------------");
           System.out.println("文件名稱(chēng) =" + doc.get("fileName"));
           System.out.println("文件大小 =" + doc.get("fileSize"));
           System.out.println("文件內(nèi)容 =" + doc.get("fileContent"));
       }
 
完整代碼如下:
public class SearchTest {
    // 指定索引目錄地址,
    private static String indexFolder = "F:\\develop\\lucene\\indexdata";
 
    //查詢(xún)方法
    @Test
    public void testTermQuery() throws IOException {
 
       // 創(chuàng)建查詢(xún)對(duì)象,根據(jù)文件名稱(chēng)域搜索匹配文件名稱(chēng)的文檔
       Query query = new TermQuery(new Term("fileName", "springmvc_test.txt"));
 
       // 指定索引目錄
       Directory directory = FSDirectory.open(new File(indexFolder));
 
       // 定義IndexReader
       IndexReader reader = DirectoryReader.open(directory);
       // 創(chuàng)建indexSearcher
       IndexSearcher indexSearcher = new IndexSearcher(reader);
       // 執(zhí)行搜索
       TopDocs topDocs = indexSearcher.search(query, 100);
       // 提取搜索結(jié)果
       ScoreDoc[] scoreDocs = topDocs.scoreDocs;
 
       System.out.println("共搜索到總記錄數(shù):" + topDocs.totalHits);
 
       for (ScoreDoc scoreDoc : scoreDocs) {
           // 文檔id
           int docID = scoreDoc.doc;
           // 得到文檔
           Document doc = indexSearcher.doc(docID);
           // 輸出 文件內(nèi)容
           System.out.println("------------------------------");
           System.out.println("文件名稱(chēng) =" + doc.get("fileName"));
           System.out.println("文件大小 =" + doc.get("fileSize"));
           System.out.println("文件內(nèi)容 =" + doc.get("fileContent"));
       }
 
    }



本文版權(quán)歸傳智播客Java培訓(xùn)學(xué)院所有,歡迎轉(zhuǎn)載,轉(zhuǎn)載請(qǐng)注明作者出處。謝謝!
作者:傳智播客Java培訓(xùn)學(xué)院
首發(fā):http://m.fskzgqt.cn/javaee 
0 分享到:
和我們?cè)诰€(xiàn)交談!