前言:

我之前没有接触过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

MVC+MQ+WinServices+Lucene.Net的更多相关文章

  1. MVC+MQ+WinServices+Lucene.Net Demo

    前言: 我之前没有接触过Lucene.Net相关的知识,最近在园子里看到很多大神在分享这块的内容,深受启发.秉着“实践出真知”的精神,再结合公司项目的实际情况,有了写一个Demo的想法,算是对自己能力 ...

  2. 瞎折腾之 Lucene.Net + MVC 搜索功能(上)

    前言 首先,关于Lucene.Net 的文章已经很多了.我这次决定写出来只是为了练练手,虽然在别人看来没什么用,但是自己确实是手动实践了一把.我个人觉得还是有意义的.爱折腾.敢于实践.才能有所收获,才 ...

  3. 【转】Spring mvc集成ZBUS--轻量级MQ、RPC、服务总线

    本文转自:http://www.cnblogs.com/top15from/p/4899954.html ZBUS = MQ + RPC + PROXY 支持消息队列, 发布订阅, RPC, 代理(T ...

  4. asp.net(mvc) 框架

    1.NFine mvc+ef 2.Grove orm架构 3.NHibernate orm 4.NBear 5.petshop 6.Membership 7.Brnshop 网上商城 8.cms快速开 ...

  5. lucene+IKAnalyzer实现中文纯文本检索系统

    首先IntelliJ IDEA中搭建Maven项目(web):spring+SpringMVC+Lucene+IKAnalyzer spring+SpringMVC搭建项目可以参考我的博客 整合Luc ...

  6. 借助 Lucene.Net 构建站内搜索引擎(下)

    前言:上一篇我们学习了Lucene.Net的基本概念.分词以及实现了一个最简单的搜索引擎,这一篇我们开始开发一个初具规模的站内搜索项目,通过开发站内搜索模块,我们可以方便地在项目中集成站内搜索功能.本 ...

  7. Asp.Net MVC 分页、检索、排序整体实现

    很多时候需要这样的功能,对表格进行分页.排序和检索.这个有很多实现的方式,有现成的表格控件.用前端的mvvm,用户控件.但很多时候看着很漂亮的东西你想进一步控制的时候却不那么如意.这里自己实现一次,功 ...

  8. jieba.NET与Lucene.Net的集成

    首先声明:我对Lucene.Net并不熟悉,但搜索确实是分词的一个重要应用,所以这里还是尝试将两者集成起来,也许对你有一参考. 看到了两个中文分词与Lucene.Net的集成项目:Lucene.Net ...

  9. 4、ASP.NET MVC入门到精通——NHibernate构建一个ASP.NET MVC应用程序

    下周就去办理离职手续了,之前没有使用过NHibernate,只知道NHibernate是一种ORM框架,但是听说新公司是使用NHibernate在做项目,所以,我就网上找资料学习一下NHibernat ...

随机推荐

  1. mybatis 并发问题解决,参考hibernate

    时候操作同一账户就是典型的样例. 比方A.B操作员同一时候读取一剩余金额为1000元的账户,A操作员为该账户添加100元.B操作员同一时候为该账户减去 50元.A先提交.B后提交. 最后实际账户剩余金 ...

  2. Java中判断字符串是否为数字的五种方法 (转)

    推荐使用第二个方法,速度最快. 方法一:用JAVA自带的函数 public static boolean isNumeric(String str){ for (int i = str.length( ...

  3. POJ 1745 Divisibility (线性dp)

    Divisibility Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 10598   Accepted: 3787 Des ...

  4. C# - is

     Checks if an object is compatible with a given type. An is expression evaluates to true if the pr ...

  5. Android setDisplayOptions 具体的使用说明

    Android有几个地方使用位计算.实例Intent Flags,它们的定义View onMeasure(int widthMeasureSpec, int heightMeasureSpec),并且 ...

  6. Android Studio中导入Android项目StepbyStep

    想把在eclipse的项目导入Android studio,有两种方法,但是我喜欢的是不改变项目文件结构的方法,因为这样可以兼容eclipse. 第一步: 导入的项目不能运行,需要配置运行环境.And ...

  7. 网络语音视频技术浅议 Visual Studio 2010(转)

    我们在开发实践中常常会涉及到网络语音视频技术.诸如即时通讯.视频会议.远程医疗.远程教育.网络监控等等,这些网络多媒体应用系统都离不开网络语音视频技术.本人才疏学浅,对于网络语音视频技术也仅仅是略知皮 ...

  8. 于ubuntu配置hadoop当问题

    1. 构造ssh登录 我们并不需要改变/etc/ssh/sshd_config 2. 新hadoop用户,home下列不hadoop夹 创建使用下面的命令 useradd -m hadoop 3. n ...

  9. 新的学生说说我是如何靠APP发展赢得了亿万

    郝萌主倾心贡献,尊重作者的劳动成果,请勿转载. 假设文章对您有所帮助,欢迎给作者捐赠.支持郝萌主,捐赠数额任意.重在心意^_^ 我要捐赠: 点击捐赠 Cocos2d-X源代码下载:点我传送 14年踏入 ...

  10. 一个MP3播放的插件jPlayer

    Jplayer小样   最近应公司要求需要一个MP3播放的插件,网上找了很多插件,看来看去还是jPlayer用着最舒服也最容易扩展.所以就找了个资料研究了下,简单做了个小DEMO.支持实时控制列表,常 ...