Hibernate search 实践[1]

如果在JEE的项目中使用了hibernate 作为数据的持久层框架,那么在涉及到数据全文搜索时(fulltext search),如果系统需要对用户输入的关键字进行分析查询,然后得出匹配排序的搜索结果,可以考虑使用hibernate search组件。

一般情况下,你也可以使用hibernate本身提供的DetachedCriteria类来进行多个字段的查询,从而实现全文检索的效果。参考的代码如下:

Junction junction = Restrictions.disjunction()
.add(Restrictions.like(“website”, search, MatchMode.ANYWHERE).ignoreCase())
.add(Restrictions.like(“company”, search, MatchMode.ANYWHERE).ignoreCase())

这样可以对表中的所有字段来查询。然而, hibernate在执行这种查询时使用HQL(最后都是使用SQL标准查询语言),这样其实还不是真正意义上的全文检索,而且这种查询会随着字段数量的增加而降低性能。

hibernate意识到这一点,面对互联网搜索引擎的强大检索性能的挑战,高效而易于实现的全文检索是开发数据库应用程序的一个重要需求。hibernate推出了基于lucene search的全文检索集成。

lucene search是apache一个有名的全文索引引擎,其主要的功能是做内容索引及查询服务。详细的技术细节可以参考apache lucene网站,这里不作详细解释了。

要在使用了hibernate框架的项目里使用hibernate search,可以按照以下几个步骤进行:

第一、获得类库及配置

Java 版本为1.5 以上, hibernate 3
你可以到hibernate search官方网站上下载其最新的api (目前版本是3.1.0 GA)
要使用hibernate search,需要在代码中使用到Java1.5支持的Java声明技术,所以你要下载hibernate annotation,这个是hibernate的声明类库。

另外,如果你的Java对象里使用了高级的hibernate集合影射,还需要J2EE包的annotation。

把这些类jar文件放到你的开发、编译及运行环境中。清单如下

# hibernate-search
3.1.0.GA
#hibernate-annotations
3.4.0.GA
#hibernate-entitymanager
3.4.0.GA
#solr-common
可选
1.3.0
#solr-core
可选
1.3.0
#lucene-snowball
可选
2.4.0

接下来配置一下hibernate的properties,有两个基本属性

property name=”hibernate.search.default.directory_provider” value=”org.hibernate.search.store.FSDirectoryProvider”

property name=”hibernate.search.default.indexBase” value=”/var/lucene/indexes”
*这个是指定lucene引擎在索引是产生的索引文件的存放位置

如果你使用了spring框架,你可能需要在对象被创建、更该或删除后hibernate自动更新lucene的索引,你还需要向spring框架里添加event listener,下面是所需的配置参考代码

这样,基本的环境配置就完成了,可以进行下一步编码的调整了

第二、调整索引编码

要使hibernate search可以索引对象的数据,你需要对hibernate的实体对象entity进行索引声明调整,以一个简单的带集合影射的contact bean为例

@Entity
@Indexed
public abstract class AbstractBean{

private Integer id;

@Id
@GeneratedValue(strategy=GenerationType.AUTO)
@DocumentId
public Integer getId() {
return id;
}

… …
}

@Entity
@Indexed
public class Contact extends AbstractBean{

@Field(index=Index.TOKENIZED, store=Store.NO)
private String phone;
@Field(index=Index.TOKENIZED, store=Store.NO)
private String fax;
@Field(index=Index.TOKENIZED, store=Store.NO)
private String mobile;
@Field(index=Index.TOKENIZED, store=Store.NO)
private String zipCode;

@OneToMany(fetch = FetchType.EAGER)
@Cascade(org.hibernate.annotations.CascadeType.ALL)
@IndexedEmbedded
private List emails = new ArrayList();

}

@Entity
public class Email extends AbstractBean{
@Field(index=Index.TOKENIZED, store=Store.NO)
private String email
}

请留意@index, @Field, @OnToMany, @Cascade, @IndexedEmbedded等,hibernate (lucene) 提供针对不同搜索内容的index声明,用于优化索引的内容以及查询的结构。

如果不需要特殊索引要求,可以保存这些声明调整,进行下一步搜索工作。

第二、使用全文搜索

也许你在引入使用hibernate search功能前,系统的数据库已经存储了一定数量的数据了,为了能够搜索到已有的数据,必须手动索引现有数据,做法如下:

public Integer reindexFullTextSearch(){
FullTextSession fullTextSession = Search.getFullTextSession(session);
Transaction tx = fullTextSession.beginTransaction();

List contacts= super.getSession(false).createQuery(“from com.test.Contact as contact”).list();
for (Contact contact: contacts) {
fullTextSession.index(contact);
}

tx.commit();

logger.info(“Contact reindex fulltext search results: “+contacts.size());

return contacts.size();
}

这个方法可以触发hibernate search对现有的contact数据做索引,在hibernate环境中运行这段码,你会在之前配置的索引目录下看到索引文件的更新。当然你打不开它,因为是二进制文件。

完成这步后,因为你在配置hibernate search时定义了event listener,以后当对象被创建、修改或删除时,都可以自动地更新索引了。这听起来不错,那要怎样搜索呢?请看下面的代码:

public java.util.Collection fullTextSearch(String keyword, Integer start, Integer limit, String sortCriteria, String sortOrder){
FullTextSession fullTextSession = Search.getFullTextSession(this.getSession());
Transaction tx = fullTextSession.beginTransaction();

// create native Lucene query
String[] fields = new String[]{“id”, “phone”, “fax”, “mobile”, “zipCode”, “Email.email”, };
List results = null;
try{
MultiFieldQueryParser parser = new MultiFieldQueryParser(fields, new StandardAnalyzer());
Query query = parser.parse( keyword );
// wrap Lucene query in a org.hibernate.Query
org.hibernate.Query hibQuery = fullTextSession.createFullTextQuery(query, Contact.class);
// execute search
// paging (optional)
if (start!=null && limit!=null) {
hibQuery.setFirstResult(start);
hibQuery.setMaxResults(limit);
results = hibQuery.list();
}else{
results = hibQuery.list();
}

logger.info(“Contact fulltext search results: “+results.size());

}catch(Exception e){
logger.error(“Failed to run fulltext search: “+e);
}

tx.commit();

return results;
}

你会看到,关键字keyword会被首先分析,然后进行查询。缺省情况下关键字keyword的分析是StandardAnalyzer,这个关键字分析是全文检索的重要功能,如果需要针对不同的语言做分析,可以选择不同的分析器。

在hibernate环境下运行这段代码,你可以得到返回的搜索结果。

Author: chooli.yip

I love to explorer the wild nature with huge curiosity

Leave a comment