Lucene索引和搜索
BuguMongo集成了Lucene的功能。当往MongoDB中新增一个Document时,能自动为该Document建立Lucene索引。相应的,当MongoDB中的Document被修改、删除时,对应的Lucene索引也会修改、删除。
另外,BuguMongo还提供了对Lucene搜索的支持。根据Lucene索引进行搜索的时候,搜索结果能自动转换成对应的Entity对象。
在Lucene集成方面,BuguMongo提供了:
10个注解:@Indexed、@IndexProperty、@IndexEmbed、@IndexEmbedList、@IndexRef、@IndexRefList、@IndexRefBy、@IndexEmbedBy、@IndexFilter、@BoostSwitch
5个类:BuguIndex、BuguParser、BuguSearcher、BuguHighlighter、IndexRebuilder
步骤一:在Entity上加注解
代码例子如下:
package com.bugull.mongo.test;import com.bugull.mongo.BuguEntity;import com.bugull.mongo.annotations.Entity;import com.bugull.mongo.annotations.Embed;import com.bugull.mongo.annotations.Id;import com.bugull.mongo.annotations.Ref;import com.bugull.mongo.lucene.annotations.Indexed;import com.bugull.mongo.lucene.annotations.IndexProperty;import com.bugull.mongo.lucene.annotations.IndexEmbed;import com.bugull.mongo.lucene.annotations.IndexRef;import com.bugull.mongo.lucene.annotations.IndexFilter;import com.bugull.mongo.lucene.annotations.Compare;import java.util.List;@Entity@IndexedpublicclassFooimplementsBuguEntity{ @Id privateString id; @IndexProperty(analyze=true,boost=2.0f) privateString title; @IndexProperty(analyze=true) privateString introduce; @Embed @IndexEmbed privateEmbedFoo embed; @Ref @IndexRef privateFatherFoo father; @IndexFilter(compare=Compare.IS_EQUALS,value="true") privateboolean valid; @BoostSwitch(compare=Compare.IS_EQUALS,value="true",fit=1.5f,unfit=0.5f) privateboolean vip; ...getter and setter...}publicclassEmbedFoo{ @IndexEmbedBy(value=Foo.class) privatefloat x; @IndexEmbedBy(value=Foo.class) privateint y; ...getter and setter }@Entity(name="father")publicclassFatherFooimplementsBuguEntity{ @Id privateString id; @IndexRefBy(value=Foo.class, analyze=true) privateString name; ...getter and setter...}
各个注解的含义如下:
@Indexed
表示需要对该Entity建索引。该Entity上必须要有@Entity和@Id注解,@Indexed才能起作用。
@IndexProperty
表示需要对该属性建立索引。
@IndexProperty支持的数据类型包括:String、char、boolean、int、long、float、double、Date等基本数据类型。
@IndexProperty注解有3个参数:
analyze——boolean型,表示是否需要分词,缺省值为false
store——boolean型,表示是否需要存储,缺省值为false
boost——float型,表示该Field的权重,缺省值为1.0
@IndexProperty还支持上述基本数据类型组成的数组、List、Set等。这些集合中的元素,不管是什么数据类型,都会连结成一个字符串,然后加以索引。
@IndexEmbed
表示需要嵌入对该Embed对象的索引。结合@IndexEmbedBy使用。索引域的名称形如“embed.x”。
@IndexEmbedList
表示需要嵌入对该EmbedList对象的索引。结合@IndexEmbedBy使用。索引域的名称形如“embed.x”。
@IndexRef
表示需要嵌入对该Ref对象的索引。结合@IndexRefBy使用。索引域的名称形如“father.name”。
@IndexRefList
表示需要嵌入对该RefList对象的索引。结合@IndexRefBy使用。索引域的名称形如“father.name”。
@IndexRefBy
表示需要嵌入到其它对象的@Ref或@RefList域的索引中。
@IndexRefBy有4个参数:
value——Class类型,表示被引用的类,该值必须设置
analyze——boolean型,表示是否需要分词,缺省值为false
store——boolean型,表示是否需要存储,缺省值为false
boost——float型,表示该Field的权重,缺省值为1.0
当某属性在多个类的索引中被引用时,上述4个参数都需要设置成数组形式。
@IndexEmbedBy
表示需要嵌入到其它对象的@Embed或@EmbedList域的索引中。
@IndexEmbedBy拥有与@IndexRefBy相同的4个参数。
@IndexFilter
表示只有满足该条件的实体才会被索引,否则不创建索引。
@IndexFilter有2个参数:compare和value。compare表示比较操作,是枚举类型Compare。value是比较的值,是字符串,会相应的解析成该属性类型的值。
compare有多个枚举值,它们的含义和所支持的数据类型如下:
Compare.IS_EQUALS——等于(==)。支持String、boolean、int、long、float、double、char。
Compare.NOT_EQUALS——不等于(!=)。支持String、boolean、int、long、float、double、char。
Compare.GREATER_THAN——大于(>)。支持int、long、float、double。
Compare.GREATER_THAN_EQUALS——大于等于(>=)。支持int、long、float、double。
Compare.LESS_THAN——小于(<)。支持int、long、float、double。
Compare.LESS_THAN_EQUALS——小于等于(<=)。支持int、long、float、double。
Compare.IS_NULL——为空(==null)。支持Object类型,包括String。这时不需要value参数。
Compare.NOT_NULL——不为空(!=null)。支持Object类型,包括String。这时不需要value参数。
在一个Entity类上可以有多个@IndexFilter注解,表示需要同时满足这些条件。
@BoostSwitch
同一个Entity类的不同的Document,可能需要设置不同的权重,@BoostSwitch注解就是用来实现这个功能的,它有4个参数:
compare——与上面@IndexFilter的compare含义相同
value——与上面@IndexFilter的value含义相同
fit——float型,表示满足比较条件时该Document的boost值,缺省值为1.0
unfit——float型,表示不满足比较条件时该Document的boost值,缺省值为1.0
在一个Entity类上只能有一个@BoostSwitch注解。
步骤二:设置索引参数
首先,系统初始化的时候,需要设置BuguIndex的各个参数,并执行open()方法,代码例子如下:
BuguIndex index =BuguIndex.getInstance(); index.setDirectoryPath("/root/lucene_index/");Version version = index.getVersion(); index.setAnalyzer(newStandardAnalyzer(version)); index.setThreadPoolSize(5); index.setIndexReopenPeriod(60L*1000L); index.open();
退出系统的时候,记得关闭索引:
index.close();
与BuguConnection一样,你也可以把BuguIndex看作是配置文件,它也是一个单例模式,BuguMongo框架中Lucene相关的组件,会自动使用BuguIndex的实例。
上述代码中,各个方法的含义都很直观。其中:
BuguMongo使用FSDirectory存储索引,通过setDirectoryPath()可以设置索引文件存放的位置。该值必须设置。
BuguMongo使用当前Lucene的默认版本号。用getVersion()可以获得版本号。如果你不想使用默认的Lucene版本,可以用setVersion()来设置Lucene索引的版本。
你可以用setAnalyzer()来设置你所需要的Analyzer。如果不设置,则BuguIndex默认使用StandardAnalyzer。
当需要对一个Entity创建索引,或者修改、删除索引的时候,BuguMongo会从线程池中选择一个线程来执行该任务。setThreadPoolSize(int poolSize),就是设置线程池的大小。该值可以不设置,默认值为10。
setIndexReopenPeriod(long milli)的参数以毫秒为单位,表示索引刷新的时间周期,该参数值根据应用的情况而定。该值可以不设置,默认值为30L*1000L,即30秒。
步骤三:搜索
通过上面的设置,BuguMongo能够实现Entity和Lucene索引之间的自动同步,你只要实现搜索就行了。最基本的搜索的例子如下:
Query query =BuguParser.parse("introduce","谷歌");BuguSearcher<Foo> searcher =newBuguSearcher(Foo.class); searcher.setQuery(query).setSort().setFilter().setPageNumber(1).setPageSize(20).setMaxPage(50);List<Foo> list = searcher.search();int count = searcher.getResultCount(); searcher.close(); //务必记得关闭BuguSearcherfor(Foo foo : list){ ...}
setMaxPage(maxPage),表示最多返回搜索结果的前maxPage页。设置该参数的原因是:如果返回所有的搜索结果,当数据量太大的时候,会导致性能问题。
pageNumber、pageSize、maxPage的值都可以不设置,默认值为分别为:1、20、50。
如果不排序,可以不用setSort()。
如果没有Filter,则可以不用设置setFilter()。
对于通过@IndexEmbed和@IndexRef嵌入的索引,索引的field名称需要加上该属性的名称作为前缀,如:father.name。参见下面的例子。
Query strQuery =BuguParser.parse(newString[]{"introduce","father.name"},"谷歌");Query xQuery =BuguParser.parse("embed.x",3.14f);Query yQuery =BuguParser.parse("embed.y",100,300);BooleanQuery bQuery =newBooleanQuery(); bQuery.add(strQuery,Occur.SHOULD); bQuery.add(xQuery,Occur.MUST_NOT); bQuery.add(yQuery,Occur.MUST);BuguSearcher<Foo> searcher =newBuguSearcher(Foo.class);List<Foo> list = searcher.search(bQuery);int count = searcher.getResultCount(); searcher.close();for(Foo foo : list){ ...}
获得IndexSearcher
BuguSearcher能实现大部分常用的搜索功能,但在一些特殊的情况下,你可能需要使用底层的Lucene搜索功能,比如使用Collector,这时,你需要通过调用BuguSearcher的getSearcher()方法,来获得底层Lucene的IndexSearcher,然后用它来进行搜索。
BuguSearcher<Foo> searcher =newBuguSearcher(Foo.class);IndexSearcheris= searcher.getSearcher();Query query =...Collector collector =...is.search(query, collector); searcher.close();
查询分析器:BuguParser
正如前面的例子中看到的那样,BuguParser类提供了对各种数据类型的查询支持,包括数值查询、范围查询等,还提供了MultiFieldQueryParser的功能。
BuguParser提供了一系列静态方法,用于创建Query。
字符串查询:
publicstaticQuery parseTerm(String field,String value) //创建一个TermQuerypublicstaticQuery parse(String field,String value) //默认使用Operator.ORpublicstaticQuery parse(String field,String value,Operator op)publicstaticQuery parse(String[] fields,String value) //默认使用Operator.ORpublicstaticQuery parse(String[] fields,String value,Operator op)publicstaticQuery parse(String[] fields,Occur[] occurs,String value)
数值查询:
publicstaticQuery parse(String field,int value)publicstaticQuery parse(String field,long value)publicstaticQuery parse(String field,float value)publicstaticQuery parse(String field,double value)
数值范围查询:
publicstaticQuery parse(String field,int minValue,int maxValue)publicstaticQuery parse(String field,long minValue,long maxValue)publicstaticQuery parse(String field,float minValue,float maxValue)publicstaticQuery parse(String field,double minValue,double maxValue)publicstaticQuery parse(String field,Datebegin,Dateend)publicstaticQuery parse(String field,Timestampbegin,Timestampend)
其它:
publicstaticQuery parse(String field,boolean value)publicstaticQuery parse(String field,char value)
辅助工具:重建索引
在某些情况下,由于特殊的原因,Lucene索引文件没有与MongoDB中的记录一一对应,比如,用shell命令往MongoDB中添加或修改了数据,这时,需要对MongoDB中的文档重新建立Lucene索引。BuguMongo提供了一个类IndexRebuilder来实现该功能。代码例子如下:
IndexRebuilder r1 =newIndexRebuilder(Foo.class); r1.rebuild();...int batchSize =50;IndexRebuilder r2 =newIndexRebuilder(MyEntity.class, batchSize); r2.rebuild();
参数batchSize表示IndexRebuilder每次从MongoDB中取多少条数据进行索引,默认值为100。
辅助工具:高亮显示
对搜索结果中的关键词进行高亮显示,是一个经常用到的功能。BuguMongo中提供了一个类BuguHighlighter来实现该功能。代码例子如下:
Query query =BuguParser.parse("introduce","谷歌");BuguSearcher<Foo> searcher =newBuguSearcher(Foo.class);BuguHighlighter highlighter =newBuguHighlighter("introduce","谷歌"); //对introduce字段的“谷歌”关键词高亮显示 searcher.setHighlighter(highlighter);List<Foo> list = searcher.search(query); searcher.close();
在网页上,只需要显示foo.introduce字段的内容,其关键字部分,会自动高亮显示。
可以对多个字段中的关键词高亮显示。比如,同时对title、introduce字段中的“谷歌”关键词高亮显示,BuguHighlighter可以这样定义:
String[] fields =newString[]{"title","introduce"};BuguHighlighter highlighter =newBuguHighlighter(fields,"谷歌");
BuguHighlighter默认将关键词用红色显示,如果需要把它改成其它的显示样式,可以这样定义:
BuguHighlighter highlighter =newBuguHighlighter("introduce","谷歌");Formatter formatter =newSimpleHTMLFormatter("<font color=\"#0000FF\"><b>","</b></font>"); //蓝色,加粗 highlighter.setFormatter(formatter);