zoukankan      html  css  js  c++  java
  • 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的可执行目录(binDebugIndexData)下面,所以前台网站要查询,需要配置索引文件的路径。

    运行效果图: 

    1. 新增索引项

        

    2. 查询

        

    参考文献:

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

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

  • 相关阅读:
    Vue实现添加、删除、关键字查询
    打开新页面 自定义方法并获取携带值
    unity3d 刷新速率
    unity3d AssetStore 下载的资源位置
    unity3d c# http 请求json数据解析
    unity3d 自定义载入条/载入动画
    课程改进意见
    梦断代码
    An internal error occurred during: "Launching MVC on Tomcat 7.x".
    n以内的1的个数
  • 原文地址:https://www.cnblogs.com/chenwei1984/p/4383341.html
Copyright © 2011-2022 走看看