Lucene.Net+盤古分詞 實現搜尋 Quartz.Net(定時任務) 實現熱詞統計
阿新 • • 發佈:2019-02-18
一 :Lucene.Net +盤古分詞 實現搜尋功能
1 、Lucene.Net的配置:
引入相關dll檔案 ( Lucene.Net、log4net、PanGu、PanGu.HighLight、PanGu.Lucene.Analyzer)。當用戶新增一條記錄的時候 ,先向資料庫寫入資料,然後馬上寫入Lucene.Net (為了解決多執行緒併發的問題,需要引入佇列,並且使用單例模式使佇列是唯一的)
示例程式碼:
搜尋部分 注意請求要以GET形式傳送(作用是把網址複製下來發給其他人其他人也可以看到搜尋結果)public sealed class IndexManager { private static readonly IndexManager indexManager = new IndexManager(); //具體單例模式原理見《單例模式》一文 private IndexManager() { } public Queue<SearchBook> queque = new Queue<SearchBook>(); public static IndexManager GetInstance() { return indexManager; } public void ThreadStart() { Thread thread = new Thread(CommondQueue); thread.IsBackground = true; thread.Start(); } private void CommondQueue() { while (true) { if (queque.Count > 0) { WriteToLucene(); } else { Thread.Sleep(3000); } } } //刪除記錄 public void Delete(int id) { SearchBook searchBook = new SearchBook() { ID = id, SearchBookType = Model.EnumType.SearchBookTypeEnum.Delete }; queque.Enqueue(searchBook); } //新增記錄 public void Add(int id, string title, string content) { SearchBook searchBook = new SearchBook() { ID = id, Title = title, Content = content, SearchBookType = Model.EnumType.SearchBookTypeEnum.Add }; queque.Enqueue(searchBook); } //從佇列裡取資料寫入Lucene private void WriteToLucene() { string indexPath = @"C:\Users\Administrator\Desktop\Project.OA12晚\資料\lucenedir";//注意和磁碟上資料夾的大小寫一致,否則會報錯。將建立的分詞內容放在該目錄下。 FSDirectory directory = FSDirectory.Open(new DirectoryInfo(indexPath), new NativeFSLockFactory());//指定索引檔案(開啟索引目錄) FS指的是就是FileSystem bool isUpdate = IndexReader.IndexExists(directory);//IndexReader:對索引進行讀取的類。該語句的作用:判斷索引庫資料夾是否存在以及索引特徵檔案是否存在。 if (isUpdate) { //同時只能有一段程式碼對索引庫進行寫操作。當使用IndexWriter開啟directory時會自動對索引庫檔案上鎖。 //如果索引目錄被鎖定(比如索引過程中程式異常退出),則首先解鎖(提示一下:如果我現在正在寫著已經加鎖了,但是還沒有寫完,這時候又來一個請求,那麼不就解鎖了嗎?這個問題後面會解決) if (IndexWriter.IsLocked(directory)) { IndexWriter.Unlock(directory); } } IndexWriter writer = new IndexWriter(directory, new PanGuAnalyzer(), !isUpdate, Lucene.Net.Index.IndexWriter.MaxFieldLength.UNLIMITED);//向索引庫中寫索引。這時在這裡加鎖。 while (queque.Count > 0) { SearchBook searchBook = queque.Dequeue();//取出資料 writer.DeleteDocuments(new Term("id", searchBook.ID.ToString()));//先把它刪了 if (searchBook.SearchBookType == Model.EnumType.SearchBookTypeEnum.Delete)//如果是刪除 { continue;//跳出本次迴圈 }//否則新增(先刪除再新增 相當於修改) string txt = searchBook.Content; Document document = new Document();//表示一篇文件。 //Field.Store.YES:表示是否儲存原值。只有當Field.Store.YES在後面才能用doc.Get("number")取出值來.Field.Index. NOT_ANALYZED:不進行分詞儲存 document.Add(new Field("id", searchBook.ID.ToString(), Field.Store.YES, Field.Index.NOT_ANALYZED)); document.Add(new Field("title", searchBook.Title, Field.Store.YES, Field.Index.NOT_ANALYZED)); //Field.Index. ANALYZED:進行分詞儲存:也就是要進行全文的欄位要設定分詞 儲存(因為要進行模糊查詢) //Lucene.Net.Documents.Field.TermVector.WITH_POSITIONS_OFFSETS:不僅儲存分詞還儲存分詞的距離。 document.Add(new Field("content", txt, Field.Store.YES, Field.Index.ANALYZED, Lucene.Net.Documents.Field.TermVector.WITH_POSITIONS_OFFSETS)); writer.AddDocument(document); } writer.Close();//會自動解鎖。 directory.Close();//不要忘了Close,否則索引結果搜不到 } }
示例程式碼:
private void SearchFromContent() { string indexPath = Request.MapPath(@"\資料\lucenedir"); //string indexPath = @"C:\Users\Administrator\Desktop\Project.OA12晚\資料\lucenedir"; List<string> kws = Common.PanGuLuceneHelper.FenCi(Request["txtsearch"]);//對使用者輸入的搜尋條件進行拆分。 幫助類見下文 FSDirectory directory = FSDirectory.Open(new DirectoryInfo(indexPath), new NoLockFactory()); IndexReader reader = IndexReader.Open(directory, true); IndexSearcher searcher = new IndexSearcher(reader); //搜尋條件 PhraseQuery query = new PhraseQuery(); foreach (string word in kws)//先用空格,讓使用者去分詞,空格分隔的就是詞“計算機 專業” { query.Add(new Term("content", word)); } //query.Add(new Term("body","語言"));--可以新增查詢條件,兩者是add關係.順序沒有關係. // query.Add(new Term("body", "大學生")); // query.Add(new Term("body", kw));//body中含有kw的文章 query.SetSlop(100);//多個查詢條件的詞之間的最大距離.在文章中相隔太遠 也就無意義.(例如 “大學生”這個查詢條件和"簡歷"這個查詢條件之間如果間隔的詞太多也就沒有意義了。) //TopScoreDocCollector是盛放查詢結果的容器 TopScoreDocCollector collector = TopScoreDocCollector.create(1000, true); searcher.Search(query, null, collector);//根據query查詢條件進行查詢,查詢結果放入collector容器 ScoreDoc[] docs = collector.TopDocs(0, collector.GetTotalHits()).scoreDocs;//得到所有查詢結果中的文件,GetTotalHits():表示總條數 TopDocs(300, 20);//表示得到300(從300開始),到320(結束)的文件內容. //可以用來實現分頁功能 List<SearchBook> list = new List<SearchBook>(); for (int i = 0; i < docs.Length; i++) { // //搜尋ScoreDoc[]只能獲得文件的id,這樣不會把查詢結果的Document一次性載入到記憶體中。降低了記憶體壓力,需要獲得文件的詳細內容的時候通過searcher.Doc來根據文件id來獲得文件的詳細內容物件Document. int docId = docs[i].doc;//得到查詢結果文件的id(Lucene內部分配的id) Document doc = searcher.Doc(docId);//找到文件id對應的文件詳細資訊 SearchBook searchBook = new SearchBook() { ID = int.Parse(doc.Get("id")), Content =Common.PanGuLuceneHelper.PanGuHighLight(Request["txtsearch"],doc.Get("content")) , Title = doc.Get("title") }; list.Add(searchBook); } ViewData["list"] = list; }
盤古分詞幫助類(把使用者搜尋的詞進行拆分,並在搜尋結果中把搜尋詞高亮顯示):(注意在前臺輸出的時候要寫@MvcHtmlString.Create()輸出用來顯示出高亮樣式)
public class PanGuLuceneHelper { public static List<string> FenCi(string str) { Analyzer analyzer = new PanGuAnalyzer(); TokenStream tokenStream = analyzer.TokenStream("", new StringReader(str)); Lucene.Net.Analysis.Token token = null; List<string> list = new List<string>(); while ((token = tokenStream.Next()) != null) { list.Add(token.TermText()); } return list; } public static string PanGuHighLight(string keyWords,string content) { PanGu.HighLight.SimpleHTMLFormatter simpleHTMLFormatter = new PanGu.HighLight.SimpleHTMLFormatter("<font color=\"red\">", "</font>"); //建立Highlighter ,輸入HTMLFormatter 和盤古分詞物件Semgent PanGu.HighLight.Highlighter highlighter = new PanGu.HighLight.Highlighter(simpleHTMLFormatter, new Segment()); //設定每個摘要段的字元數 highlighter.FragmentSize = 150; //獲取最匹配的摘要段 return highlighter.GetBestFragment(keyWords, content); }