项目中可能需要再次用到Lucene.Net,利用空闲时间写了个demo,主要涉及到索引的创建、删除、更新和一个简单查询。在本文示例中,Lucene.Net的版本是2.4.0,某些类和方法与最新版本或者较旧的版本有较多不同,希望您阅读顺利。

一、简单认识索引

Lucene.Net的应用相对比较简单。一段时间以来,我最多只是在项目中写点代码,利用一下它的类库而已,对很多名词术语不是很清晰,甚至理解可能还有偏差。从我过去的博客你也可以看出,语言表达一直不是个人所长,就算”表达“了也有大面积抄书的嫌疑,所以很多概念性的介绍能省则省(除非特别有别要说明),希望有心的初学者注意,理清概念和辨别技术名词非常重要,请参考相关文档。

Lucene的索引由1或多个segment(片段)构成,一个segment由多个document构成,一个document又由1个或多个field构成,一个field又由一个或多个term构成。下面这张图可以说明一切:

从图中不难看出,Lucene的索引是一个由点到线,由线到面的组成结构,这一点我们可以通过查看Lucene生成的索引文件看出来。

参考图片来源: http://alone2004.spaces.live.com/blog/cns!C2525069080D7BB!675.entry

二、创建、优化、删除和更新索引实践

备注:在解决方案所在文件夹中,有一个测试用的Resource文件夹,内有4个.txt文件。我在本地测试的时候,就使用了Resource下的四个文本文件。

1、索引保存至文件

(1)、创建索引

先初始化一个IndexModifier对象,然后执行创建索引的核心方法:

        /// <summary>/// 给txt文件创建索引/// </summary>/// <param name="file"></param>/// <param name="modifier"></param>private void IndexFile(FileInfo file, IndexModifier modifier){try{Document doc = new Document();//创建文档,给文档添加字段,并把文档添加到索引书写器里SetOutput("正在建立索引,文件名:" + file.FullName);doc.Add(new Field("id", id.ToString(), Field.Store.YES, Field.Index.TOKENIZED));//存储且索引id++;/* filename begin */doc.Add(new Field("filename", file.FullName, Field.Store.YES, Field.Index.TOKENIZED));//存储且索引//doc.Add(new Field("filename", file.FullName, Field.Store.YES, Field.Index.UN_TOKENIZED));//doc.Add(new Field("filename", file.FullName, Field.Store.NO, Field.Index.TOKENIZED));//doc.Add(new Field("filename", file.FullName, Field.Store.NO, Field.Index.UN_TOKENIZED));/* filename end *//* contents begin *///doc.Add(new Field("contents", new StreamReader(file.FullName, System.Text.Encoding.Default)));string contents = string.Empty;using (TextReader rdr = new StreamReader(file.FullName, System.Text.Encoding.Default)){contents = rdr.ReadToEnd();//将文件内容提取出来doc.Add(new Field("contents", contents, Field.Store.YES, Field.Index.TOKENIZED));//存储且索引//doc.Add(new Field("contents", contents, Field.Store.NO, Field.Index.TOKENIZED));//不存储索引}/* contents end */modifier.AddDocument(doc);}catch (FileNotFoundException fnfe){}}

最后,IndexModifier对象执行Close方法。

几个注意点:

a、IndexModifier类封装了平时经常使用的IndexWriter和IndexReader,而且不用我们额外考虑多线程;

b、StandardAnalyzer是经常使用的一个Analyzer,目前对中文分词支持的也还不错(大名鼎鼎的盘古分词请参考牛人eaglet的这几篇);

c、IndexModifier的Optimize方法的执行可以优化索引文件,但是比较耗时间,根据我的测试,索引文件越大,优化时间线性增加,所以实际的开发中这个方法我们都会按照一定的策略执行;

d、IndexModifier的Close方法必须执行,否则你所做的一切都是无用功。

(2)、按照id删除一条索引

代码相对而言非常简单,直接利用IndexModifier 的DeleteDocuents方法:

            Directory directory = FSDirectory.GetDirectory(INDEX_STORE_PATH, false);IndexModifier modifier = new IndexModifier(directory, new StandardAnalyzer(), false);Term term = new Term("id", id);modifier.DeleteDocuments(term);//删除  modifier.Close();directory.Close();

其中,IndexModifier还有一个方法DeleteDocument,它的参数是整数docNum,通常我们也不知道索引文件的内部docNum是多少,所以非常少用它。

(3)、按照id更新一条索引

贴一下主要方法:

            bool enableCreate = IsEnableCreated();//是否已经创建索引文件Term term = new Term("id", id);Document doc = new Document();doc = new Document();//创建文档,给文档添加字段,并把文档添加到索引书写器里doc.Add(new Field("id", id, Field.Store.YES, Field.Index.TOKENIZED));//存储且索引doc.Add(new Field("filename", filename, Field.Store.YES, Field.Index.TOKENIZED));doc.Add(new Field("contents", filename, Field.Store.YES, Field.Index.TOKENIZED));LuceneIO.Directory directory = LuceneIO.FSDirectory.GetDirectory(INDEX_STORE_PATH, enableCreate);IndexWriter writer = new IndexWriter(directory, new StandardAnalyzer(),IndexWriter.MaxFieldLength.LIMITED);writer.UpdateDocument(term, doc);writer.Optimize();//writer.Commit();writer.Close();directory.Close();

需要注意,这一次,我们使用了IndexWriter对象的UpdateDocument方法,而IndexModifier没有找到现成的UpdateDocument方法。Optimize通常需要执行一下,否则索引文件中会有两个相同id的索引。

2、索引保存至内存

如果1你已经理解了,2其实可以不用细究。在IndexModifier的构造函数里有一个重载:

 public IndexModifier(Directory directory, Analyzer analyzer, bool create);

下面的示例代码中第一个参数RAMDirectory就是一个Directory,我们可以把它定义成静态,创建索引的时候就完成了保存至内存的效果:

private static RAMDirectory ramDir = null;
IndexModifier  modifier = new IndexModifier(ramDir, new StandardAnalyzer(), true);

经测试,增删改查原理同1。

3、利用Lucene.Net配合数据库查询

平时开发中,对于数据库中的海量数据,频繁读库可能不能满足效率和速度的需求。我们也可以利用Lucene.Net配合数据库快速查询结果。至于如何对数据库利用Lucene.Net创建索引,增删改查和同1中的介绍是一模一样的。比如本文demo中创建索引的实现,取前1000个人对他们的Id和姓名进行索引。在编码之前,我先往Person表中插入了一些数据:

INSERT Person(FirstName,LastName,Weight,Height) VALUES('明','姚',200,223)
INSERT Person(FirstName,LastName,Weight,Height) VALUES('建联','易',180,213)
INSERT Person(FirstName,LastName,Weight,Height) VALUES('德科','诺维斯基',180,211)
INSERT Person(FirstName,LastName,Weight,Height) VALUES('德怀特','霍华德',190,218)
INSERT Person(FirstName,LastName,Weight,Height) VALUES('约什','霍华德',178,197)
INSERT Person(FirstName,LastName,Weight,Height) VALUES('蒂姆','邓肯',183,211)
INSERT Person(FirstName,LastName,Weight,Height) VALUES('凯文','加内特',182,215)
INSERT Person(FirstName,LastName,Weight,Height) VALUES('德隆','威廉姆斯',166,197)

接着先取出1000个人:

string sql = "SELECT TOP 1000 Id,FirstName,LastName FROM Person(NOLOCK)";
IList<Person> listPersons = EntityConvertor.QueryForList<Person>(sql, strSqlConn, null);

然后建立索引即可:

     private void IndexDB(IndexModifier modifier,IList<Person> listModels){SetOutput(string.Format("正在建立数据库索引,共{0}人",listModels.Count));foreach (Person item in listModels){Document doc = new Document();//创建文档,给文档添加字段,并把文档添加到索引书写器里doc.Add(new Field("id", item.Id.ToString(), Field.Store.YES, Field.Index.TOKENIZED));//存储且索引doc.Add(new Field("fullname", string.Format("{0} {1}",item.FirstName,item.LastName), Field.Store.YES, Field.Index.TOKENIZED));//存储且索引modifier.AddDocument(doc);}}

同样的道理,最后我们也执行这两个方法(Optimize方法不是一定要做的):

  modifier.Optimize();//优化索引modifier.Close();//关闭索引读写器

三、搜索

本文示例代码中的搜索都是利用Lucene.Net的IndexSearcher默认的比较直接简单的一个搜索方法 Search(Query query, Filter filter, int n),很多重载方法我也没有使用过:

    /// <summary>/// 根据索引搜索/// </summary>/// <param name="keyword"></param>/// <returns></returns>private TopDocs Search(string keyword,string field){TopDocs docs = null;int n = 10;//最多返回多少个结果SetOutput(string.Format("正在检索关键字:{0}", keyword));try{QueryParser parser = new QueryParser(field, new StandardAnalyzer());//针对内容查询Query query = parser.Parse(keyword);//搜索内容 contents  (用QueryParser.Parse方法实例化一个查询)Stopwatch watch = new Stopwatch();watch.Start();docs = searcher.Search(query, (Filter)null, n); //获取搜索结果watch.Stop();StringBuffer sb = "索引完成,共用时:" + watch.Elapsed.Hours + "时 " + watch.Elapsed.Minutes + "分 " + watch.Elapsed.Seconds + "秒 " + watch.Elapsed.Milliseconds + "毫秒";SetOutput(sb);}catch (Exception ex){SetOutput(ex.Message);docs = null;}return docs;}/// <summary>/// 显示搜索结果/// </summary>/// <param name="queryResult"></param>private void ShowFileSearchResult(TopDocs queryResult){if (queryResult == null || queryResult.totalHits == 0){SetOutput("Sorry,没有搜索到你要的结果。");return;}int counter = 1;foreach (ScoreDoc sd in queryResult.scoreDocs){try{Document doc = searcher.Doc(sd.doc);string id = doc.Get("id");//获取idstring fileName = doc.Get("filename");//获取文件名string contents = doc.Get("contents");//获取文件内容string result = string.Format("这是第{0}个搜索结果,Id为{1},文件名为:{2},文件内容为:{3}{4}", counter, id, fileName, Environment.NewLine, contents);SetOutput(result);}catch (Exception ex){SetOutput(ex.Message);}counter++;}}

下一篇我会补充介绍一下Lucene.Net常用的搜索、排序和分页,今天偷懒一下。

最后,本文demo中的代码算不上优美,可读性还凑合,希望大家下载之后看看吧,我还在幻想万一对新手能有所帮助,或者引来某个误入的高手指点一二,于人于己那就真是善莫大焉了。

demo下载:LuceneNetApp

参考:

http://www.cnblogs.com/birdshover/category/152283.html

http://lucene.apache.org/lucene.net/

http://lucene.apache.org/lucene.net/docs/

Lucene.Net无障碍学习和使用:索引篇相关推荐

  1. Lucene.Net无障碍学习和使用:搜索篇

    在上一篇中,我们初步理解了索引的增删改查基本操作.本文着重介绍一下常用的搜索,以及搜索结果的排序和分页.本文的搜索主要是基于前一篇介绍的文本文件的索引,建议下载最后改进的demo对照着看阅读本文,同时 ...

  2. access 导入 txt sql语句_从零开始学习 MySQL 系列索引、视图、导入和导出

    阅读本文大概需要 8 分钟 前言上篇文章我们学习了数据库和数据表操作语句,今天我们学习下数据库索引,视图,导入和导出的知识.作为基础篇,不会涉及到关于索引和视图的高级应用和核心概念,但是基本操作大家会 ...

  3. mysql导入dat文件_从零开始学习 MySQL 系列--索引、视图、导入和导出

    前言 上篇文章我们学习了数据库和数据表操作语句,今天我们学习下数据库索引,视图,导入和导出的知识. 作为基础篇,不会涉及到关于索引和视图的高级应用和核心概念,但是基本操作大家会了解,尤其是关于索引的内 ...

  4. Azure IoT Hub和Event Hub相关的技术系列-索引篇

    Azure IoT Hub和Event Hub相关的技术系列,最近已经整理了不少了,统一做一个索引链接,置顶. Azure IoT 技术研究系列1-入门篇 Azure IoT 技术研究系列2-设备注册 ...

  5. Hadoop学习笔记—15.HBase框架学习(基础知识篇)

    Hadoop学习笔记-15.HBase框架学习(基础知识篇) HBase是Apache Hadoop的数据库,能够对大型数据提供随机.实时的读写访问.HBase的目标是存储并处理大型的数据.HBase ...

  6. bcp out 带列名导出_从零开始学习 MySQL 系列索引、视图、导入和导出

    阅读本文大概需要 8 分钟 前言上篇文章我们学习了数据库和数据表操作语句,今天我们学习下数据库索引,视图,导入和导出的知识.作为基础篇,不会涉及到关于索引和视图的高级应用和核心概念,但是基本操作大家会 ...

  7. Redis学习笔记1-理论篇

    目录 1,Redis 数据类型的底层结构 1.1,Redis 中的数据类型 1.2,全局哈希表 1.3,数据类型的底层结构 1.4,哈希冲突 1.5,rehash 操作 2,Redis 的 IO 模型 ...

  8. 【面试准备】MySQL索引篇

    [面试准备]1.MySQL索引篇 引言: 什么是索引? MySQL索引的结构有那些?并简单的介绍. B+tree在innodb与myisam存储引擎中有何区别? 为什么b+tree在innodb为存储 ...

  9. 学习MyBatis3这一篇就够了

    目录 第一章 MyBatis3概述 1.1.概述 1.2.特点 1.3.对比 1.4.官网 1.5.下载 第二章 MyBatis3的增删改查 2.1.环境准备 2.2.创建工程 2.3.导入依赖 2. ...

最新文章

  1. python pandas DataFrame 排序
  2. java jfreechart下载_jfreechart下载-JFreeChart下载安装[java图表插件]-PC下载网
  3. java thread 内存泄露_Java ThreadLocal 内存泄露问题分析及解决方法。
  4. linux 中 ~/.和$
  5. 欧几里德算法求最大公约数
  6. 我的女朋友漏电了–论C++中的失败(failure),缺陷(bug)和异常(exception)
  7. python动态映射_sqlalchemy动态映射
  8. 用js实现鼠标点击爱心特效
  9. 一加8渲染图曝光:后置三摄/五种配色可选
  10. 屏保壁纸引发血案,三星手机瞬间变砖
  11. 关于C语言的指针、链表的原理和各类操作
  12. 在Ubuntu上为Android系统内置C可执行程序测试Linux内核驱动程序
  13. 【sklearn第九讲】支持向量机之分类篇
  14. 数商云医药行业SCM供应链管理系统应用场景、运用模式
  15. 如何在ppt中生成柱状图_Excel表格数据如何生成柱状图等-EXCEL如何制作柱状图?...
  16. win7蓝屏_0x0000007e蓝屏代码怎么回事?Win7蓝屏0x0000007e解决方法
  17. convert 函数的使用
  18. 如何进行计算机系统安装教程,电脑怎么重装系统:系统安装教程
  19. Chronometer实现计时器 开始、暂停、停止功能
  20. vmware之设置共享文件夹

热门文章

  1. vue实现多个元素或多个组件之间动画效果
  2. 高频数据交换下Flutter与ReactNative的对比
  3. JakartaEE Exception: Invalid bound statement (not found): com.mazaiting.blog.dao.UserDao.selectUs...
  4. Azure Logic Apps正式发布
  5. 案例:Oracle dul数据挖掘 磁盘损坏dul提取数据文件中表的数据及l
  6. flume-hdfs 按照时间关闭并新开文件
  7. 马年计划2014-2-21
  8. Windows PowerShell 批量迁移Windows用户信息
  9. WCF客户端不能用在Using语句块中,因为它可能会抛出不可预知的异常。即使你捕获了异常,仍有可能一直保持连接。...
  10. 狼奔代码生成工具使用心得