传智播客课程——Lucene搜索引擎
Lucene不是一个现成的程序,类似文件搜索程序或web网络爬行器或是一个网站的搜索引擎。Lucene是一个软件库,一个开发工具包,而不是一个具有完整特征的搜索应用程序。它本身只关注文本的索引和搜索。Lucene使你可以为你的应用程序添加索引和搜索能力。目前已经有很多应用程序的搜索功能是基于 Lucene 的,比如 Eclipse 的帮助系统的搜索功能。
Lucene 采用的是一种称为反向索引(inverted index)的机制。反向索引就是说我们维护了一个词/短语表,对于这个表中的每个词/短语,都有一个链表描述了有哪些文档包含了这个词/短语。这样在用户输入查询条件的时候,就能非常快的得到搜索结果。
文档建立好索引后,就可以在这些索引上面进行搜索了。搜索引擎首先会对搜索的关键词进行解析,然后再在建立好的索引上面进行查找,最终返回和用户输入的关键词相关联的文档。 今天在传智播客的课堂上,汤阳光老师教我们实现了简单的Lucene搜索引擎,使我们能够对大量的文档实现不同需求的查找。以下是我的总结。
1. 准备环境:添加jar包 lucene-core-2.4.0.jar(核心); lucene-analyzers-2.4.0.jar(分词器); lucene-highlighter-2.4.0.jar(高亮器);
2. 构造IndexWriter。IndexWriter是Lucene用来创建索引的一个核心的类。使用构造方法IndexWriter(Directory d, Analyzer a, MaxFieldLength mfl); 如果索引不存在,就会被创建。
* 相关参数说明
<1> Directory,代表了 Lucene 的索引的存储的位置。这是一个抽象类,常用的有两个实现,第一个是 FSDirectory,它表示一个存储在文件系统中的索引位置。第二个是 RAMDirectory,它表示一个存储在内存当中的索引位置。
<2> Analyzer,在一个文档被索引之前,首先需要对文档内容进行分词处理,这部分工作就是由 Analyzer 来做的。Analyzer 类是一个抽象类,它有多个实现。针对不同的语言和应用需要选择适合的 Analyzer。Analyzer 把分词后的内容交给IndexWriter 来建立索引。
<3> MaxFieldLength,用于限制Field的大小。这个变量可以让用户有计划地对大文档Field进行截取。假如取值为10000,就只索引每个Field的前10000个Term(关键字)。也就是说每个Field中只有前10000个Term(关键字)建立索引,除此之外的部分都不会被Lucene索引,当然也不能被搜索到。
3. 创建索引,使用方法IndexWriter.addDocument(Document doc)。
* 相关参数说明
<1> Document,是用来描述Lucene文档结构的。任何需要进行索引的数据都必须转化成Document对象。Document是索引和搜索的最基本单元,是一组Field的集合。
<2> Field,组成Document的元素,用来描述一个文档的某个属性的,比如一封电子邮件的标题和内容可以用两个Field对象分别描述。Field是由name和value组成的,value只接受字符串(非字符串类型要先转换成字符串才行)。在构造Field时要指定Store和Index。
Field.Store,指定Field是否或怎样存储。 Store.NO,不存储。 Store.YES,存储。 Store.COMPRESS,压缩后存储(数据量很大的时候可以使用,但要考虑效率)。
Field.Index,指定Field是否或怎么被索引。 Index.NO,不索引(不索引就不能被搜索到)。 Index.ANALYZED,(以前版本中为TOKENIZED),分词后索引。 Index.NOT_ANALYZED,(以前版本中为UN_TOKENIZED),不分词,直接索引(把整个Field做为一个term)。
注意:当完成了索引操作后,一定要调用IndexWriter.close()方法。
4,删除索引:
IndexWriter.deleteDocuments(Term term);
会删除索引文件里含有指定Term的所有Document。Term,是搜索的基本单位。代表某个Field中出现的某个关键字。
5,更新索引:
IndexWriter.updateDocument(Term term, Document doc);
实际上是先删除再创建索引,就是说如果有多条符合条件的Document,更新后只有一条。
6,搜索:
使用类IndexSearcher。查询方法为:
IndexSearcher.search(Query, Filter, int);
相关参数说明: <1> Query,查询对象,把用户输入的查询字符串封装成Lucene能够识别的Query。 <2> Filter,用来过虑搜索结果。 <2> 第三个参数(int类型),最多返回的Document的数量。 返回的是一个TopDocs类型,调用TopDocs.scoreDocs得到查询结果。
ScoreDoc.doc返回文档的内部编号。 IndexSearcher.doc(hits[i].doc) 通过编号,拿到文档。
Query可以用QueryParser解析查询字符串生成。使用构造方法为QueryParser(String defaultFieldName, Analyzer a),第一个参数为默认查询的Field,第二个参数为使用的分词器(这里用的分词器要和建立索引时用的分词器一致,否则可能会搜索不到结果)。使用parse(String)方法解析查询内容。
相关代码:
List<Document> docs = new ArrayList<Document>(); IndexSearcher indexSearcher = null; try { indexSearcher = new IndexSearcher(dir);
Filter filter = null; int nDocs = 10000;
TopDocs topDocs = indexSearcher.search(query, filter, nDocs); System.out.println("共有【" + topDocs.totalHits + "】条匹配记录");
for (int i = 0; i < topDocs.totalHits; i++) { ScoreDoc scoreDoc = topDocs.scoreDocs[i]; int docNum = scoreDoc.doc; // 文档在索引库中的编号 Document doc = indexSearcher.doc(docNum); // 通过编号取出相应的文档
docs.add(doc); }
return new SearchResult(topDocs.totalHits, docs); } catch (Exception e) { throw new RuntimeException(e); } finally { try { indexSearcher.close(); } catch (IOException e) { e.printStackTrace(); } }
7,测试LuceneIndexDao的增删改查方法,把LuceneIndexDao做为练习,要求通过LuceneIndexDaoTest中的单元测试,相关的测试代码由于比较多,暂不一一列出。
8. 下面是对一些重要的类及术语进行解释:
<1> Directory,有两个: a) FSDirectory,将索引放到磁盘中。 b) RAMDirectory,将索引放到内存中。速度快,但是在jvm退出之后,内存中的索引就不存在了。可以在jvm退出之前调用另一个使用FSDirectory的IndexWriter 把内存中的索引转存到文件系统中。 相应的API为IndexWriter.addIndexesNoOptimize(Directory[]),注意这个调用代码要放在内存中索引的操作RAMDirectory的IndexWriter关闭后,以便所有的document都进入RAMDirectory。在创建RAMDirectory的实例时,可以使用无参的构造方法,或是有一个参数的构造方法:可以指定一个文件路径,以便加载磁盘中的索引到内存中。 <2> 相关度排序,Lucene的搜索结果默认按相关度排序的,所谓相关度,就是文档的得分。Lucene有一个评分机制,就是对检索结果按某种标准进行评估,然后按分值的高低来对结果进行排序。
a) 文档的得分与用户输入的关键字有关系,而且是实时运算的结果。得分会受关键字在文档中出现的位置与次数等的影响。
b) 可以利用Boost影响Lucene查询结果的排序,通过设置Document的Boost来影响文档的权重,以达到控制查询结果顺序的目的:Document.setBoost(float)。默认值为1f,值越大,得分越高。
c) 也可以在查询时给Filed指定boost。当同样的term属于不同的field时,如果field的boost不一样,其所属的文件的得分也不一样。 <3> Analyzer,分词器,对文本资源进行切分,将文本按规则切分为一个个可以进行索引的最小单位(关键字)。
对于英文的分词,是按照标点、空白等拆分单词,比较简单;而中文的分词就比较复杂了,如”中华人民共和国“可以分为”中华”、“人民”、“共和国”,但不应有“华人”这个词(不符合语义)。
中文分词几种常用的方式: a) 单字分词,就是按照中文一个字一个字地进行分词。如:我们是中国人,效果:我\们\是\中\国\人。(StandardAnalyzer就是这样)。 b) 二分法:按两个字进行切分。如:我们是中国人,效果:我们\们是\是中\中国\国人。(CJKAnalyzer就是这样)。 c) 词库分词:按某种算法构造词然后去匹配已建好的词库集合,如果匹配到就切分出来成为词语。通常词库分词被认为是最理想的中文分词算法如:我们是中国人,效果为:我们\是\中国\中国人。 <4> Highlighter,高亮器,用于高亮显示匹配的关键字。 包含三个主要部分: 1) 格式化器:Formatter(使用SimpleHTMLFormatter,在构造时指定前缀和后缀)。 2) 计分器:Scorer(使用QueryScorer)。 3) 段划分器:Fragmenter(使用SimpleFragmenter,在构造时指定关键字所在的内容片段的长度)。
通过方法getBestFragment(Analyzer a, String fieldName,String text)实现高亮。(如果进行高亮的field中没有出现关键字,返回null)。 <5> 查询,有两种方式:通过Query Parser解析查询字符串或使用API构建查询。 使用Query Parser时不匹分大小写。以下是常用的查询:
1) TermQuery,按Term(关键字)查询(term的值应是最终的关键字,英文应全部小写)。 TermQuery(Term t); syntax: propertyName:keyword;
2) RangeQuery,指定范围查询。 RangeQuery(Term lowerTerm, Term upperTerm, boolean inclusive); syntax: propertyName:[lower TO upper](包含lower和upper); syntax: propertyName:{lower TO upper}(不包含lower和upper);
3) PrefixQuery,前缀查询。 PrefixQuery(Term prefix); syntax:propertyName:prefix*;
4) WildcardQuery,通配符查询,可以使用"?"代表一个字符,"*"代表0个或多个字符。(通配符不能出现在第一个位置上) WildcardQuery(Term term); syntax: propertyName:chars*chars?chars
5) MultiFieldQueryParser,在多个Field中查询。 queryParser = MultiFieldQueryParser(String[] fields, Analyzer analyzer); Query query = parse(String query); 6) BooleanQuery,布尔查询。这个是一个组合的Query,可以把各种Query添加进去并标明他们的逻辑关系。 TermQuery q1 = new TermQuery(new Term("title","javadoc")); TermQuery q2 = new TermQuery(new Term("content","term")); BooleanQuery boolQuery = new BooleanQuery(); boolQuery.add(q1, Occur.MUST); boolQuery.add(q2, Occur.MUST_NOT); Occur.MUST,必须出现。 Occur.MUST_NOT,必须未出现。 Occur.SHOULD,只有一个SHOULD时为必须出现,多于一个时为或的关系。
syntax: + - AND NOT OR (必须为大写)。
传智播客课程——Lucene搜索引擎的更多相关文章
- 传智播客DotNet面试题
技术类面试.笔试题汇总(整理者:杨中科,部分内容从互联网中整理而来) 注:标明*的问题属于选择性掌握的内容,能掌握更好,没掌握也没关系. 下面的参考解答只是帮助大家理解,不用背,面试题.笔试题千变万化 ...
- 传智播客C语言视频第二季(第一季基础上增加诸多C语言案例讲解,有效下载期为10.5-10.10关闭)
卷 backup 的文件夹 PATH 列表卷序列号为 00000025 D4A8:14B0J:.│ 1.txt│ c语言经典案例效果图示.doc│ ├─1传智播客_尹成_C语言从菜鸟到高手_第一 ...
- 传智播客C语言视频第一季(有效下载期为10.1-10.7,10.8关闭)
J:\传智播客_尹成_C语言从菜鸟到高手├─传智播客_尹成_C语言从菜鸟到高手_第一章C语言概述A│ 第一讲1.1C语言第一阶段.mp4│ 第二讲1.2c语言入门教程.mp4 ...
- 传智播客C/C++学员荣膺微软&Cocos 2d-x黑客松最佳创新奖
6月30日,历时32小时的微软开放技术Cocos 2d-x 编程黑客松在北京望京微软大厦成功落下帷幕,这是微软开放技术首次联合Cocos 2d-x 在中国举办黑客松.此次活动共有包括传智播客C/ ...
- 传智播客C/C++学院年薪24-50万招聘C/C++讲师
C/C++技术讲师 6名 (北京,年薪:24-50万) 传智播客C/C++课程培训体系如下: 1.C语言,世界五百强C语言面试训练 2.C++语言,世界五百强C++语言面试训练 3.数据结构与算法,世 ...
- 传智播客成都java培训中心秀就业
传智播客成都java培训中心秀就业 2013年被称为"史上最难就业季",成都传智播客学员如何应对的呢? 成都传智播客的学员在工作经验上颇占优势,我们采用项目驱动式教学模式,具有多年开发实战经验及教学经 ...
- 揭秘传智播客毕业班的超级薪水7k内幕系列II----Offer工资表5.7k,为什么不能让老师就业就业
在上海传智播客宋学生Java六期学员.在班级尚未毕业阶段,私自投递简历,而且逃课去面试,获得某国企的Offer.入职薪资5.7K,,兼有五险一金.饭补等齐全福利,因就业老师要求班级同学未毕业不要急于就 ...
- 揭秘上海传智播客平均工资超过7k
其中一位知情人士
大学毕业生人数破700万大关.如何破解"毕业即失业"中国式的大学困境? 2014年全国高校毕业生总数将达到727万人,比被称为"史上最难就业年"的2013年再添 ...
- 揭秘传智播客班级毕业薪资超7k的内幕系列 之三 ----国企慕名而来,将未毕业学员“抢走”,传智播客又一次定义“被就业”
前面文章提及Java六期学员张同学提前就业某国企,入职薪资6.3k,各种福利齐全.作为班级首位就业同学,他的就业也成为了班级其它同学就业的风向标.但事实上张同学的就业属于"被就业" ...
随机推荐
- splice 操作符
几乎所有的数组操作都可用 splice 实现. 除了第一个参数,数组,为必须,其余的参数都不是必须的. splice ARRAY, OFFSET, LENGTH, LIST OFFSET 和 LENG ...
- JQUERY、AJAX双击DIV,直接修改DIV内的内容
最近在做后台功能开发的时候,用到对排序字段的修改,感觉只为了修改一个排序值,而要重新进入编辑页比较麻烦,于是自己动手写…… 最近在做后台功能开发的时候,用到对排序字段的修改,感觉只为了修改一个排序值, ...
- CSS3中动画transform必须要了解的Skew变化原理
transform是CSS3中比较安全的动画(对于性能来说),其中有一些动画属性,来执行不同的变化.今天我们来了解skew的变化原理. skew,其实使用的频率不是很高,当然也没有最低.但是往往,一直 ...
- php之文件上传类代码
/* 单个文件上传 功能 上传文件 配置允许的后缀 配置允许的大小 获取文件后缀 判断文件的后缀 报错 */ class UpTool{ protected $allowExt = 'jpg,jpeg ...
- Vim及VimScript资料总结《转载》
版权声明:本文为博主原创文章,未经博主允许不得转载. 目录(?)[+] Vim教程 入门 Vim 实用技术 Learning the vi and Vim Editors A Byte of ...
- frame,bounds,center-三者的含义
frame与bounds的区别比较 frame,bounds,center-三者的含义 偶然觉的,这三个属性有时候定位的时候,需要用.于是就来搞清楚,到底frame,bounds,center 这三个 ...
- 转: Linux C 动态内存分配 malloc及相关内容 .
一.malloc()和free()的基本概念以及基本用法: 1.函数原型及说明: void *malloc(long NumBytes):该函数分配了NumBytes个字节,并返回了指向这块内存的指针 ...
- 工作总结:MFC自写排序算法(升序)
最近一个需求里面需要实IP升序排序,用了qsort,结果是内部排序,甚至感觉排序结果不可预测性,于是自己写了一个外部排序. 需求如下:一个指针里面有N条记录,每条记录包含:IP,偏移地址,保留位,均占 ...
- 设计模式——如何避免在OO设计中违反依赖倒置原则
1 变量不可以包含具体类的引用.一旦new,就对具体类产生依赖,用工厂模式来避开. 2 类不要派生至具体类.用派生抽象类避开. 3 不要覆盖基类已经实现的方法.基类中已实现的方法应该由所有子类共享.
- BZOJ 1049 数字序列
Description 现在我们有一个长度为n的整数序列A.但是它太不好看了,于是我们希望把它变成一个单调严格上升的序列.但是不希望改变过多的数,也不希望改变的幅度太大. Input 第一行包含一个数 ...