当前位置: 首页 > 编程日记 > 正文

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

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

一、简单认识索引

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

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

lucenenetindex

从图中不难看出,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/

相关文章:

【ubuntu】vim中鼠标选中时变成 可视模式,不能复制的解决方法

1、问题描述 配置好vim后&#xff0c;打开一个文件&#xff0c;鼠标选中文本时&#xff0c;选中的内容变成可视模式。 可视模式&#xff0c;不能将选中内容复制到剪切板 2、解决方法 在用户根目录下&#xff0c;打开 .vimrc ~$ vi .vimrc 将set mousea 删除或注释掉 3、我的…

Ruby11 拾遗

Agenda LoopExpressionFile Read/WriteDebugProcess & ThreadLoop while a 10 while a > 0puts aa - 1 enduntil a 100until a 0puts aa - 1 endloop a 10loop dobreak if a < 0puts aa - 1 end循环控制 breaknextbreak for x in 1..10break if x 5puts x endne…

CVPR2019|微软、中科大开源基于深度高分辨表示学习的姿态估计算法

作者 | 周强&#xff08;CV君&#xff09; 来源 | 我爱计算机视觉&#xff08;公众号id&#xff1a;aicvml&#xff09; 如何挑战百万年薪的人工智能&#xff01; https://edu.csdn.net/topic/ai30?utm_sourcecsdn_bw 昨天arXiv出现了好几篇被CVPR 2019接收的论文。 其中来自…

本地连接受限制或无法连接怎么办?

一个非常常见的问题&#xff0c;就是我们家用电脑安装宽带后&#xff0c;任务栏上的“本地连接”图标有一个的叹号。查看状态&#xff1a;“受限制或无连接”&#xff0c;点“修复”却无法修复&#xff0c;显示无法获取IP地址&#xff0c;获得私网地址&#xff01;但ADSL又可以…

糟心!苹果无人车裁员190人,程序员却首当其冲

整理 | 琥珀 出品 | AI科技大本营&#xff08;ID:rgznai100&#xff09; 60s测试&#xff1a;你是否适合转型人工智能&#xff1f; https://edu.csdn.net/topic/ai30?utm_sourcecxrs_bw 仅过了一个多月&#xff0c;苹果公司自动驾驶部门裁员的真相便已浮出水面&#xff0c;该…

【工具】Internet Download Manager( IDM )抓取站点

软件说明&#xff1a; 扒网站的好东西&#xff0c;免费&#xff0c;支持中文 下载地址&#xff08;官网&#xff09; http://www.internetdownloadmanager.com/ 使用方法 https://jingyan.baidu.com/article/a681b0de0be10b3b1943465d.html

2010-12-30

I want to invest in stocks to make a quick buck.我想炒股&#xff0c;尽快赚点儿钱花&#xff11;、Which company are you going to invest in?那你打算买什么股&#xff1f;&#xff12;、Dont put all your eggs in one basket造成不要孤注一掷。&#xff13;、You need…

PFLD:简单、快速、超高精度人脸特征点检测算法

作者 | 周强&#xff08;CV君&#xff09; 来源 | 我爱计算机视觉&#xff08;公众号id&#xff1a;aicvml&#xff09; 60s测试&#xff1a;你是否适合转型人工智能&#xff1f; https://edu.csdn.net/topic/ai30?utm_sourcecxrs_bw 什么样的算法才是好算法&#xff1f; 真…

Cookie实现记住密码、自动登录

前端代码 <form id"form" action"xxx" method"post"><div><input type"text" name"account" id"account" placeholder"账号"><input type"text" name"pwd" i…

【Ubuntu】VirtualBox+ubuntu中显示摄像头

1、下载插件 https://www.virtualbox.org/wiki/Downloads 2、修改下载的插件的后缀 将后缀名改为vbox-extpack 如下载的插件为Oracle_VM_VirtualBox_Extension_Pack-5.2.14.txt&#xff0c;改为Oracle_VM_VirtualBox_Extension_Pack-5.2.14.vbox-extpack 3、安装插件 点击…

以SIGSEGV为例详解信号处理(与栈回溯)

以SIGSEGV为例详解信号处理(与栈回溯) 信号是内核提供的向用户态进程发送信息的机制, 常见的有使用SIGUSR1唤醒用户进程执行子程序或发生段错误时使用SIGSEGV保存用户错误现场. 本文以SIGSEGV为例, 详细分析信号使用方法, 内核信号的发送与接收机制. 1. 信号处理例程 以下是一…

十个jQuery图片画廊插件推荐

2019独角兽企业重金招聘Python工程师标准>>> jQuery的画廊插件可以将分组图像和多媒体资料转成类似Flash的图像或照片。当幻灯片已经成为网站的重要组成部分&#xff0c;jQuery的重要性不能被忽视。下面为你介绍了10个最有美感&#xff0c;创新性和创造性的jQuery图…

总结机器学习优质学习文章Top50!

整理 | Jane出品 | AI科技大本营&#xff08;公众号id&#xff1a;rgznai100&#xff09;在过去一年里&#xff0c;我们每个月都会给大家推荐一些优质的、最新的机器学习研究成果或机器学习技术文章&#xff0c;很多文章是从近千篇文章中评选出来的。综合考虑这些文章的更新时间…

【Qt】ubuntu QtCreator的pro文件中使用pkg-config

试验环境 ubuntu14.04.5 opencv2.4.13.6 Qt Creator 4.0.3 Qt 5.6 使用方法 在pro文件中&#xff0c;添加opencv头文件路径 INCLUDEPATH /usr/local/opencv2.4.13.6/include 添加pkg-config –libs opencv CONFIG link_pkgconfig PKGCONFIG opencv 可以在命令行下…

弯曲评论上关于SOC的一段文章【整理】

近几天&#xff0c;弯曲评论上针对一个文章进行了热烈的讨论。这个文章本无关乎SOC&#xff0c;不过&#xff0c;有业界同仁willchen将话题引到了SOC上&#xff0c;并发表了一番言论&#xff0c;整理如下&#xff08;BTW&#xff0c;看着willchen的文字&#xff0c;让我想到了s…

C# richtextbox 自动下拉到最后 方法 RichTextBox读取txt中文后出现乱码

C# richtextbox 自动滚动到最后 光标到最后 自动显示最后一行 private void richTextBox1_TextChanged(object sender, EventArgs e) { richTextBox1.SelectionStart richTextBox1.TextLength; // Scrolls the contents of the control to the current car…

【Qt】在Qt中使用opencv,不要使用opencv创建窗口

问题描述 在ubuntu14.04.5 Qt5.6中使用opencv创建窗口显示摄像头时&#xff0c;报错&#xff1a; (:1103): Gtk-WARNING **: gtk_disable_setlocale() must be called before gtk_init() (:1103): GLib-CRITICAL **: Source ID 52 was not found when attempting to remove …

曝贾扬清第二跳,加入阿里!达摩院或将承载中国下一个AI愿景?

整理 | Jane出品 | AI科技大本营&#xff08;公众号id&#xff1a;rgznai100&#xff09;无论是国外还是国内&#xff0c;AI 界的人才动向一直是大家关注的焦点&#xff0c;从 2017 年3 月&#xff0c;吴恩达离职百度&#xff0c;开启创业之路&#xff1b;2018 年 9 月&#xf…

excel表格出问题了

我excel文件出问题&#xff0c;所有sheet表的N2表格内容自动替换成了几个固定的文字&#xff0c;出现两次了&#xff0c;第一次我手工改回来了&#xff0c;这有出现了第二次&#xff0c;我不知道是病毒原因&#xff0c;还是其他原因&#xff0c;应该不是我误操作&#xff0c;一…

阿里云凌晨大规模宕机,华北部分网站陷入瘫痪

整理 | 琥珀出品 | AI科技大本营&#xff08;ID:rgznai100&#xff09;今天凌晨&#xff0c;有不少网友反馈称阿里云疑似出现了宕机故障。据消息称&#xff0c;北京时间 2019 年 3 月 2 日 23:55 分左右开始&#xff0c;监控发现华北 2 地域部分 ECS 实例及部分 EMR、RDS on EC…

java8中的时间处理6 - 格式化

下面是格式化和解析的粒子&#xff1a; LocalDate date LocalDate.now();System.out.println(date);System.out.println(date.format(DateTimeFormatter.ofPattern("d::MMM::uuuu")));System.out.println(date.format(DateTimeFormatter.BASIC_ISO_DATE));LocalDate…

【Qt】ubuntu14.04.5 qt5.6中使用opencv3.4报错:Using GTK+ 2.x and GTK+ 3 in the same process is not supported

问题描述 ubuntu14.04.5 qt5.6中使用opencv3.4报错&#xff1a; Using GTK 2.x and GTK 3 in the same process is not supported 原因 Qt5.6使用的GTK2.x&#xff0c;opencv3.4安装时使用的GTK3。 解决方法 1、降低opencv版本&#xff0c;将opencv3.4更换为opencv2.4 这…

mysql 开启慢查询命令【转】

以MySQL 5.1.36为例&#xff1a;在slow_query_log &#xff08;注意log_slow_querys参数已经废弃&#xff09;值为ON的情况下&#xff08;默认为OFF&#xff09;&#xff0c;当一条SQL语句执行的时间超过了 long_query_time 预设的时间&#xff08;默认为10s&#xff0c;同时精…

不用开着电脑,如何将脚本代码放到服务器上?

作者 | sergiojune 转载自公众号日常学python&#xff08;ID:daily_learn&#xff09; 程序员转行学什么语言&#xff1f; https://edu.csdn.net/topic/ai30?utm_sourcecsdn_bw 上次弄了个机器人&#xff0c;反响挺不错的&#xff0c;我也挺满意的&#xff0c;因为这个我也初…

【Qt】ubuntu14.04+qt5.6+opencv2.4编程注意事项

cv::imshow();//不能在QT中使用该接口来创建窗口 否侧会报错&#xff1a; (:1103): Gtk-WARNING **: gtk_disable_setlocale() must be called before gtk_init() (:1103): GLib-CRITICAL **: Source ID 52 was not found when attempting to remove it 参见&#xff1a;ht…

Docker完整建站详介

2019独角兽企业重金招聘Python工程师标准>>> 一&#xff0c;在主服务器上操作&#xff1a;安装完成docker;略 二&#xff0c;查看镜像 [rootlocalhost ~]#docker images REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE centos/shop2.web.top latest 8fc9b211671a 14…

word2007启用宏

word选项-》信任中心->启用宏

用Python分析《工作细胞》的一万多条评论后,非漫迷也要入番了

作者 | 量化小白一枚&#xff0c;上财研究生在读&#xff0c;专注于数据分析与量化投资 来源 | 量化小白上分记&#xff08;公众号 id&#xff1a;quanthzp&#xff09; 责编 | Jane 人工智能的现状及今后发展趋势如何&#xff1f; https://edu.csdn.net/topic/ai30?utm_so…

【VritualBox】虚拟机VirtualBox中ubuntu下加载USB设备报错:busy with a previous request.Plaease try again later

1、问题描述 在VirtualBox中添加USB设备时&#xff0c;报错&#xff1a; USB device with ‘SanDisk Crurer Blade’UUID {*} is busy with a previous request.Plaease try again later 返回 代码: E_INVALIDARG(0x80070057) 组件: HostUSBDeviceWrap 界面&#xff1a; I…

谈谈动态规划的思想

动态规划&#xff08; dynamic programming &#xff09;算法是解决多阶段决策过程最优化问题的一种常用方法&#xff0c;难度比较大&#xff0c;技巧性也很强。利用动态规划算法&#xff0c;可以优雅而高效地解决很多贪婪算法或分治算法不能解决的问题。动态规划算法的基本思想…