前言:

我之前没有接触过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的创建索引和根据关键字查询的类库

关键代码片段:

         /// <summary>
/// 创建索引
/// </summary>
/// <param name="model"></param>
public void CreateIndex(IndexSet model)
{
//打开 索引文档保存位置
var directory = FSDirectory.Open(new DirectoryInfo(this._indexPath), new NativeFSLockFactory());
//IndexReader:对索引库进行读取的类
var isExist = IndexReader.IndexExists(directory); if (isExist)
{
//如果索引目录被锁定(比如索引过程中程序异常退出或另一进程在操作索引库),则解锁
if (IndexWriter.IsLocked(directory))
//手动解锁
IndexWriter.Unlock(directory);
} //创建向索引库写操作对象,IndexWriter(索引目录,指定使用盘古分词进行切词,最大写入长度限制)
//补充:使用IndexWriter打开directory时会自动对索引库文件上锁
var writer = new IndexWriter(directory, new PanGuAnalyzer(), !isExist, IndexWriter.MaxFieldLength.UNLIMITED);
//新建文档对象,一条记录对应索引库中的一个文档
var document = new Document(); //向文档中添加字段
//所有字段的值都将以字符串类型保存,因为索引库只存储字符串类型数据 //Field.Store:是否存储原文:
//Field.Store.YES:存储原值(如显示原内容必须为YES),可以用document.Get取出原值
//Field.Store.NO:不存储原值
//Field.Store.COMPRESS:压缩存储 //Field.Index:是否创建索引:
//Field.Index.NOT_ANALYZED:不创建索引
//Field.Index.ANALYZED:创建索引(利于检索) //WITH_POSITIONS_OFFSETS:指示不仅保存分割后的词,还保存词之间的距离
document.Add(new Field("title", model.Title, Field.Store.YES, Field.Index.ANALYZED, Field.TermVector.WITH_POSITIONS_OFFSETS));
document.Add(new Field("content", model.Content, Field.Store.YES, Field.Index.ANALYZED, Field.TermVector.WITH_POSITIONS_OFFSETS)); //文档写入索引库
writer.AddDocument(document); //会自动解锁
writer.Close();
//不要忘了Close,否则索引结果搜不到
directory.Close();
}
        /// <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(); //TopScoreDocCollector:存放查询结果的容器
var collector = TopScoreDocCollector.create(, 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(, 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 Demo的更多相关文章

  1. MVC+MQ+WinServices+Lucene.Net

    MVC+MQ+WinServices+Lucene.Net Demo 前言: 我之前没有接触过Lucene.Net相关的知识,最近在园子里看到很多大神在分享这块的内容,深受启发.秉着“实践出真知”的精 ...

  2. ASP.NET MVC+JQueryEasyUI1.4+ADO.NET Demo

    1.JQueryEasyUI使用 JQuery EasyUI中文官网:http://www.jeasyui.net/ JQuery EasyUI中文官网下载地址:http://www.jeasyui. ...

  3. ECharts 初识(基于MVC+jQuery+Angularjs实现的Demo)

    一.背景:      我们这行做web开发的,很多时候都需要做数据统计报表,现在我所使用的是来自百度团队的ECharts.官方网址:http://echarts.baidu.com/      我们知 ...

  4. EasyUI+MVC+EF简单用户管理Demo(问题及解决)

    写在前面 iframe-src EntityFramework版本 connectionStrings View.Action.页面跳转 EasyUI中DataGrid绑定 新增.修改和删除数据 效果 ...

  5. lucene 索引 demo

    核心util /** * Alipay.com Inc. * Copyright (c) 2004-2015 All Rights Reserved/ */ package com.lucene.de ...

  6. MVC 4 Razor Design Sample Demo Project

    This is a demo project in MCV 4 razor design which encompases the general design of MVC pattern. The ...

  7. lucene 搜索demo

    package com.ljq.utils; import java.io.File; import java.util.ArrayList; import java.util.List; impor ...

  8. 1.【转】spring MVC入门示例(hello world demo)

    1. Spring MVC介绍 Spring Web MVC是一种基于Java的实现了Web MVC设计模式的请求驱动类型的轻量级Web框架,即使用了MVC架构模式的思想,将web层进行职责解耦,基于 ...

  9. Spring MVC 学习 之 - 配置简单demo

    1.环境参数: Maven:3.1.1 JDK  :1.6 2.项目文件结构图: 3.各文件配置: 3.1. pom.xml <project xmlns="http://maven. ...

随机推荐

  1. Nuget包之间的依赖

    为什么我们使用依赖呢??原因是某些资源是基于某些资源的基础上才可以运行的,比如bootstrap基于Jquery,EntityFramework.zh-Hans基于EntityFramework,如果 ...

  2. Topcoder SRM570 900 CurvyonRails

    题意:给定一个网格,一些格子是障碍不用管,剩余的格子是城市,你可以修建铁路,铁路的形状可以是直的或者弯的,也就是说可以以这个点为节点连接它四联通的其中两个方块.要求用一个或多个环来覆盖所有城市.对于有 ...

  3. fonts.useso.com 访问变慢

    fonts.useso.com 替换为        fonts.lug.ustc.edu.cn ajax.useso.com   替换为       ajax.lug.ustc.edu.cn the ...

  4. VS一次删除多个窗体后报错

    今天在做项目的时候,需要删除多个窗体,删除了之后,VS报了个错误: 命名空间“项目名称”中不存在类型或命名空间名称“文件夹”(是否缺少程序集引用?) 具体原因不清楚,就算恢复窗体后还是一样的错误,无法 ...

  5. 面试复习(C++)之直接选择排序

    #include <iostream> using namespace std; void Selection(int *a,int len) { ;i<len;i++) { int ...

  6. Spring 通过配置文件注入 properties文件

    当我们需要将某些值放入 properties文件 key=value 的方式,获取文件信息使用spring 注入的方式会变得很便捷 1. spring 配置文件需要导入 <?xml versio ...

  7. windows8.1下javaweb环境搭建及基本配置(jdk+tomcat+eclipse)

    1.下载安装jdk在无空格的路径下,否则在linux下可能出问题.配置环境变量: a.新建系统变量——JAVA_HOME,值——D:\programming\java\jdk8 // win8下若建为 ...

  8. Oracle表解锁语句

    如果你发现无法对一个表进行修改.删除等操作时,你可以利用以下语句查询是否是该表被锁住了 --查询锁select sess.sid,sess.serial#, lo.oracle_username,lo ...

  9. linux基本常用命令列举

    上回装完虚拟机中的linux系统了,进入找到terminal,打开命令行界面 pwd:展示目前为止绝对路径 cd cd~:跳转到/home/yy的位置 cd-:跳转到上一步的位置 cd path(绝对 ...

  10. Android之如何使用JUnit进行单元测试

    转的:http://www.blogjava.net/qileilove/archive/2014/05/19/413824.html Android中如何使用JUnit进行单元测试 在我们日常开发a ...