使用 Compass 第三方框架维护索引库数据
1. Compass 基于 Lucene 的关系,就类似 Hibernate 于 JDBC 的关系。但是目前 Compass 应用并不广泛,大多都是直接使用 Lucene 。咱为了效率,不差 Compass 再封装的这点功夫。但这个框架确实很精彩 ~
2. 由于 Compass 的 API 风格和 Hibernate 的风格很像,先看看 Hibernate 的 API:
- Configuration config = new Configuration().configure();
- SessionFactory sessionFactory = config.buildSessionFactory();
- Session session = null ;
- Transaction tx = null ;
- try {
- session = sessionFactory.openSession();
- tx = session.beginTransaction();
- // 更新操作
- tx.commit();
- } catch (Exception e) {
- if (tx != null ) {
- tx.rollback();
- }
- throw e;
- } finally {
- if (session != null ) {
- session.close();
- }
- }
3. Compass 的 API
- CompassConfiguration cfg = new CompassConfiguration().configure();
- Compass compassSessionFactory = cfg.buildCompass();
- CompassSession session = null ;
- CompassTransaction tx = null ;
- try {
- session = compassSessionFactory.openSession();
- tx = session.beginTransaction();
- // 更新操作
- tx.commit();
- } catch (Exception e) {
- if (tx != null ) {
- tx.rollback();
- }
- throw e;
- } finally {
- if (session != null ) {
- session.close();
- }
- }
4. 比较看来, Compass 的 API 简直和 Hibernate 如出一辙。具体,下面写一个 HelloWorld :
** 导入 jar 包:
dist\commons-logging.jar
dist\compass-2.2.0.jar
dist\lucene\*.jar
2.2 是 Compass 最新的版本,但是使用的 lucene 版本却是 2.4 的
lucene2.4 和 现在 3.0 版本的比起来不但 API 有些较大的变化,效率上 3.0 也更好。但是此处不能将 Lucene 的版本换成 3.0 。那样 Compass 底层就没办法工作了。
** 在 src 目录下新建 XML 文件 : compass.cfg.xml
- <? xml version = "1.0" encoding = "UTF-8" ?>
- < compass-core-config xmlns = "http://www.compass-project.org/schema/core-config" xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation ="http://www.compass-project.org/schema/core-config
- http://www.compass-project.org/schema/compass-core-config-2.2.xsd">
- < compass name = "default" >
- <!-- 1,连接信息 -->
- < connection >
- <!-- 索引库创建的位置 -->
- < file path = "./indexDir/" />
- </ connection >
- <!-- 2,声明映射信息 -->
- < mappings />
- <!-- 3,其他配置 -->
- < settings />
- </ compass >
- </ compass-core-config >
** HelloWorld
domain : Article
- /**
- * 文章实体
- */
- @Searchable // 将实体纳入 Compass 索引库 -- 实体 映射管理中
- public class Article {
- @SearchableId
- private Long id;
- @SearchableProperty (store = Store.YES, index = Index.ANALYZED) // 意思参见 Lucene-HelloWorld
- private String title;
- @SearchableProperty (store = Store.YES, index = Index.ANALYZED)
- private String content;
- public Long getId() {
- return id;
- }
- public void setId(Long id) {
- this .id = id;
- }
- public String getTitle() {
- return title;
- }
- public void setTitle(String title) {
- this .title = title;
- }
- public String getContent() {
- return content;
- }
- public void setContent(String content) {
- this .content = content;
- }
- }
** 在 compass.cfg.xml 中映射实体类:
<mappings>
<class name="compass.helloworld.domain.Article" />
</mappings>
** HelloWorld
- public class HelloWorld {
- // 创建索引(模拟发帖的操作)
- @Test
- public void createIndex() throws Exception {
- // 模拟一条文章数据
- Article article = new Article();
- article.setId(1L);
- article.setTitle("wjh上天山" );
- article.setContent("据悉,wjh已于昨日抵达天山。高歌一曲HelloWorld" );
- // 保存到索引库中
- CompassSession session = CompassUtils.getCompassSessionFactory().openSession();
- CompassTransaction tx = session.beginTransaction();
- session.create(article); // 创建索引,并非 sesion.save(),save() 方法在 Compass 中表示 “保存或更新”
- tx.commit();
- session.close();
- }
- // 搜索(模拟搜索文章的操作)
- @Test
- public void search() throws Exception {
- // 搜索条件
- String queryString = "HelloWorld" ;
- CompassSession session = CompassUtils.getCompassSessionFactory().openSession();
- CompassTransaction tx = session.beginTransaction();
- CompassHits hits = session.find(queryString);
- int count = hits.length(); // 符合条件的总结果数
- // 处理结果
- List<Article> list = new ArrayList<Article>();
- for ( int i = 0 ; i < hits.length(); i++) {
- Article article = (Article) hits.data(i); // 真正的取出数据
- list.add(article);
- }
- tx.commit();
- session.close();
- // 显示结果
- System.out.println("符合条件的总结果数:" + count);
- for (Article article : list) {
- System.out.println("-------> id = " + article.getId());
- System.out.println("title = " + article.getTitle());
- System.out.println("content = " + article.getContent());
- }
- }
- }
5. 使用 compass 维护索引库
** CompassUtils
- public class CompassUtils {
- // Compass对象只需要一个就可以了
- private static Compass compassSessionFactory;
- static {
- CompassConfiguration cfg = new CompassConfiguration().configure();
- compassSessionFactory = cfg.buildCompass();
- }
- /**
- * 获取Compass对象
- *
- * @return
- */
- public static Compass getCompassSessionFactory() {
- return compassSessionFactory;
- }
- }
** QueryResult
- /**
- * 分页搜索结果集
- */
- public class QueryResult {
- /* 总记录数 */
- private int count;
- /* 一页的数据 */
- private List list;
- public QueryResult( int count, List list) {
- this .count = count;
- this .list = list;
- }
- public int getCount() {
- return count;
- }
- public void setCount( int count) {
- this .count = count;
- }
- public List getList() {
- return list;
- }
- public void setList(List list) {
- this .list = list;
- }
- }
** DAO
- /**
- * DAO
- */
- public class ArticleIndexDao {
- /**
- * 保存到索引库(创建索引)
- *
- * @param article
- */
- public void save(Article article) {
- CompassSession session = CompassUtils.getCompassSessionFactory().openSession();
- CompassTransaction tx = null ;
- try {
- tx = session.beginTransaction();
- session.create(article);// 创建索引
- tx.commit();
- } catch (Exception e) {
- tx.rollback();
- throw new RuntimeException(e);
- } finally {
- session.close();
- }
- }
- /**
- * 删除索引
- *
- * @param id
- */
- public void delete(Long id) {
- CompassSession session = CompassUtils.getCompassSessionFactory().openSession();
- CompassTransaction tx = null ;
- try {
- tx = session.beginTransaction();
- session.delete(Article.class , id); // 删除索引
- tx.commit();
- } catch (Exception e) {
- tx.rollback();
- throw new RuntimeException(e);
- } finally {
- session.close();
- }
- }
- /**
- * 更新索引
- *
- * @param article
- */
- public void update(Article article) {
- CompassSession session = CompassUtils.getCompassSessionFactory().openSession();
- CompassTransaction tx = null ;
- try {
- tx = session.beginTransaction();
- session.save(article); // 更新索引(创建或更新索引)
- tx.commit();
- } catch (Exception e) {
- tx.rollback();
- throw new RuntimeException(e);
- } finally {
- session.close();
- }
- }
- /**
- * 搜索
- *
- * @param queryString
- * 搜索条件
- * @param firstResult
- * @param maxResults
- * @return 一段数据 + 总记录数
- */
- public QueryResult search(String queryString, int firstResult, int maxResults) {
- CompassSession session = CompassUtils.getCompassSessionFactory().openSession();
- CompassTransaction tx = null ;
- try {
- tx = session.beginTransaction();
- // 查询
- CompassHits hits = session.find(queryString);
- int count = hits.length(); // 总结果数
- // 处理数据列表
- List<Article> list = new ArrayList<Article>();
- int endIndex = Math.min(firstResult + maxResults, hits.length());
- for ( int i = firstResult; i < endIndex; i++) { // 只取一段数据
- Article article = (Article) hits.data(i);
- // ===========================================
- // 进行高亮操作。返回高亮后的一段文本,如果高亮的属性中没有搜索的关键词,则返回null
- String text = hits.highlighter(i).fragment("content" );
- if (text != null ) {
- article.setContent(text); // 替换为高亮后的摘要
- }
- // ===========================================
- list.add(article);
- }
- tx.commit();
- // 返回结果
- return new QueryResult(count, list);
- } catch (Exception e) {
- tx.rollback();
- throw new RuntimeException(e);
- } finally {
- session.close();
- }
- }
- }
- /**
- * 由于在编写过程中,常常需要编写大量模板代码。
- * 于是 compass 也提供了一个封装后的模板 CompassTemplate,使用起来代码更简洁
- */
- public class ArticleIndexDao2 {
- /**
- * 保存到索引库(创建索引)
- *
- * @param article
- */
- public void save(Article article) {
- CompassTemplate compassTemplate = new CompassTemplate(CompassUtils.getCompassSessionFactory());
- compassTemplate.create(article);
- }
- /**
- * 删除索引
- *
- * @param id
- */
- public void delete(Long id) {
- CompassTemplate compassTemplate = new CompassTemplate(CompassUtils.getCompassSessionFactory());
- compassTemplate.delete(Article.class , id);
- }
- /**
- * 更新索引
- *
- * @param article
- */
- public void update(Article article) {
- CompassTemplate compassTemplate = new CompassTemplate(CompassUtils.getCompassSessionFactory());
- compassTemplate.save(article);
- }
- /**
- * 搜索
- *
- * @param queryString
- * 搜索条件
- * @param firstResult
- * @param maxResults
- * @return 一段数据 + 总记录数
- */
- public QueryResult search( final String queryString, final int firstResult, final int maxResults) {
- CompassTemplate compassTemplate = new CompassTemplate(CompassUtils.getCompassSessionFactory());
- return compassTemplate.execute( new CompassCallback<QueryResult>() {
- public QueryResult doInCompass(CompassSession session) throws CompassException {
- // 查询
- CompassHits hits = session.find(queryString);
- int count = hits.length(); // 总结果数
- // 处理数据列表
- List<Article> list = new ArrayList<Article>();
- int endIndex = Math.min(firstResult + maxResults, hits.length());
- for ( int i = firstResult; i < endIndex; i++) { // 只取一段数据
- Article article = (Article) hits.data(i);
- list.add(article);
- }
- // 返回结果
- return new QueryResult(count, list);
- }
- });
- }
- }
6. 对搜索关键字高亮显示
在 compass.cfg.xml 中 <settings> 节点中加入配置:
<settings>
<!-- 高亮器:摘要大小 -->
<setting name="compass.engine.highlighter.default.fragmenter.simple.size" value="100" />
<!-- 高亮器:前缀 <font color="#FF0000"> -->
<setting name="compass.engine.highlighter.default.formatter.simple.pre" value="<font color='red'>" />
<!-- 高亮器:后缀 </font> -->
<setting name="compass.engine.highlighter.default.formatter.simple.post" value="</font >"/>
</settings>
7. 自定义分词器:
<!-- 这里使用的是 " 极易分词 " 。要注意的时候,分词器必须要是支持 Lucene2.4 的版本 -->
<setting name="compass.engine.analyzer.default.type" value="jeasy.analysis.MMAnalyzer" />
8. 排序
** 影响搜索结果的 “相关度” 排序。 Lucene 默认是通过对匹配结果按照 " 关键字出现的次数 " 、 " 关键词出现的密集程度 " 等,用某种算法形成 “相关度得分”实现排序,得分越高排序越靠前。
程序员可以人为影响搜索结果的相关度。
例如本例中,只需要为实体类 Article 增加一个属性,并加上对应注解:
@SearchableBoostProperty
private float boostValue = 1F;
属性名称随意,但是一定要加上 "@SearchableBoostProperty" 注解,并设置默认值为 1 。设置了这个属性之后,每次保存这个实体时,若不改变这个属性的值,每次搜索出实体数据时,它的相关度都会被乘以 1 。也就是不变。
但是若将这个值设置为 2 的话,搜索时,这个实体对象对应的数据的相关度将乘以 2 。本来它的相关度可能仅仅为 0.4 ,但是乘以 2 后,就成了 0.8 了。它在搜索结果中的排名将大大靠前。例如:
Article article = new Article();
article.setId(27L);
article.setTitle("Title");
article.setContent("content content content");