搜索引擎項目代碼主要分爲三個部分,第一部分是構建索引,全文檢索;第二部分是輸入問題,對問題進行分詞、提取關鍵詞、關鍵詞擴展;第三部分是將搜索結果輸出到GUI圖形用戶界面。java
搜索引擎的工做流程能夠簡化歸結爲如下四個步驟:數據庫
(1)網上抓取網頁apache
(2)創建索引數據庫架構
(3)在索引數據庫中搜索ide
(4)對搜索結果進行處理和排序函數
全文檢索是一種將文件中全部文本與檢索項匹配的文字資料檢索方法。性能
全文檢索大致分兩個過程,索引建立 (Indexing) 和搜索索引 (Search) 。ui
①索引建立:從結構化和非結構化數據提取信息,建立索引的過程。this
②搜索索引:獲得用戶的查詢請求,搜索建立的索引,而後返回結果。搜索引擎
Lucene是一個高性能,易於擴展的IR(Information Retrieval)Java類庫,能夠利用其中的Java類輕鬆地在應用程序中增長索引和搜索功能。Lucene徹底用Java實現,具備良好的跨平臺性,是Apache Jakarta項目中一個子項目。
Lucene的架構圖以下圖4.1所示:
圖4.1Lucene的架構圖
Lucene的核心的索引類簡介以下:
① IndexWriter類
IndexWriter是索引過程當中的核心部件。這個類創建一個新的索引和添加文檔(Documents)到一個存在的索引中,它對索引只有寫權限,不具備讀和搜索的權限。
② Directory類
Directory類表明了Lucene索引的位置,它是一個抽象類,有兩個子類: FSDirectory類,它將索引存儲在磁盤上,並在文件系統中維護一個真實文件的列表;另外一個是RAMDirectory類,它將全部索引信息都放在內存中,適用於索引較小的狀況。
③ Analyzer類
在文本被索引以前,要通過一個Analyzer的處理。在IndexWriter的構造函數中指定的Analyzer負責從被索引的文本中抽取出標誌(tokens),並排除掉標誌之外的部分。假如被索引的內容不是純文本的話,應該先將它轉換爲純文本。Analyzer是一個抽象類,Lucene中包含了幾種它的實現,用於完成不一樣的功能,例如用於去掉文本中的無用詞(stop word)、將標誌(tokens)所有轉換爲小寫等等。爲了達到特定的目標,能夠設計不一樣的Analyzer的實現,這樣就使得Lucene具備高度的靈活性及可擴充性。
④ Document類
一個Document表明了一些字段(fields)的集合。
⑤ Field類:
在索引中的每一個Document都包含了一個或更多的命名了的字段(fields),這些字段用一個稱爲Field的類來表示。每一個字段都對應於在搜索期間能從索引中查詢並提取的一段數據。
⑥ IndexSearcher類
IndexSearcher用於搜索IndexWriter創建的索引。IndexSearcher以只讀方式打開索引,並提供了幾種搜索方法,這些方法是它的抽象父類的實現。
⑦ Term類
Term是最基本的搜索單元。與Field對象相似,它也包含了一對字符串元素:名字和值。Term對象和索引過程也是相關的。在搜索期間,先構建Term對象而後連同TermQuery一塊兒使用。
⑧ TermQuery類
TermQuery類是Lucene支持的最基本的查詢類。它被用來匹配含有特定值的字段的文檔。
⑨ Hits類
Htis類是一個簡單的指向分級搜索結果的指針容器。考慮到性能的緣由,Hits的實例並不從索引中裝載匹配查詢的全部文檔只是他們的一小部分。
構建索引所用到的數據來自搜狗實驗室( www.sogou.com/labs/resource)的搜狐新聞數據,對其中健康(health)和娛樂(yule)兩類的數據構建了索引。
①Lucene-core-4.0.0.jar:包括了經常使用的文檔,索引,搜索,存儲等相關核心代碼。
②Lucene-analyzers-common-4.0.0.jar:包含詞法分析器,用於提取關鍵字。
③Lucene-highlighter-4.0.0.jar:將搜索出的內容高亮顯示。
④Lucene-queryparser-4.0.0.jar:用於各類搜索,如模糊搜索,範圍搜索等。
①建立索引,經過IndexWriter對不一樣的文件進行索引的建立,並將其保存在索引相關文件存儲的位置中。
②經過索引查詢關鍵字相關文檔。
①定義詞法分析器
②肯定索引文件存儲的位置
③建立IndexWriter,進行索引文件的寫入
④內容提取,進行索引的存儲
⑤關閉查詢器
4.所有代碼
package lucene; public class Article { private Integer id; private String title; private String content; public Article() { super(); } public Article(Integer id, String title, String content) { super(); this.id = id; this.title = title; this.content = content; } public synchronized Integer getId() { return id; } public synchronized void setId(Integer id) { this.id = id; } public synchronized String getTitle() { return title; } public synchronized void setTitle(String title) { this.title = title; } public synchronized String getContent() { return content; } public synchronized void setContent(String content) { this.content = content; } @Override public String toString() { return "Article [id=" + id + ", title=" + title + ", content=" + content + "]"; } }
package lucene; /* * 環境:lucene-3.6.0 */ import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.document.Document; import org.apache.lucene.index.CorruptIndexException; import org.apache.lucene.queryParser.ParseException; import org.apache.lucene.queryParser.QueryParser; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; import org.apache.lucene.search.ScoreDoc; import org.apache.lucene.search.Sort; import org.apache.lucene.search.TopDocs; import org.apache.lucene.store.Directory; import org.apache.lucene.store.SimpleFSDirectory; import org.apache.lucene.util.Version; import org.wltea.analyzer.lucene.IKAnalyzer; import lucene.Article; /** * Lucene 檢索各類索引的實現方式總結 * @author Administrator * */ public class lucene_Search { //public static final String INDEX_DIR_PATH = "indexDir1"; public static final String INDEX_DIR_PATH = "indexDir"; /* 建立簡單中文分析器 建立索引使用的分詞器必須和查詢時候使用的分詞器同樣,不然查詢不到想要的結果 */ private Analyzer analyzer = null; // 索引保存目錄 private File indexFile = null; //目錄對象,由於操做索引文件都要用到它,因此定義爲全局變量 private Directory directory = null; //索引搜索對象 private IndexSearcher indexSearcher; /** * 初始化方法 * @throws IOException */ public void init() throws IOException{ analyzer = new IKAnalyzer(true); indexFile = new File(INDEX_DIR_PATH); directory = new SimpleFSDirectory(indexFile); indexSearcher = new IndexSearcher(directory); System.out.println("*****************搜索索引程序初始化成功**********************"); } /** * 根據傳遞的結果集 封裝成集合後顯示出來 * @param scoreDocs * @return * @throws IOException * @throws CorruptIndexException */ public List<String> showResult(ScoreDoc[] scoreDocs) throws CorruptIndexException, IOException{ List<Article> articles = new ArrayList<Article>(); for (int i = 0; i < scoreDocs.length; i++) { int doc = scoreDocs[i].doc;//索引id Document document = indexSearcher.doc(doc); Article article = new Article(); if (document.get("id") == null) { System.out.println("id爲空"); } else { article.setId(Integer.parseInt(document.get("id"))); article.setTitle(document.get("title")); article.setContent(document.get("content")); articles.add(article); } } List<String> resultList = new ArrayList<String>(); if(articles.size()!=0){ for (Article article : articles) { //System.out.println(article); resultList.add(article.getContent()); } }else{ System.out.println("沒有查到記錄。"); } return resultList; } /** * 經過QueryParser綁定單個字段來檢索索引記錄 * @param keyword * @return * @throws ParseException * @throws IOException * @throws CorruptIndexException */ public List<String> searchByQueryParser(String keyword) throws ParseException, CorruptIndexException, IOException{ System.out.println("*****************經過QueryParser來檢索索引記錄**********************"); QueryParser queryParser = new QueryParser(Version.LUCENE_36, "content", analyzer); Query query = queryParser.parse(keyword); // public TopFieldDocs search(Query query, int n, Sort sort) // 參數分別表示 Query查詢對象,返回的查詢數目,排序對象 TopDocs topDocs = indexSearcher.search(query, 50, new Sort()); List<String> resultList = showResult(topDocs.scoreDocs); return resultList; } /** * 銷燬當前的操做類的實現,主要關閉資源的鏈接 */ public void destory() throws IOException{ analyzer.close(); directory.close(); System.out.println("*****************成功關閉資源鏈接**********************"); } public static List<String> lucene_searchIndex(String question) throws IOException, ParseException { lucene_Search luceneInstance = new lucene_Search(); luceneInstance.init(); List<String> resultList = luceneInstance.searchByQueryParser(question); luceneInstance.destory(); HashSet<String> resultset = new HashSet<String>(); for(int i=0;i<resultList.size();i++){ resultset.add(resultList.get(i)); } resultList.clear(); for(String data:resultset){ resultList.add(data); } return resultList; } }
package lucene; import java.io.File; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.List; import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.document.Document; import org.apache.lucene.document.Field; import org.apache.lucene.index.CorruptIndexException; import org.apache.lucene.index.IndexReader; import org.apache.lucene.index.IndexWriter; import org.apache.lucene.index.IndexWriterConfig; import org.apache.lucene.index.Term; import org.apache.lucene.queryParser.ParseException; import org.apache.lucene.store.Directory; import org.apache.lucene.store.LockObtainFailedException; import org.apache.lucene.store.SimpleFSDirectory; import org.apache.lucene.util.Version; import org.wltea.analyzer.lucene.IKAnalyzer; import Utils.readBigData; public class lucene_BuildIndex { // public static final String INDEX_DIR_PATH = "indexDir1"; public static final String INDEX_DIR_PATH = "indexDir3"; /* 建立簡單中文分析器 建立索引使用的分詞器必須和查詢時候使用的分詞器同樣,不然查詢不到想要的結果 */ private Analyzer analyzer = null; // 索引保存目錄 private File indexFile = null; //目錄對象,由於操做索引文件都要用到它,因此定義爲全局變量 private Directory directory = null; //建立IndexWriter索引寫入器 IndexWriterConfig indexWriterConfig = null; SimpleDateFormat simpleDateFormat = null; /** * 得到指定格式的時間字符串 * @return */ public String getDate(){ return simpleDateFormat.format(new Date()); } /** * 初始化方法 * @throws IOException */ public void init() throws IOException{ analyzer = new IKAnalyzer(true); indexFile = new File(INDEX_DIR_PATH); directory = new SimpleFSDirectory(indexFile); simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); System.out.println("*****************倒排索引程序初始化成功**********************"); } /** * 將article對象的屬性所有封裝成document對象,重構以後方便建立索引 * @param article * @return */ public Document createDocument(Article article){ Document document = new Document(); document.add(new Field("id", article.getId().toString(), Field.Store.YES, Field.Index.NOT_ANALYZED)); document.add(new Field("content", article.getContent().toString(), Field.Store.YES, Field.Index.ANALYZED)); //document.add(new Field("title", article.getTitle().toString(),Field.Store.YES, Field.Index.ANALYZED)); return document; } /** * 爲了如實反映操做索引文件以後的效果,每次操做以後查詢索引目錄下全部的索引內容 * @throws IOException * @throws CorruptIndexException */ public void openIndexFile() throws CorruptIndexException, IOException{ System.out.println("*****************讀取索引開始**********************"); IndexReader indexReader = IndexReader.open(directory); int docLength = indexReader.maxDoc(); for (int i = 0; i < docLength; i++) { Document doc = indexReader.document(i); Article article = new Article(); if (doc.get("id") == null) { System.out.println("id爲空"); } else { article.setId(Integer.parseInt(doc.get("id"))); article.setTitle(doc.get("title")); article.setContent(doc.get("content")); } //System.out.println(article); } System.out.println("*****************讀取索引結束**********************\n"); } /** * 建立索引到索引文件中 * @param article * @throws IOException * @throws LockObtainFailedException * @throws CorruptIndexException */ public void createIndex(Article article) throws CorruptIndexException, LockObtainFailedException, IOException{ indexWriterConfig = new IndexWriterConfig(Version.LUCENE_36, analyzer); IndexWriter indexWriter = new IndexWriter(directory, indexWriterConfig); indexWriter.addDocument(createDocument(article)); indexWriter.close(); //System.out.println("[ " + getDate() + " ] Lucene寫入索引到 [" + indexFile.getAbsolutePath() + "] 成功。"); } /** * 根據文件中的id刪除對應的索引文件 * @param contentId * @throws IOException * @throws ParseException */ public void deleteIndex(String contentId) throws IOException, ParseException{ //判斷索引文件目錄內容是否有索引,有返回true ,沒有返回false if(IndexReader.indexExists(directory)){ indexWriterConfig = new IndexWriterConfig(Version.LUCENE_36, analyzer); IndexWriter indexWriter = new IndexWriter(directory, indexWriterConfig); //封裝term去檢索字段名爲id ,具體值爲contentId的記錄,若是存在就會刪除,不然什麼都不作 indexWriter.deleteDocuments(new Term("id",contentId)); /* QueryParser queryParser = new QueryParser(Version.LUCENE_36, "id", analyzer); Query query = queryParser.parse(contentId); indexWriter.deleteDocuments(query); */ indexWriter.close(); System.out.println("[ " + getDate() + " ] Lucene刪除索引到 [" + indexFile.getAbsolutePath() + "] 成功。"); }else{ throw new IOException("[ " + getDate() + " ] Lucene刪除索引失敗,在 " + indexFile.getAbsolutePath() + "目錄中沒有找到索引文件。" ); } } /** * 因爲實際的內容修改,因此索引文件也要跟着修改 ,具體實現方式就是先刪除索引,而後從新添加 * @param article * @throws IOException * @throws ParseException */ public void updateIndex(Article article) throws IOException, ParseException{ deleteIndex(article.getId().toString()); createIndex(article); System.out.println("updateIndex函數執行完畢"); } /** * 銷燬當前的操做類的實現,主要關閉資源的鏈接 * * @throws IOException */ public void destory() throws IOException{ analyzer.close(); directory.close(); System.out.println("*****************成功關閉資源鏈接**********************"); } public static void main(String[] args) throws IOException, ParseException { String path = "D:\\#個人文件夾\\天然語言處理\\問答系統\\爬取網頁\\1_2文檔切分紅句子.txt"; List<String> information = readBigData.readTxtByStringBuffer(path); lucene_BuildIndex luceneInstance = new lucene_BuildIndex(); luceneInstance.init();//初始化 for(int i=0;i<information.size();i++){ Article article = new Article(i,null,information.get(i)); luceneInstance.createIndex(article); } luceneInstance.openIndexFile(); luceneInstance.destory();//銷燬 } }