將Hibernate Search集成進已有項目中,實現全文檢索功能
本來是準備使用Lucene的但是新版本的API過于繁瑣,最后還是決定使用Hibernate Search來實現全文檢索。這篇博文以我以前做的博客為例來實現全文檢索。
1、修改Hibernate配置文件,因為我的系統采用的是SSH2來開發的所以我修改的是spring配置文件
<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="hibernateProperties"> <props> <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop> <prop key="hibernate.hbm2ddl.auto">update</prop> <prop key="hibernate.show_sql">true</prop> <prop key="hibernate.search.default.directory_provider">filesystem</prop> <prop key="hibernate.search.default.indexBase">E:/index</prop> </props> </property> <property name="mappingResources"> <list> <value>cn/harmel/blog/domain/User.hbm.xml</value> <value>cn/harmel/blog/domain/Category.hbm.xml</value> <value>cn/harmel/blog/domain/Article.hbm.xml</value> <value>cn/harmel/blog/domain/Comment.hbm.xml</value> <value>cn/harmel/blog/domain/Attachment.hbm.xml</value> </list> </property> </bean>
其實就是配置如下兩個屬性:
hibernate.search.default.directory_provider = filesystem
hibernate.search.default.indexBase = 索引存儲目錄
2、給實體標注注解
package cn.harmel.blog.domain;import java.util.Date; import java.util.HashSet; import java.util.Set;
import org.hibernate.search.annotations.Analyzer; import org.hibernate.search.annotations.DocumentId; import org.hibernate.search.annotations.Field; import org.hibernate.search.annotations.Indexed; import org.wltea.analyzer.lucene.IKAnalyzer;
/* 文章 @author Harmel / @Indexed @Analyzer(impl = IKAnalyzer.class) public class Article {
@DocumentId private Long id; @Field private String title; @Field private String content; @Field private String description; private Date postTime; private Date lastEditTime; private int viewCount;
private Category category; private Set<Comment> comments = new HashSet<Comment>(); private Set<Attachment> attachments = new HashSet<Attachment>();
// 此處省略一些getter和setter方法 //........ }</pre>
注解說明:
@Indexed:讓實體支持索引
@Analyzer :設置分詞器,我這里使用的是開源的IK中文分詞器
@DocumentID:索引文檔ID
@Field :索引字段,該注解默認屬性值為
store=Store.NO:是否將數據存儲在索引中,經實驗無論store=Store.NO還是store=Store.YES都不會影響最終的搜索。如果store=Store.NO值是通過數據庫中獲取,如果store=Store.YES值是直接從索引文檔中獲取。
index=Index.YES:是否索引
analyze=Analyze.YES:是否分詞
標注了注解后的實體在保存和更新的時候,會自動生成或修改索引。
3、查詢索引
public PageModel<Article> searchArticle(int pageNum, int pageSize, String keyword) { FullTextSession fts = Search.getFullTextSession(sessionFactory.getCurrentSession()); QueryBuilder qb = fts.getSearchFactory().buildQueryBuilder().forEntity(Article.class).get(); Query luceneQuery = qb.keyword().onFields("title", "content", "description").matching(keyword).createQuery(); FullTextQuery query = fts.createFullTextQuery(luceneQuery, Article.class); query.setFirstResult((pageNum - 1) * pageSize); query.setMaxResults(pageSize); List<Article> data = query.list(); //封裝分頁數據 PageModel<Article> model = new PageModel<>(pageNum, pageSize, data.size()); //將數據高亮 model.setData(SearchUtils.hightLight(luceneQuery, data, "title", "content", "description")); return model; }
將數據高亮工具方法
/** * 高亮顯示文章 * * @param query {@link org.apache.lucene.search.Query} * @param data 未高亮的數據 * @param fields 需要高亮的字段 * @return 高亮數據 */ public static List<Article> hightLight(Query query, List<Article> data, String... fields) { List<Article> result = new ArrayList<Article>(); Formatter formatter = new SimpleHTMLFormatter("<b style=\"color:red\">", "</b>"); QueryScorer queryScorer = new QueryScorer(query); Highlighter highlighter = new Highlighter(formatter, queryScorer); // 使用IK中文分詞 Analyzer analyzer = new IKAnalyzer(); for (Article a : data) { // 構建新的對象進行返回,避免頁面錯亂(我的頁面有錯亂) Article article = new Article(); for (String fieldName : fields) { // 獲得字段值,并給新的文章對象賦值 Object fieldValue = ReflectionUtils .invokeMethod(BeanUtils.getPropertyDescriptor(Article.class, fieldName).getReadMethod(),a); ReflectionUtils.invokeMethod(BeanUtils.getPropertyDescriptor(Article.class, fieldName).getWriteMethod(), article, fieldValue); String hightLightFieldValue = null; try { hightLightFieldValue = highlighter.getBestFragment(analyzer, fieldName, String.valueOf(fieldValue)); } catch (Exception e) { throw new RuntimeException("高亮顯示關鍵字失敗", e); } // 如果高亮成功則重新賦值 if (hightLightFieldValue != null) { ReflectionUtils.invokeMethod(BeanUtils.getPropertyDescriptor(Article.class, fieldName).getWriteMethod(), article,hightLightFieldValue); } } // 賦值ID ReflectionUtils.invokeMethod(BeanUtils.getPropertyDescriptor(Article.class, "id").getWriteMethod(), article, a.getId()); result.add(article); } return result; }
4、頁面迭代顯示
<s:iterator value="#request.pageModel.data"> <div class="article"> <div class="article_title_area"> <span class="article_title"><a href="${pageContext.request.contextPath }/article/show.action?id=${id }">${title }</a></span> <span class="article_date">發表時間:<s:date name="postTime" format="yyyy-MM-dd HH:mm:ss"/></span> </div> <div class="article_content">${description }</div> <div class="article_count_info"> <span>閱讀(${viewCount })</span> <span>評論(${comments.size() })</span> </div> </div> </s:iterator>來自:http://my.oschina.net/harmel/blog/491159