程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> MVC+MQ+WinServices+Lucene.Net Demo,

MVC+MQ+WinServices+Lucene.Net Demo,

編輯:C#入門知識

MVC+MQ+WinServices+Lucene.Net Demo,


前言:

我之前沒有接觸過Lucene.Net相關的知識,最近在園子裡看到很多大神在分享這塊的內容,深受啟發。秉著“實踐出真知”的精神,再結合公司項目的實際情況,有了寫一個Demo的想法,算是對自己能力的考驗吧。

 

功能描述:

1. 前台網站把新增的索引項對象(標題、內容)序列化後,發送給MQ

2. MQ接收到消息後先持久化,再推送給消息的消費者

3. 消息的消費者(WinServices)接收到消息後,反序列化成索引項對象,調用SearchEngine類庫的創建索引方法

4. 前台網站調用SearchEngine類庫的查詢方法,並傳入用戶輸入的關鍵字,把查詢後匹配的結果顯示在View上

注:

1. 為了模擬多個用戶同時新增索引項對象,互聯網本身就是一個多線程的環境。這裡使用了ActiveMQ的隊列模式(另外還有主題模式,主要用於消息廣播的場景),因為其內部維護了一個先進先出的隊列,可以保證每次只能有一個消息被接收,所有其它待接收的都需要排隊等待。

2. 這裡引入了分布式項目的思想,前台網站只復制新增索引項和查詢,MQ負責消息的接收和推送,WinServices負責生成索引文件。

3. 因為還只是Demo,所以很多功能還不完善,離真正企業級應用還有很大的差距,目的只是想練練手,熟悉下相關的知識點。

 

流程圖:

 

架構圖:

 

層次圖:

 

項目結構:

LuceneTest.Entity:定義索引項和查詢結果類的類庫

LuceneTest.MQ:封裝消息隊列(ActiveMQ)發送和接收功能的類庫

LuceneTest.Web:用於管理索引項和查詢的MVC工程

LuceneTest.WinService.Test:用於WinService測試的WinForm工程

LuceneTest.SearchEngine:封裝Lucene.Net的創建索引和根據關鍵字查詢的類庫

 

關鍵代碼片段:

 1         /// <summary>
 2         /// 創建索引
 3         /// </summary>
 4         /// <param name="model"></param>
 5         public void CreateIndex(IndexSet model)
 6         {
 7             //打開 索引文檔保存位置
 8             var directory = FSDirectory.Open(new DirectoryInfo(this._indexPath), new NativeFSLockFactory());
 9             //IndexReader:對索引庫進行讀取的類
10             var isExist = IndexReader.IndexExists(directory);
11 
12             if (isExist)
13             {
14                 //如果索引目錄被鎖定(比如索引過程中程序異常退出或另一進程在操作索引庫),則解鎖
15                 if (IndexWriter.IsLocked(directory))
16                     //手動解鎖
17                     IndexWriter.Unlock(directory);
18             }
19 
20             //創建向索引庫寫操作對象,IndexWriter(索引目錄,指定使用盤古分詞進行切詞,最大寫入長度限制)
21             //補充:使用IndexWriter打開directory時會自動對索引庫文件上鎖
22             var writer = new IndexWriter(directory, new PanGuAnalyzer(), !isExist, IndexWriter.MaxFieldLength.UNLIMITED);
23             //新建文檔對象,一條記錄對應索引庫中的一個文檔
24             var document = new Document();
25 
26             //向文檔中添加字段
27             //所有字段的值都將以字符串類型保存,因為索引庫只存儲字符串類型數據
28 
29             //Field.Store:是否存儲原文:
30             //Field.Store.YES:存儲原值(如顯示原內容必須為YES),可以用document.Get取出原值
31             //Field.Store.NO:不存儲原值
32             //Field.Store.COMPRESS:壓縮存儲
33 
34             //Field.Index:是否創建索引:
35             //Field.Index.NOT_ANALYZED:不創建索引 
36             //Field.Index.ANALYZED:創建索引(利於檢索)
37 
38             //WITH_POSITIONS_OFFSETS:指示不僅保存分割後的詞,還保存詞之間的距離
39             document.Add(new Field("title", model.Title, Field.Store.YES, Field.Index.ANALYZED, Field.TermVector.WITH_POSITIONS_OFFSETS));
40             document.Add(new Field("content", model.Content, Field.Store.YES, Field.Index.ANALYZED, Field.TermVector.WITH_POSITIONS_OFFSETS));
41 
42             //文檔寫入索引庫
43             writer.AddDocument(document);
44 
45             //會自動解鎖
46             writer.Close();
47             //不要忘了Close,否則索引結果搜不到
48             directory.Close();
49         }
        /// <summary>
        /// 查詢
        /// </summary>
        /// <param name="keyWord"></param>
        /// <returns></returns>
        public List<SearchResult> Search(string keyWord)
        {
            var searchResultList = new List<SearchResult>();

            //打開 索引文檔保存位置
            var directory = FSDirectory.Open(new DirectoryInfo(this._indexPath), new NoLockFactory());
            //IndexReader:對索引庫進行讀取的類
            var reader = IndexReader.Open(directory, true);

            //關鍵詞分詞
            var words = this.SplitWords(keyWord);
            //搜索條件
            var query = new PhraseQuery();

            foreach (var item in words)
            {
                query.Add(new Term("content", item));
            }

            //指定關鍵詞相隔最大距離
            query.SetSlop(100);

            //TopScoreDocCollector:存放查詢結果的容器
            var collector = TopScoreDocCollector.create(1000, true);

            //IndexReader:對索引庫進行查詢的類
            var searcher = new IndexSearcher(reader);
            //根據query查詢條件進行查詢,查詢結果放入collector容器
            searcher.Search(query, null, collector);

            //TopDocs:指定0到GetTotalHits(),即所有查詢結果中的文檔,如果TopDocs(20,10)則意味著獲取第20-30之間文檔內容,達到分頁的效果
            var docs = collector.TopDocs(0, collector.GetTotalHits()).scoreDocs;

            foreach (var item in docs)
            {
                var searchResult = new SearchResult();

                //得到查詢結果文檔的id(Lucene內部分配的id)
                var docId = item.doc;
                //根據文檔id來獲得文檔對象Document
                var doc = searcher.Doc(docId);

                searchResult.Id = docId;
                searchResult.Title = doc.Get("title");
                //高亮顯示
                searchResult.Content = this.HightLight(keyWord, doc.Get("content"));

                searchResultList.Add(searchResult);
            }

            return searchResultList;
        }

查詢頁面View

@{
    ViewBag.Title = "Search";
}

<h2>Search List</h2>

@using (Html.BeginForm("Search", "IndexMgr"))
{
    <div>
        關鍵字:
    </div>
    <div>
        @Html.TextBox("keyWord")
    </div>   
    
    <input type="submit" value="保存" />
}

@{
    var list = this.ViewBag.SearchResultList;

    if (list != null)
    {
        foreach (var item in list)
        {
    @Html.Raw("標題:" + item.Title)
    <br />
    @Html.Raw("內容:" + item.Content)
    <hr />
        }
    }
}

 

注意事項:

1. 如果使用盤古分詞算法,以下文件的“復制到輸出目錄”需要選擇“如果較新則復制”

    

2. 本Demo的索引文件保存在WinServices的可執行目錄(bin\Debug\IndexData)下面,所以前台網站要查詢,需要配置索引文件的路徑。

 

運行效果圖: 

1. 新增索引項

    

2. 查詢

    

 

參考文獻:

http://www.cnblogs.com/jiekzou/p/4364780.html

http://www.cnblogs.com/piziyimao/archive/2013/01/31/2887072.html

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved