Lucene的简单应用

背景

在项目中经常会有各种搜索的需求,简单的需求可以用Mysql like查询来做,但稍显复杂的查询就无法直接使用SQL来完成了。为此我开始了尝试探索lucene这个开源的搜索框架,试图解决我们实际开发种的搜索需求,注意全文搜索不在本次探讨的范围之内,本篇只是试图去解决我们开发中常见的资料搜索。

Lucene版本选择

Lucene版本选择,我记得我曾今买了一本lucene的书籍那时候的版本还是2.0,到现在再次使用Lucene的时候版本已经到了4.0alpha版本。这里版本的选择我尽量选择稳定版本,也就是3.6版本而不是最新的4.0版本,面对还没有足够了解的东西,我还是建议足够了解之后再去尝试新的特性。

Lucene API

Lucene的官方文档地址:http://lucene.apache.org/core/3_6_1/index.html,这里要吐槽一下的是,Lucene的文档有点太粗糙了,各主要的知识点只是轻轻一点,其他的就只能靠Java doc了,没有完整的学习路径可以遵循。至今为止个人认为看到的最好的开源项目文档可能属Spring了吧。言归正传,学习Lucene的API最好的例子就是从Lucene自带的demo中的例子开始学,该例子位于contrib中的demo模块下。

下面就来讲Lucene中几大关键类(Essential Classes)

从索引上讲,与构建索引有关的类主要有,Analyzer、Directory、IndexWriterConfig、IndexWriter、Document、Field
其中Analyzer用于分词构建索引词,Directory用于索引存储,IndexWrtier和IndexWriter用于索引创建与维护,Document、Field为索引的数据结构

Analyzer
Analyzer是用来将文本解析成索引词的工具。
Directory
Directory为Lucene存放索引文件的目录,Directory可以是文件、DB、内存等形式。
IndexWriterConfig
用来配置IndexWriter的各种参数
IndexWriter
IndexWriter顾名思义是用来创建和维护索引的工具
Document
Document是索引和查询的最小单元,我们在创建索引的时候会以文档为单位添加,而在查询时也会以文档为单位返回查询结果。
Field
一个文档可以包含多个Field,Field代表字段一个文档可以由多个字段组成。

到这里构建索引的所有元素都已经介绍完成,下面介绍与搜索有关的类,他们是IndexReader、IndexSearcher、Query、QueryParser、TopDocs、ScoreDoc
其中IndexReader负责读取索引文件,IndexSearcher用来查询索引,Query代表查询实例,QueryParser用来解析复杂查询语句,TopDocs和ScoreDoc分别代表
一次查询的hit结果集和Hit结果集中的一个结果。

IndexReader
用来访问索引文件,线程安全
IndexSearcher
用来执行查询,其实现基于上面提到的IndexReader,线程安全。需要注意的是,当索引发生变化时为了能够读取到相应的修改你需要重新创建IndexSearcher,
IndexReader也需要重新打开。
Query、QueryParser、TopDocs、ScoreDoc不再介绍。

为了更加简单的使用以上的各个组建,我对以上各个组建重新组织了一下设计了一套新的工具

我设计的搜索主要由上面的四个部分组成,其中SearchBee是搜索的入口,提供query和doPaginationQuery这两个主要的查询接口。DataFetcher是用来为SearchBee输送数据,从DataFetcher获取来的数据用来构建索引。QueryBuilder是查询构造器,针对与不同的查询需求可以构造不同的QueryBuilder来满足需求。
PaginationResult是一个分页返回结果的数据结构,用来存储分页结果。另外我还提供了一个基于pin4j用于拼音搜索的工具类PinyinUtils来帮助实现拼音检索。

如何实现一整套的搜索业务?
首先我们要扩展SearchBee,实现其getIndexWriter、getIndexSearcher、和buildDocument方法。然后我们需要实现一个DataFetcher来作为索引数据源,最后我们根据业务的需求创建QueryBuilder来满足搜索需求。完成了这些内容就完成一个简单的搜索功能。

索引重建
注意这里只采用索引重建这一种方法来更新索引,其他方式的索引更新方式不在此讨论范围之内。
对于索引重建我采用的是最简单的定时重建索引的方式,我们采用Quartz的cronJob来实现类似与crontab的定时任务,相应的配置如下

     <bean name="indexScheduleJob" class="org.springframework.scheduling.quartz.JobDetailBean">
          <property name="jobClass" value="com.ivannotes.demo.IndexScheduleJob" />
          <property name="jobDataAsMap">
               <map>
                    <entry key="some value" value-ref="some bean"></entry>
               </map>
          </property>
     </bean>
     <bean id="cronSearchTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">
          <property name="jobDetail" ref="indexScheduleJob" />
          <property name="cronExpression" value="0 0 2 * * ?" />
     </bean>
     <bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
          <property name="triggers">
               <list>
                    <ref bean="cronSearchTrigger"/>
               </list>
          </property>
     </bean>

其中IndexScheduleJob实现QuartzJobBean

到这里还没有完。文章上面提到,如果索引更新的情况下IndexSearcher也需要重建,那么我们如何做到这一点呢?
在这里我们采用了一种事件机制,我们定义了索引变更的事件IndexChangedEvent,并将SearchBee的实现注册为事件处理器,当索引重建发生后
我们派发事件,SearchBee监听到对应的事件后触发IndexSearcher的重建完成,这也是索引更新的最后一环。

到这里搜索实现就讲完了,以上所说的只是一种实现思路,并不是最好的实现也不是唯一的实现,优化还需要继续。代码之后我将整理之后提交至github;-)

github地址:https://github.com/ivannotes/search-bee

Meta

Published: Aug. 20, 2012 Author: ivan Comments:   Word Count: 231
Bookmark and Share

Next: RRDTOOL安装指南

Previous: 利用AOP做性能监控

Tags

java lucene spring

Article Links

  1. GitHub - ivannotes/search-bee: a simple search tool based on lucene

Attachments

Comments powered by Disqus