Lucene 是apache軟件基金會一個開放源代碼的全文檢索引擎工具包,是一個全文檢索引擎的架構,提供了完整的查詢引擎和索引引擎,部分文本分析引擎。它不是一個完整的搜索應用程序,而是爲你的應用程序提供索引和搜索功能。lucene 可以爲文本類型的數據創建索引,因此你只要能把你要索引的數據格式轉化的文本的,Lucene 就能對你的文檔進行索引和搜索。好比你要對一些 HTML 文檔,PDF 文檔進行索引的話你就首先須要把 HTML 文檔和 PDF 文檔轉化成文本格式的,而後將轉化後的內容交給 Lucene 進行索引,而後把建立好的索引文件保存到磁盤或者內存中,最後根據用戶輸入的查詢條件在索引文件上進行查詢。前端
搜索應用程序和 Lucene 之間的關係,也反映了利用 Lucene 構建搜索應用程序的流程:java
索引是現代搜索引擎的核心,創建索引的過程就是把源數據處理成很是方便查詢的索引文件的過程。爲何索引這麼重要呢,試想你如今要在大量的文檔中搜索含有某個關鍵詞的文檔,那麼若是不創建索引的話你就須要把這些文檔順序的讀入內存,而後檢查這個文章中是否是含有要查找的關鍵詞,這樣的話就會耗費很是多的時間,想一想搜索引擎但是在毫秒級的時間內查找出要搜索的結果的。這就是因爲創建了索引的緣由,你能夠把索引想象成這樣一種數據結構,他可以使你快速的隨機訪問存儲在索引中的關鍵詞,進而找到該關鍵詞所關聯的文檔。Lucene 採用的是一種稱爲反向索引(inverted index)的機制。反向索引就是說咱們維護了一個詞 / 短語表,對於這個表中的每一個詞 / 短語,都有一個鏈表描述了有哪些文檔包含了這個詞 / 短語。這樣在用戶輸入查詢條件的時候,就能很是快的獲得搜索結果。搜索引擎首先會對搜索的關鍵詞進行解析,而後再在創建好的索引上面進行查找,最終返回和用戶輸入的關鍵詞相關聯的文檔。對於中文用戶來講,最關心的問題是其是否支持中文的全文檢索。因爲Lucene良好架構設計,對中文的支持只需對其語言詞法分析接口進行擴展就能實現對中文檢索的支持。web
三. 索引步驟ajax
四. 搜索組件數據庫
搜索組件即爲輸入搜索短語,而後進行分詞,然從索引中查找單詞,從而找到包含該單詞的文檔。搜索質量由查準率和查全率來衡量。搜索組件主要包括如下內容:
apache
五. 官網實例解析瀏覽器
Lucene的使用主要體如今兩個步驟:
服務器
下面針對官網上面給出的一個例子,進行分析:網絡
Analyzer analyzer = new StandardAnalyzer(Version.LUCENE_CURRENT); // Store the index in memory: Directory directory = new RAMDirectory(); // To store an index on disk, use this instead: //Directory directory = FSDirectory.open("/tmp/testindex"); IndexWriterConfig config = new IndexWriterConfig(Version.LUCENE_CURRENT, analyzer); IndexWriter iwriter = new IndexWriter(directory, config); Document doc = new Document(); String text = "This is the text to be indexed."; doc.add(new Field("fieldname", text, TextField.TYPE_STORED)); iwriter.addDocument(doc); iwriter.close(); // Now search the index: DirectoryReader ireader = DirectoryReader.open(directory); IndexSearcher isearcher = new IndexSearcher(ireader); // Parse a simple query that searches for "text": QueryParser parser = new QueryParser(Version.LUCENE_CURRENT, "fieldname", analyzer); Query query = parser.parse("text"); ScoreDoc[] hits = isearcher.search(query, null, 1000).scoreDocs; assertEquals(1, hits.length); // Iterate through the results: for (int i = 0; i < hits.length; i++) { Document hitDoc = isearcher.doc(hits[i].doc); assertEquals("This is the text to be indexed.", hitDoc.get("fieldname")); } ireader.close(); directory.close();
首先,咱們須要定義一個詞法分析器。數據結構
好比一句話,「我愛咱們的中國!」,如何對他拆分,扣掉停頓詞「的」,提取關鍵字「我」「咱們」「中國」等等。這就要藉助的詞法分析器Analyzer來實現。這裏面使用的是標準的詞法分析器,若是專門針對漢語,還能夠搭配paoding,進行使用。
1 Analyzer analyzer = new StandardAnalyzer(Version.LUCENE_CURRENT);
參數中的Version.LUCENE_CURRENT,表明使用當前的Lucene版本,本文環境中也能夠寫成Version.LUCENE_40。
第二步,肯定索引文件存儲的位置,Lucene提供給咱們兩種方式:
1 本地文件存儲
Directory directory = FSDirectory.open("/tmp/testindex");
2 內存存儲
Directory directory = new RAMDirectory();
能夠根據本身的須要進行設定。
第三步,建立IndexWriter,進行索引文件的寫入。
IndexWriterConfig config = new IndexWriterConfig(Version.LUCENE_CURRENT, analyzer); IndexWriter iwriter = new IndexWriter(directory, config);
這裏的IndexWriterConfig,據官方文檔介紹,是對indexWriter的配置,其中包含了兩個參數,第一個是目前的版本,第二個是詞法分析器Analyzer。
第四步,內容提取,進行索引的存儲。
Document doc = new Document(); String text = "This is the text to be indexed."; doc.add(new Field("fieldname", text, TextField.TYPE_STORED)); iwriter.addDocument(doc); iwriter.close();
第一行,申請了一個document對象,這個相似於數據庫中的表中的一行。
第二行,是咱們即將索引的字符串。
第三行,把字符串存儲起來(由於設置了TextField.TYPE_STORED,若是不想存儲,可使用其餘參數,詳情參考官方文檔),並存儲「代表」爲"fieldname".
第四行,把doc對象加入到索引建立中。
第五行,關閉IndexWriter,提交建立內容。
這就是索引建立的過程。
第一步,打開存儲位置
DirectoryReader ireader = DirectoryReader.open(directory);
第二步,建立搜索器
IndexSearcher isearcher = new IndexSearcher(ireader);
第三步,相似SQL,進行關鍵字查詢
QueryParser parser = new QueryParser(Version.LUCENE_CURRENT, "fieldname", analyzer); Query query = parser.parse("text"); ScoreDoc[] hits = isearcher.search(query, null, 1000).scoreDocs; assertEquals(1, hits.length); for (int i = 0; i < hits.length; i++) { Document hitDoc = isearcher.doc(hits[i].doc); assertEquals("This is the text to be indexed.",hitDoc.get("fieldname")); }
這裏,咱們建立了一個查詢器,並設置其詞法分析器,以及查詢的「表名「爲」fieldname「。查詢結果會返回一個集合,相似SQL的ResultSet,咱們能夠提取其中存儲的內容。
關於各類不一樣的查詢方式,能夠參考官方手冊,或者推薦的PPT
第四步,關閉查詢器等。
ireader.close();
directory.close();
本身實現的一個小實例:對一個文件夾內的內容進行索引的建立,並根據關鍵字篩選文件,並讀取其中的內容。
package cn.lnu.edu.yxk; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.util.ArrayList; import java.util.Date; import java.util.List; import jxl.Cell; import jxl.Sheet; import jxl.Workbook; import jxl.read.biff.BiffException; import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.analysis.standard.StandardAnalyzer; import org.apache.lucene.document.Document; import org.apache.lucene.document.Field; import org.apache.lucene.index.DirectoryReader; import org.apache.lucene.index.IndexWriter; import org.apache.lucene.index.IndexWriterConfig; import org.apache.lucene.queryparser.classic.ParseException; import org.apache.lucene.queryparser.classic.QueryParser; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; import org.apache.lucene.search.ScoreDoc; import org.apache.lucene.store.Directory; import org.apache.lucene.store.FSDirectory; import org.apache.poi.hwpf.HWPFDocument; import org.apache.poi.hwpf.usermodel.Range; /** * 對一個文件夾內的內容進行索引的建立,並根據關鍵字篩選文件,讀取其中的內容。 * @author yxk * */ public class IndexManager { private static String content = "";//文件裏面的內容 private static String INDEX_DIR = "D:\\test\\luceneIndex";//索引建立的存儲目錄 private static String DATA_DIR = "D:\\test\\luceneData";//文件夾的目錄 private static Analyzer analyzer = null;//詞法分析器 private static Directory directory = null;//索引文件存儲的位置 private static IndexWriter indexWriter = null;//建立索引器,索引文件的寫入 /** * 建立當前文件目錄的索引 * @param path當前目錄的文件 * @return 返回是否建立成功 */ public static Boolean createIndex(String path) { Date date1 = new Date();//建立須要的時間 List<File> files = listFile(path);// 獲取指定目錄下得全部符合條件的文件 // 獲取文件的內容 for (File file : files) { content = ""; //經過文件類型獲取文件的內容 String type = file.getName().substring( file.getName().lastIndexOf(".") + 1); if ("txt".equalsIgnoreCase(type)) { content += txt2String(file); } else if ("doc".equalsIgnoreCase(type)) { content += doc2String(file); } else if ("xls".equalsIgnoreCase(type)) { content += xls2String(file); } System.out.println("name"+file.getName()); System.out.println("path"+file.getPath()); //System.out.println(file.getName().getBytes().toString()); System.out.println(); try { analyzer = new StandardAnalyzer();//詞法分析器 directory = FSDirectory.open(new File(INDEX_DIR).toPath());//索引建立存儲的位置 // System.out.println("ssss" // + new File(INDEX_DIR).toPath().toString()); //自動建立索引目錄 File indexFile = new File(INDEX_DIR); if (!indexFile.exists()) { indexFile.mkdirs(); } //索引文件的寫入 IndexWriterConfig config = new IndexWriterConfig(analyzer); indexWriter = new IndexWriter(directory, config); /* * 內容提取,進行索引的存儲 */ //申請了一個document對象,這個相似於數據庫中的表中的一行。 Document document = new Document(); //把字符串存儲起來(由於設置了TextField.TYPE_STORED,若是不想存儲,可使用其餘參數,詳情參考官方文檔),並存儲「代表」爲"fieldname". document.add(new org.apache.lucene.document.TextField( "filename", file.getName(), Field.Store.YES));//文件名索引建立 document.add(new org.apache.lucene.document.TextField( "content", content, Field.Store.YES));//文件內容索引建立 document.add(new org.apache.lucene.document.TextField("path", file.getPath(), Field.Store.YES));//文件路徑索引的建立 //把document對象加入到索引建立中 indexWriter.addDocument(document); //關閉IndexWriter,提交建立內容。 indexWriter.commit(); closeWriter(); } catch (IOException e) { e.printStackTrace(); } content = ""; } Date date2 = new Date(); System.out.println("建立索引-----耗時:" + (date2.getTime() - date1.getTime()) + "ms\n"); return true; } /** * 查詢索引,返回符合條件的文件 * * @param 查詢的字符串 * @return 符合條件的結果 * @throws IOException */ public static void serarchIndex(String text) { Date date1 = new Date(); try { //打開存儲位置 directory = FSDirectory.open(new File(INDEX_DIR).toPath()); analyzer = new StandardAnalyzer(); DirectoryReader ireader = DirectoryReader.open(directory); //建立搜索器 IndexSearcher isearcher = new IndexSearcher(ireader); /* * 相似SQL,進行關鍵字查詢 */ QueryParser parser = new QueryParser("content", analyzer); Query query = parser.parse(text); //建立了一個查詢器,並設置其詞法分析器,以及查詢的「表名「爲」fieldname「。查詢結果會返回一個集合,相似SQL的ResultSet,咱們能夠提取其中存儲的內容。 ScoreDoc[] hits = isearcher.search(query, 1000).scoreDocs; for (int i = 0; i < hits.length; i++) { Document hitDoc = isearcher.doc(hits[i].doc); System.out.println("-----------"); System.out.println(hitDoc.get("filename")); System.out.println(hitDoc.get("content")); System.out.println(hitDoc.get("path")); System.out.println("------------"); } //關閉查詢器 ireader.close(); directory.close(); } catch (IOException e) { e.printStackTrace(); } catch (ParseException e) { e.printStackTrace(); } Date date2 = new Date(); System.out.println("關鍵字查詢-----耗時:" + (date2.getTime() - date1.getTime()) + "ms\n"); } /** * * @throws IOException */ private static void closeWriter() throws IOException { if (indexWriter != null) indexWriter.close(); } /** * 讀取xls文件內容,引入jxl.jar類型的包 * @param file * @return 返回內容 */ private static String xls2String(File file) { String result = ""; try { FileInputStream fis = new FileInputStream(file); StringBuilder sb = new StringBuilder(); jxl.Workbook rwb = Workbook.getWorkbook(fis); Sheet[] sheet = rwb.getSheets(); for (int i = 0; i < sheet.length; i++) { Sheet rs = rwb.getSheet(i); for (int j = 0; i < rs.getRows(); j++) { Cell[] cells = rs.getRow(j); for (int k = 0; k < cells.length; k++) { sb.append(cells[k].getContents()); } } } fis.close(); result += sb.toString(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (BiffException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return result; } /** * 讀取doc類型文件的內容,經過poi.jar * @param file的類型 * @return 返回文件的內容 */ private static String doc2String(File file) { String result = ""; try { FileInputStream fis = new FileInputStream(file);//文件輸入流 HWPFDocument document = new HWPFDocument(fis); Range range = document.getRange(); result += range.text(); fis.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return result; } /** * 讀取txt文件的內容 * * @param file想要讀取的文件類型 * @return 返回文件內容 */ private static String txt2String(File file) { String result = ""; try { BufferedReader reader = new BufferedReader(new FileReader(file)); String s = ""; while ((s = reader.readLine()) != null) { result += result + "\n" + s; } reader.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return result; } /** * 過濾當前目錄下得文件 * @param path 當前目錄下得文件 * @return 返回符合條件的文件 */ private static List<File> listFile(String path) { File[] files = new File(path).listFiles(); List<File> fileList = new ArrayList<File>(); for (File file : files) { if (isTxtFile(file.getName())) { fileList.add(file); } } return fileList; } /** * 判斷是否爲目標文件,支持的格式爲.txt,.doc,.xls文件格式 若是是文件類型知足過濾條件,返回true;不然返回false * @param name 根據文件名的後綴 * @return 是否符合格式規範 */ private static boolean isTxtFile(String name) { if (name.lastIndexOf(".txt") > 0) return true; else if (name.lastIndexOf(".doc") > 0) return true; else if (name.lastIndexOf(".xls") > 0) return true; return false; } public static void main(String[] args) { //建立索引目錄,運行一次,從新建立一次 File fileIndex = new File(INDEX_DIR); if (deleteIndex(fileIndex)) { fileIndex.mkdir(); } else { fileIndex.mkdir(); } //建立索引文件 createIndex(DATA_DIR); //經過關鍵字查詢 serarchIndex("中華"); } /** * 刪除文件目錄下得全部文件 * * @param fileIndex 當前索引目錄下得文件 * @return 返回是否刪除從新建立 */ private static boolean deleteIndex(File fileIndex) { if (fileIndex.isDirectory()) { File[] files = fileIndex.listFiles(); for (int i = 0; i < files.length; i++) { deleteIndex(files[i]); } } fileIndex.delete(); return true; } }