Lucene.net站內搜索-最簡單搜索引擎代碼
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號帖子進行索引。“只要能看懂例子和文檔,稍作修改即可實現自己的需求”。除了基礎知識外,第三方開發包只要“能看懂,改改即可”
引入命名空間:
- usingLucene.Net.Store;
- usingSystem.IO;
- usingLucene.Net.Index;
- usingLucene.Net.Analysis.PanGu;
- usingLucene.Net.Documents;
- usingLucene.Net.Search;
1、對數據進行索引
- stringindexPath=@"C:/1017index";//注意和磁盤上文件夾的大小寫一致,否則會報錯。
- FSDirectorydirectory=FSDirectory.Open(newDirectoryInfo(indexPath),newNativeFSLockFactory());
- boolisUpdate=IndexReader.IndexExists(directory);//判斷索引庫是否存在
- if(isUpdate)
- {
- //如果索引目錄被鎖定(比如索引過程中程序異常退出),則首先解鎖
- //Lucene.Net在寫索引庫之前會自動加鎖,在close的時候會自動解鎖
- //不能多線程執行,只能處理意外被永遠鎖定的情況
- if(IndexWriter.IsLocked(directory))
- {
- IndexWriter.Unlock(directory);//un-否定。強制解鎖
- }
- }
- IndexWriterwriter=newIndexWriter(directory,newPanGuAnalyzer(),!isUpdate,Lucene.Net.Index.IndexWriter.MaxFieldLength.UNLIMITED);
- for(inti=1000;i<1100;i++)
- {
- stringtxt=File.ReadAllText(@"D:/我的文檔/文章/"+i+".txt");
- Documentdocument=newDocument();//一條Document相當于一條記錄
- document.Add(newField("id",i.ToString(),Field.Store.YES,Field.Index.NOT_ANALYZED));
- //每個Document可以有自己的屬性(字段),所有字段名都是自定義的,值都是string類型
- //Field.Store.YES不僅要對文章進行分詞記錄,也要保存原文,就不用去數據庫里查一次了
- //需要進行全文檢索的字段加Field.Index.ANALYZED
- document.Add(newField("msg",txt,Field.Store.YES,Field.Index.ANALYZED,Lucene.Net.Documents.Field.TermVector.WITH_POSITIONS_OFFSETS));
- //防止重復索引
- writer.DeleteDocuments(newTerm("id",i.ToString()));//防止存在的數據//deletefromtwhereid=i
- //如果不存在則刪除0條
- writer.AddDocument(document);//把文檔寫入索引庫
- }
- writer.Close();
- directory.Close();//不要忘了Close,否則索引結果搜不到
2、搜索的代碼
- stringindexPath=@"C:/1017index";
- stringkw=TextBox1.Text;
- FSDirectorydirectory=FSDirectory.Open(newDirectoryInfo(indexPath),newNoLockFactory());
- IndexReaderreader=IndexReader.Open(directory,true);
- IndexSearchersearcher=newIndexSearcher(reader);
- PhraseQueryquery=newPhraseQuery();//查詢條件
- query.Add(newTerm("msg",kw));//wherecontains("msg",kw)
- //foreach(stringWordinkw.Split(''))//先用空格,讓用戶去分詞,空格分隔的就是詞“計算機專業”
- //{
- //query.Add(newTerm("msg",word));//contains("msg",word)
- //}
- query.SetSlop(100);//兩個詞的距離大于100(經驗值)就不放入搜索結果,因為距離太遠相關度就不高了
- TopScoreDocCollectorcollector=TopScoreDocCollector.create(1000,true);//盛放查詢結果的容器
- searcher.Search(query,null,collector);//使用query這個查詢條件進行搜索,搜索結果放入collector
- //collector.GetTotalHits()總的結果條數
- ScoreDoc[]docs=collector.TopDocs(0,collector.GetTotalHits()).scoreDocs;//從查詢結果中取出第m條到第n條的數據
- List<SearchResult>list=newList<SearchResult>();
- for(inti=0;i<docs.Length;i++)//遍歷查詢結果
- {
- intdocId=docs[i].doc;//拿到文檔的id。因為Document可能非常占內存(DataSet和DataReader的區別)
- //所以查詢結果中只有id,具體內容需要二次查詢
- Documentdoc=searcher.Doc(docId);//根據id查詢內容。放進去的是Document,查出來的還是Document
- //Console.WriteLine(doc.Get("id"));
- //Console.WriteLine(doc.Get("msg"));
- SearchResultresult=newSearchResult();
- result.Id=Convert.ToInt32(doc.Get("id"));
- result.Msg=doc.Get("msg");//只有Field.Store.YES的字段才能用Get查出來
- list.Add(result);
- }
- Repeater1.DataSource=list;
- Repeater1.DataBind();
aspx代碼:
- <formid="form1"runat="server">
- <div>
- <asp:ButtonID="Button1"runat="server"onclick="Button1_Click"Text="創建索引"/>
- <br/>
- <br/>
- <asp:TextBoxID="TextBox1"runat="server"></asp:TextBox>
- <asp:ButtonID="Button2"runat="server"onclick="Button2_Click"Text="搜索"/>
- <br/>
- <ul>
- <asp:RepeaterID="Repeater1"runat="server">
- <ItemTemplate><li>Id:<%#Eval("Id")%><br/><%#Eval("Msg")%></li></ItemTemplate>
- </asp:Repeater>
- </ul>
- </div>
- </form>
- 本文摘自51CTO;