class="tags" href="/tags/LUCENE.html" title=lucene>lucene是一个class="tags" href="/tags/QuanWenJianSuo.html" title=全文检索>全文检索引擎(非分布式),使用java编写并且可以很方便的嵌入到各种系统中以实现class="tags" href="/tags/QuanWenJianSuo.html" title=全文检索>全文检索功能,其通过对字符串进行分词,然后针对分词结果分别建立索引,存入内存或者磁盘,以提供搜索服务,这个功能用起来就像是一个单机版的google,模糊匹配的结果会被展示给用户。应用class="tags" href="/tags/LUCENE.html" title=lucene>lucene较为出名的就包括了eclipse的帮助系统。
很多时候搜索这个概念会含糊不清,通常意义上对于一个网站的搜索功能是针对于从class="tags" href="/tags/ShuJuKu.html" title=数据库>数据库中捞取数据并通过freemarker或者velocity渲染模板后展示出来,然后搜索的另一个含义,是针对于文件系统而言,将文件的内容进行搜索并读取文件展示出来。那么对于class="tags" href="/tags/LUCENE.html" title=lucene>lucene而言,显然更大的用于之地在于后者,虽然前者的功能也是能够实现。
我们可以想象google的服务器不断的定时dump全球Internet的网页,然后放入自己的存储区域,通过检索评级等一系列的复杂算法,建立针对于关键字的索引,当用户输入关键字进行查询的时候,她会根据建立的索引以及评级等信息进行相似度的判断并展现出查询结果。当然具体的过程会远远比这个复杂。这里要说的是建立搜索的这个概念,从简单的class="tags" href="/tags/ShuJuKu.html" title=数据库>数据库查询的思想出脱离出来,建立一个class="tags" href="/tags/QuanWenJianSuo.html" title=全文检索>全文检索的概念。
索引可以理解成现实中书籍的目录,通过这个目录可以很快的定位到具体的页数,拿到想要的结果。这个和class="tags" href="/tags/ShuJuKu.html" title=数据库>数据库的索引是一样的功能,都是为了提高效率,然后class="tags" href="/tags/ShuJuKu.html" title=数据库>数据库却无法实现一个高效的模糊匹配,通常而言的%like%语句,是一条一条的数据比对类似于书籍的翻页过程,这样的效率是极其低下而且对于class="tags" href="/tags/ShuJuKu.html" title=数据库>数据库系统而言是很大的性能耗费,尤其是当有多个关键字的时候,多个%like%的查询条件足以让DBA疯掉。
所以对于模糊匹配的查询而言,高效而准确是一个关键的因素。而class="tags" href="/tags/LUCENE.html" title=lucene>lucene的优势就是通过对文本进行分词,通过分词得到一系列的关键字以用于建立索引,同时使用自己默认的socre相关度算法进行排序,通常而言如果对于搜索结果有自己特殊的排序要求,可以在使用class="tags" href="/tags/LUCENE.html" title=lucene>lucene建立索引之前,先将数据排好序,可以防止class="tags" href="/tags/LUCENE.html" title=lucene>lucene默认的排序之外再次进行排序操作。
对于原理性的知识,可以参看网络上的一些文章,google一下看到很多写的都不错,个人不是很擅长写这种原理性的东西,所以下面做一个小实验,来熟悉一下具体的class="tags" href="/tags/LUCENE.html" title=lucene>lucene step by step
我们的需求是针对于一个网站的帮助系统开发一个搜索功能,用户输入自己希望搜索的问题或者关键字,我们通过class="tags" href="/tags/LUCENE.html" title=lucene>lucene进行搜索并展示结果给用户。
就是这样简单的一个需求,假设我们的帮助系统的文档是存放在class="tags" href="/tags/ShuJuKu.html" title=数据库>数据库中的,这个数据量在百这个级别,很小的一个数据量。我们先模拟一个小数据量来实现这个功能,如果数据量是百万级那需要进行分布式的索引建立和搜索,那将涉及到其他额外的很多条件和问题解决方案。
我们希望通过这个系统的开发,不但能解决当前的帮助系统的问题,还可以一定程度上解决一些数据量较小的其他搜索问题,所以是势必需要我们去开发一个通用的中间件,并且通过一定的控制手段来注入特定的业务。
要建立索引,我们需要知道面对的数据对象是谁?哪张表?在哪些字段上建立索引?这几个是关键因素,当然还包括了索引文件存放位置等小问题暂且掠过。这几个关键的因素我们需要注入到我们开发的中间件中,通过配置注入的方式隔离具体的业务代码交织。
怎么做呢?如何去从class="tags" href="/tags/ShuJuKu.html" title=数据库>数据库中获取数据并建立索引呢?索引文件以多大的频率更新?
看看代码:
这个是我们的中间件,提供的是索引建立、索引更新和搜索服务,其中有几个变量如className指明了要针对哪个Do对象所对应的表来建立class="tags" href="/tags/QuanWenJianSuo.html" title=全文检索>全文检索,fieldsStr指明了需要建立索引的字段,以逗号分开,其他的参数也都很简单。
我们需要根据配置的字段去查询class="tags" href="/tags/ShuJuKu.html" title=数据库>数据库已完成索引的建立工作,那么如何获取呢?我们说过不希望通过显示的业务代码来完成,所以这里使用了前段时间开发的一个jdbcTemplate的封装抽象类,其中的查询字段列表的方法来得到特定字段值的列表,为了不显示的使用业务DAO,我们使用了一个匿名内部类来完成。具体的过程可以参见前面的文章。其中与之有所不同的是,在删除索引的时候,我们需要有一个类似于记录ID的标志位来标记唯一约束,以防止误删的情况,这里由于数据来自于class="tags" href="/tags/ShuJuKu.html" title=数据库>数据库,所以很自然的我们选择使用class="tags" href="/tags/ShuJuKu.html" title=数据库>数据库记录ID,如果用户配置的索引串中没有配置ID,那我们将通过注解的方式自动获取其配置在DO类中的ID名称,如果注解中也没配置,那么将使用默认的名称“id”。
看看我们的注解:
看看用法:
配置文件中,我们添加相关的配置:
为了完成自动刷新,我们添加配置任务:
这样就完成了开发工作。系统启动先建立索引,然后每隔15分钟将刷新一次索引。自动刷新我们的策略是删除所有的索引,重新建立索引,这个特别针对于数据量较小的操作,如果数据量稍微大一些,不推荐采用这种方式,我们需要进行一个自动识别的工作,只刷新变更过的记录,而不要过多的开销系统来重新全部重建。这里由于数据量较小,所以为了方便,我们直接删除所有索引重新建立。在后续的工作中,我们将重点研究如何精准刷新。
看看单元测试代码:
万里长征只走了第一步,剩下的事情还有很多,这个只是最简单的一个例子,万事开头难,在熟练应用的基础上进行二次开发或者源码分析将是接下来的主要工作思路。