最簡單搜索引擎代碼


最簡單搜索引擎代碼

Lucene.Net核心類簡介

    先運行寫好的索引的代碼,再向下講解各個類的作用,不用背代碼。

   (*)Directory表示索引文件(Lucene.net用來保存用戶扔過來的數據的地方)保存的地方,是抽象類,兩個子類FSDirectory(文件中)、RAMDirectory (內存中)。使用的時候別和IO里的Directory弄混了。

    創建FSDirectory的方法,FSDirectory directory =FSDirectory.Open(new DirectoryInfo(indexPath),new NativeFSLockFactory()), path索引的文件夾路徑

    IndexReader對索引進行讀取的類,對IndexWriter進行寫的類。IndexReader的靜態方法bool IndexExists(Directory directory)判斷目錄directory是否是一個索引目錄。IndexWriter的bool IsLocked(Directory directory) 判斷目錄是否鎖定,在對目錄寫之前會先把目錄鎖定。兩個IndexWriter沒法同時寫一個索引文件。IndexWriter在進行寫操作的時候會自動 加鎖,close的時候會自動解鎖。IndexWriter.Unlock方法手動解鎖(比如還沒來得及close IndexWriter 程序就崩潰了,可能造成一直被鎖定)。

創建索引

    構造函數:IndexWriter(Directorydir, Analyzer a, bool create, MaxFieldLength mfl)因為IndexWriter把輸入寫入索引的時候,Lucene.net是把寫入的文件用指定的分詞器將文章分詞(這樣檢索的時候才能查的快), 然后將詞放入索引文件。

    void AddDocument(Document doc),向索引中添加文檔(Insert)。Document類代表要索引的文檔(文章),最重要的方法Add(Field field),向文檔中添加字段。Document是一片文檔,Field是字段(屬性)。Document相當於一條記錄,Field相當於字段。

    Field類的構造函數 Field(string name, string value, Field.Store store, Field.Indexindex, Field.TermVector termVector):name表示字段名; value表示字段值;

    store表示是否存儲value值,可選值Field.Store.YES存儲,Field.Store.NO不存 儲,Field.Store.COMPRESS壓縮存儲;默認只保存分詞以后的一堆詞,而不保存分詞之前的內容,搜索的時候無法根據分詞后的東西還原原 文,因此如果要顯示原文(比如文章正文)則需要設置存儲。

    index表示如何創建索引,可選值Field.Index. NOT_ANALYZED,不創建索引,Field.Index. ANALYZED,創建索引;創建索引的字段才可以比較好的檢索。是否碎屍萬段!是否需要按照這個字段進行“全文檢索”。

    termVector表示如何保存索引詞之間的距離。“北京歡迎你們大家”,索引中是如何保存“北京”和“大家”之間“隔多少單詞”。方便只檢索在一定距離之內的詞。

    為什么要把帖子的url做為一個Field,因為要在搜索展示的時候先帖子地址取出來構建超鏈接,所以Field.Store.YES;一般不需要 對url進行檢索,所以Field.Index.NOT_ANALYZED 。根據《紅樓夢》構建的“詞:頁數”紙,在構建完成后就可以把原文《紅樓夢》扔了

案例:對1000至1100號帖子進行索引。“只要能看懂例子和文檔,稍作修改即可實現自己的需求”。除了基礎知識外,第三方開發包只要“能看懂,改改即可”

引入命名空間:

  1. using Lucene.Net.Store;  
  2. using System.IO;  
  3. using Lucene.Net.Index;  
  4. using Lucene.Net.Analysis.PanGu;  
  5. using Lucene.Net.Documents;  
  6. using Lucene.Net.Search;  

1、 對數據進行索引

  1. string indexPath = @"C:\1017index";//注意和磁盤上文件夾的大小寫一致,否則會報錯。  
  2. FSDirectory directory = FSDirectory.Open(new DirectoryInfo(indexPath), new NativeFSLockFactory());  
  3. bool isUpdate = IndexReader.IndexExists(directory);//判斷索引庫是否存在  
  4. if (isUpdate)  
  5. {  
  6.     //如果索引目錄被鎖定(比如索引過程中程序異常退出),則首先解鎖  
  7.     //Lucene.Net在寫索引庫之前會自動加鎖,在close的時候會自動解鎖  
  8.     //不能多線程執行,只能處理意外被永遠鎖定的情況  
  9.     if (IndexWriter.IsLocked(directory))  
  10.     {  
  11.         IndexWriter.Unlock(directory);//un-否定。強制解鎖  
  12.     }  
  13. }  
  14. IndexWriter writer = new IndexWriter(directory, new PanGuAnalyzer(), !isUpdate, Lucene.Net.Index.IndexWriter.MaxFieldLength.UNLIMITED);  
  15. for (int i = 1000; i < 1100; i++)  
  16. {  
  17.     string txt = File.ReadAllText(@"D:\我的文檔\文章\" + i + ".txt");  
  18.     Document document = new Document();//一條Document相當於一條記錄  
  19.     document.Add(new Field("id", i.ToString(), Field.Store.YES, Field.Index.NOT_ANALYZED));  
  20.     //每個Document可以有自己的屬性(字段),所有字段名都是自定義的,值都是string類型  
  21.     //Field.Store.YES不僅要對文章進行分詞記錄,也要保存原文,就不用去數據庫里查一次了  
  22.     //需要進行全文檢索的字段加 Field.Index. ANALYZED  
  23.     document.Add(new Field("msg", txt, Field.Store.YES, Field.Index.ANALYZED, Lucene.Net.Documents.Field.TermVector.WITH_POSITIONS_OFFSETS));  
  24.     //防止重復索引  
  25.     writer.DeleteDocuments(new Term("id", i.ToString()));//防止存在的數據//delete from t where id=i  
  26.     //如果不存在則刪除0條  
  27.     writer.AddDocument(document);//把文檔寫入索引庫  
  28. }  
  29. writer.Close();  
  30. directory.Close();//不要忘了Close,否則索引結果搜不到  

2、搜索的代碼

  1. string indexPath = @"C:\1017index";  
  2.   
  3. string kw = TextBox1.Text;  
  4. FSDirectory directory = FSDirectory.Open(new DirectoryInfo(indexPath), new NoLockFactory());  
  5. IndexReader reader = IndexReader.Open(directory, true);  
  6. IndexSearcher searcher = new IndexSearcher(reader);  
  7. PhraseQuery query = new PhraseQuery();//查詢條件  
  8. query.Add(new Term("msg", kw));//where contains("msg",kw)  
  9. //foreach (string word in kw.Split(' '))//先用空格,讓用戶去分詞,空格分隔的就是詞“計算機 專業”  
  10. //{  
  11. //    query.Add(new Term("msg", word));//contains("msg",word)  
  12. //}  
  13. query.SetSlop(100);//兩個詞的距離大於100(經驗值)就不放入搜索結果,因為距離太遠相關度就不高了  
  14. TopScoreDocCollector collector = TopScoreDocCollector.create(1000, true);//盛放查詢結果的容器  
  15. searcher.Search(query, null, collector);//使用query這個查詢條件進行搜索,搜索結果放入collector  
  16. //collector.GetTotalHits()總的結果條數  
  17. ScoreDoc[] docs = collector.TopDocs(0, collector.GetTotalHits()).scoreDocs;//從查詢結果中取出第m條到第n條的數據  
  18.   
  19. List<SearchResult> list = new List<SearchResult>();  
  20. for (int i = 0; i < docs.Length; i++)//遍歷查詢結果  
  21. {  
  22.     int docId = docs[i].doc;//拿到文檔的id。因為Document可能非常占內存(DataSet和DataReader的區別)  
  23.     //所以查詢結果中只有id,具體內容需要二次查詢  
  24.     Document doc = searcher.Doc(docId);//根據id查詢內容。放進去的是Document,查出來的還是Document  
  25.     //Console.WriteLine(doc.Get("id"));  
  26.     //Console.WriteLine(doc.Get("msg"));  
  27.     SearchResult result = new SearchResult();  
  28.     result.Id = Convert.ToInt32(doc.Get("id"));  
  29.     result.Msg = doc.Get("msg");//只有 Field.Store.YES的字段才能用Get查出來  
  30.     list.Add(result);  
  31. }  
  32.   
  33. Repeater1.DataSource = list;  
  34. Repeater1.DataBind();  

aspx代碼:

    1. <form id="form1" runat="server">  
    2. <div>  
    3.     <asp:Button ID="Button1" runat="server" onclick="Button1_Click" Text="創建索引" />  
    4.     <br />  
    5.     <br />  
    6.     <asp:TextBox ID="TextBox1" runat="server"></asp:TextBox>  
    7.     <asp:Button ID="Button2" runat="server" onclick="Button2_Click" Text="搜索" />  
    8.     <br />  
    9.     <ul>  
    10.     <asp:Repeater ID="Repeater1" runat="server">  
    11.         <ItemTemplate><li>Id:<%#Eval("Id") %><br /><%#Eval("Msg") %></li></ItemTemplate>  
    12.     </asp:Repeater>  
    13.     </ul>  
    14. </div>  
    15. </form>  

總結下執行順序:發布文章的時候,創建索引,然后搜索文章的時候,就直接從索引庫進行搜索。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM