一、准备

个人在学习中采用Struts2 + Hibernate3.2 + Spring2.5 + Compass2.2.0, 一下图片为本次学习中用到的jar包:

图中圈出的jar包为本次学习的主要部分,另外用绿色框圈出的jar包为分词器,主要用来做实验看分词效果的,选用一个即可。

二、什么是Compass

Compass是一个Java搜索框架。它封装了Lucene,增加了一些Lucene不支持的特性(例如实时更新索引),支持各种数据(Java对象、xml、json)到索引的映射,支持各种数据源(JDBC, Hibernate, iBatis)

图解:

  • Compass - 一般在程序启动时建立并被整个程序共享,主要用于建立CompassSession并通过其管理索引数据。与Hibernate的SessionFactory类似
  • CompassSession - 用于处理数据的session。与Hibernate的Session类似
  • CompassTransaction - 手动进行事务管理,如果不使用,Compass会自动管理事务。与Hibenate的事物管理类似
  • CompassTemplate - 将session和transaction透明化。类似Spring提供的HibernateTemplate
  • 数据到索引的各种映射 - OSEM, XSEM, JSEM, RSEM。支持通过程序、XML、JSON进行配置。
  • CompassGps - Gps的核心模块,管理GpsDevice,有两种实现:SingleCompassGps和DualCompassGps。
  • CompassGpsDevice - 处理各种数据源到索引的操作:JDBC, Hibernate, iBatis等。不能独立使用而必须融合到CompassGps中。

三、与Spring、Hibernate整合

这里主要结合代码进行。

1.数据库脚本(Oracle)

  1. --创建表Article
  2. create table ARTICLE
  3. (
  4. ID      NUMBER,--ID,主键
  5. TITLE   VARCHAR2(100),--标题
  6. CONTENT CLOB,--文章内容
  7. PUBDATE DATE--发布日期
  8. )

--创建表Article
create table ARTICLE
(
ID NUMBER,--ID,主键
TITLE VARCHAR2(100),--标题
CONTENT CLOB,--文章内容
PUBDATE DATE--发布日期
)

2.配置Compass的OSEM 以及Hibernate映射

  1. import java.io.Serializable;
  2. import java.util.Date;
  3. import javax.persistence.Column;
  4. import javax.persistence.Entity;
  5. import javax.persistence.GeneratedValue;
  6. import javax.persistence.Id;
  7. import javax.persistence.Lob;
  8. import javax.persistence.Table;
  9. import javax.persistence.Temporal;
  10. import javax.persistence.TemporalType;
  11. import org.compass.annotations.Index;
  12. import org.compass.annotations.Searchable;
  13. import org.compass.annotations.SearchableId;
  14. import org.compass.annotations.SearchableProperty;
  15. import org.compass.annotations.Store;
  16. import org.hibernate.annotations.GenericGenerator;
  17. @Searchable(alias = "article")
  18. @Entity
  19. @Table(name = "ARTICLE", schema = "SCOTT")
  20. public class Article implements Serializable {
  21. private static final long serialVersionUID = 1L;
  22. private Long id;
  23. private String title;
  24. private Date pubdate = new Date();
  25. private String content;
  26. @SearchableId(
  27. name = "id",
  28. store = Store.NO,
  29. index = Index.NOT_ANALYZED)
  30. @Id
  31. @GeneratedValue(generator = "paymentableGenerator")
  32. @GenericGenerator(name = "paymentableGenerator", strategy = "increment")
  33. public Long getId() {
  34. return id;
  35. }
  36. public void setId(Long id) {
  37. this.id = id;
  38. }
  39. @SearchableProperty(
  40. name = "title",
  41. store = Store.YES,
  42. index = Index.ANALYZED)
  43. @Column(name = "TITLE")
  44. public String getTitle() {
  45. return title;
  46. }
  47. public void setTitle(String title) {
  48. this.title = title;
  49. }
  50. @SearchableProperty(
  51. name = "pubdate",
  52. store = Store.NO,
  53. index = Index.UN_TOKENIZED)
  54. @Temporal(TemporalType.TIMESTAMP)
  55. @Column(name = "PUBDATE")
  56. public Date getPubdate() {
  57. return pubdate;
  58. }
  59. public void setPubdate(Date pubdate) {
  60. this.pubdate = pubdate;
  61. }
  62. @SearchableProperty(
  63. name = "content",
  64. index = Index.TOKENIZED,
  65. store = Store.YES,
  66. converter = "htmlPropertyConverter")
  67. @Lob
  68. @Column(name = "CONTENT")
  69. public String getContent() {
  70. return content;
  71. }
  72. public void setContent(String content) {
  73. this.content = content;
  74. }
  75. }

import java.io.Serializable;
import java.util.Date;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Lob;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;

import org.compass.annotations.Index;
import org.compass.annotations.Searchable;
import org.compass.annotations.SearchableId;
import org.compass.annotations.SearchableProperty;
import org.compass.annotations.Store;
import org.hibernate.annotations.GenericGenerator;

@Searchable(alias = "article")
@Entity
@Table(name = "ARTICLE", schema = "SCOTT")
public class Article implements Serializable {

private static final long serialVersionUID = 1L;

private Long id;
private String title;
private Date pubdate = new Date();
private String content;

@SearchableId(
name = "id",
store = Store.NO,
index = Index.NOT_ANALYZED)
@Id
@GeneratedValue(generator = "paymentableGenerator")
@GenericGenerator(name = "paymentableGenerator", strategy = "increment")
public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

@SearchableProperty(
name = "title",
store = Store.YES,
index = Index.ANALYZED)
@Column(name = "TITLE")
public String getTitle() {
return title;
}

public void setTitle(String title) {
this.title = title;
}

@SearchableProperty(
name = "pubdate",
store = Store.NO,
index = Index.UN_TOKENIZED)
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "PUBDATE")
public Date getPubdate() {
return pubdate;
}

public void setPubdate(Date pubdate) {
this.pubdate = pubdate;
}

@SearchableProperty(
name = "content",
index = Index.TOKENIZED,
store = Store.YES,
converter = "htmlPropertyConverter")
@Lob
@Column(name = "CONTENT")
public String getContent() {
return content;
}

public void setContent(String content) {
this.content = content;
}

}

说明:

@Searchable(alias="article")表示这个是可以搜索实体,别名为article.
    @SearchableId 
这个是实体搜索的标识ID,和hibernate里的概念差不多,用来区分索引文件里的实体索引。
    @SearchableProperty(index
= Index.NOT_ANALYZED, store = Store.NO) 表示这个属性存入索引文件,不进行分词,不存储要索引中。

另外在getContent()方法上的@SearchableProperty中还加入了converter =
"htmlPropertyConverter",主要是用来将文章中的HTML标签进行过滤获取纯文本,在建立到索引中。在后面会具体介绍这个转换器。

3.建立Compass搜索的类

  1. import java.util.ArrayList;
  2. import java.util.List;
  3. import javax.annotation.Resource;
  4. import org.compass.core.Compass;
  5. import org.compass.core.CompassHighlighter;
  6. import org.compass.core.CompassHits;
  7. import org.compass.core.CompassQuery;
  8. import org.compass.core.CompassQueryBuilder;
  9. import org.compass.core.CompassSession;
  10. import org.compass.core.CompassTemplate;
  11. import org.compass.core.CompassHighlighter.TextTokenizer;
  12. import org.compass.core.CompassQuery.SortPropertyType;
  13. import org.springframework.stereotype.Component;
  14. import com.compass.example.dao.SearchArticleDao;
  15. import com.compass.example.model.Article;
  16. @Component("SearchArticleDao")
  17. public class SearchArticleDaoCompass implements SearchArticleDao {
  18. @Resource
  19. private CompassTemplate compassTemplate;
  20. @Override
  21. public List<Article> searchWithList(final String queryString) {
  22. Compass compass = compassTemplate.getCompass();
  23. CompassSession session = compass.openSession();
  24. CompassQueryBuilder builder = session.queryBuilder();
  25. CompassQuery compassQuery = builder.queryString(queryString).toQuery().addSort("article.id",SortPropertyType.STRING);
  26. CompassHits compassHits = compassQuery.hits();
  27. List<Article> articles = new ArrayList<Article>();
  28. for(int i=0; i<compassHits.length(); i++) {
  29. Article article = (Article) compassHits.data(i);
  30. CompassHighlighter highlighter = compassHits.highlighter(i);
  31. String title = highlighter.fragment("title");
  32. if(title != null) {
  33. article.setTitle(title);
  34. }
  35. String content = highlighter.setTextTokenizer(TextTokenizer.AUTO).fragment("content");
  36. if(content != null) {
  37. article.setContent(content);
  38. }
  39. articles.add(article);
  40. }
  41. return articles;
  42. }
  43. }

import java.util.ArrayList;
import java.util.List;

import javax.annotation.Resource;

import org.compass.core.Compass;
import org.compass.core.CompassHighlighter;
import org.compass.core.CompassHits;
import org.compass.core.CompassQuery;
import org.compass.core.CompassQueryBuilder;
import org.compass.core.CompassSession;
import org.compass.core.CompassTemplate;
import org.compass.core.CompassHighlighter.TextTokenizer;
import org.compass.core.CompassQuery.SortPropertyType;
import org.springframework.stereotype.Component;

import com.compass.example.dao.SearchArticleDao;
import com.compass.example.model.Article;

@Component("SearchArticleDao")
public class SearchArticleDaoCompass implements SearchArticleDao {

@Resource
private CompassTemplate compassTemplate;

@Override
public List<Article> searchWithList(final String queryString) {

Compass compass = compassTemplate.getCompass();
CompassSession session = compass.openSession();
CompassQueryBuilder builder = session.queryBuilder();
CompassQuery compassQuery = builder.queryString(queryString).toQuery().addSort("article.id",SortPropertyType.STRING);
CompassHits compassHits = compassQuery.hits();

List<Article> articles = new ArrayList<Article>();
for(int i=0; i<compassHits.length(); i++) {
Article article = (Article) compassHits.data(i);
CompassHighlighter highlighter = compassHits.highlighter(i);
String title = highlighter.fragment("title");
if(title != null) {
article.setTitle(title);
}
String content = highlighter.setTextTokenizer(TextTokenizer.AUTO).fragment("content");
if(content != null) {
article.setContent(content);
}
articles.add(article);
}
return articles;
}

}

索引的查询主要是根据传过来的参数,关键字keyword,是搜索的关键字

String title = hits.highlighter(i).fragment("title");这段是检索titile这个属性有没有出现搜索的关键字,有就将它高亮(其实就是在关键字前后加个html标记设置颜色,等下可以看到在配置文件里可以自由设置高亮的颜色).
String content = hits.highlighter(i).setTextTokenizer(

CompassHighlighter.TextTokenizer.AUTO)
.fragment("content");

这段代码和上面的title具有一样的一样的功能,另外还多了个很重要的功能,自动选择正文中最匹配关键字的内容中的一部分输出。因为很多时候一篇文章几千字,我们只想显示有关键字的那部分的摘要,这时候这个功能就很方便.

4.建立索引,将在服务器启动时或定时重建索引

  1. import org.compass.gps.CompassGps;
  2. import org.springframework.beans.factory.InitializingBean;
  3. /**
  4. * 通过quertz 定时调度定时重建索引或自动随Spring ApplicationContext 启动而重建
  5. * 索引的Builder。会在启动后延时数秒新开线程调用compassGps.index()函数。
  6. * 默认会在Web应用每次启动时重建索引,可以设置BuildIndex属性为false来禁止此功能。
  7. * 也可以不用本builder,编写手动调用compasssGps.index()的代码。
  8. * @author YinGuojun
  9. *
  10. */
  11. public class CompassIndexBuilder implements InitializingBean {
  12. /*是否需要建立索引,可以被设置为false使本Builder失效*/
  13. private boolean buildIndex = false;
  14. /*索引操作线程延时启动的时间,单位为秒*/
  15. private int lazyTime = 10;
  16. /*Compass封装*/
  17. private CompassGps compassGps;
  18. private Thread indexThread = new Thread() {
  19. @Override
  20. public void run() {
  21. try {
  22. System.out.println("lazyTime: " + lazyTime);
  23. Thread.sleep(lazyTime * 1000);
  24. System.out.println("begin compass index ...");
  25. long beginTime = System.currentTimeMillis();
  26. // 重建索引.
  27. // 如果compass实体中定义的索引文件已存在,索引过程中会建立临时索引,
  28. // 索引完成后再进行覆盖.
  29. compassGps.index();
  30. long costTime = System.currentTimeMillis() - beginTime;
  31. System.out.println("compss index finished.");
  32. System.out.println("costed " + costTime + " milliseconds");
  33. } catch (InterruptedException e) {
  34. e.printStackTrace();
  35. }
  36. }
  37. };
  38. /**
  39. * 实现<code>InitializingBean</code>接口,在完成注入后调用启动索引线程.
  40. *
  41. * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
  42. */
  43. @Override
  44. public void afterPropertiesSet() throws Exception {
  45. if (buildIndex) {
  46. indexThread.setDaemon(true);
  47. indexThread.setName("Compass Indexer");
  48. indexThread.start();
  49. }
  50. }
  51. public boolean isBuildIndex() {
  52. return buildIndex;
  53. }
  54. public void setBuildIndex(boolean buildIndex) {
  55. this.buildIndex = buildIndex;
  56. }
  57. public int getLazyTime() {
  58. return lazyTime;
  59. }
  60. public void setLazyTime(int lazyTime) {
  61. this.lazyTime = lazyTime;
  62. }
  63. public CompassGps getCompassGps() {
  64. return compassGps;
  65. }
  66. public void setCompassGps(CompassGps compassGps) {
  67. this.compassGps = compassGps;
  68. }
  69. }

import org.compass.gps.CompassGps;
import org.springframework.beans.factory.InitializingBean;

/**
* 通过quertz 定时调度定时重建索引或自动随Spring ApplicationContext 启动而重建
* 索引的Builder。会在启动后延时数秒新开线程调用compassGps.index()函数。
* 默认会在Web应用每次启动时重建索引,可以设置BuildIndex属性为false来禁止此功能。
* 也可以不用本builder,编写手动调用compasssGps.index()的代码。
* @author YinGuojun
*
*/
public class CompassIndexBuilder implements InitializingBean {

/*是否需要建立索引,可以被设置为false使本Builder失效*/
private boolean buildIndex = false;
/*索引操作线程延时启动的时间,单位为秒*/
private int lazyTime = 10;
/*Compass封装*/
private CompassGps compassGps;

private Thread indexThread = new Thread() {

@Override
public void run() {
try {
System.out.println("lazyTime: " + lazyTime);
Thread.sleep(lazyTime * 1000);
System.out.println("begin compass index ...");
long beginTime = System.currentTimeMillis();

// 重建索引.
// 如果compass实体中定义的索引文件已存在,索引过程中会建立临时索引,
// 索引完成后再进行覆盖.
compassGps.index();

long costTime = System.currentTimeMillis() - beginTime;
System.out.println("compss index finished.");
System.out.println("costed " + costTime + " milliseconds");

} catch (InterruptedException e) {
e.printStackTrace();
}

}

};

/**
* 实现<code>InitializingBean</code>接口,在完成注入后调用启动索引线程.
*
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
*/
@Override
public void afterPropertiesSet() throws Exception {
if (buildIndex) {
indexThread.setDaemon(true);
indexThread.setName("Compass Indexer");
indexThread.start();
}
}

public boolean isBuildIndex() {
return buildIndex;
}

public void setBuildIndex(boolean buildIndex) {
this.buildIndex = buildIndex;
}

public int getLazyTime() {
return lazyTime;
}

public void setLazyTime(int lazyTime) {
this.lazyTime = lazyTime;
}

public CompassGps getCompassGps() {
return compassGps;
}

public void setCompassGps(CompassGps compassGps) {
this.compassGps = compassGps;
}

}

5.转换器

  1. import org.apache.log4j.Logger;
  2. import org.compass.core.Property;
  3. import org.compass.core.converter.ConversionException;
  4. import org.compass.core.converter.basic.AbstractBasicConverter;
  5. import org.compass.core.mapping.ResourcePropertyMapping;
  6. import org.compass.core.marshall.MarshallingContext;
  7. import com.compass.example.utils.StringUtil;
  8. public class HtmlPropertyConverter extends AbstractBasicConverter<String> {
  9. private static Logger logger = Logger.getLogger(HtmlPropertyConverter.class);
  10. public HtmlPropertyConverter() {
  11. super();
  12. // called by application server starting
  13. logger.info("----------HtmlPropertyConverter Initializing ...");
  14. }
  15. /**
  16. *  搜索的时候被调用
  17. */
  18. @Override
  19. protected String doFromString(String str,
  20. ResourcePropertyMapping resourcePropertyMapping,
  21. MarshallingContext context) throws ConversionException {
  22. logger.info("----------calling doFromString...");
  23. return str;
  24. }
  25. /**
  26. * 创建索引的时候被调用,此时将文本中的HTML标签过滤
  27. */
  28. @Override
  29. protected Property createProperty(String value,
  30. ResourcePropertyMapping resourcePropertyMapping,
  31. MarshallingContext context) {
  32. logger.info("----------calling createProperty...");
  33. //过滤html标签
  34. value = StringUtil.removeHTML(value);
  35. return super.createProperty(value, resourcePropertyMapping, context);
  36. }
  37. public class StringUtil {
  38. /**
  39. * Remove occurences of html, defined as any text
  40. * between the characters "<" and ">". Optionally
  41. * replace HTML tags with a space.
  42. * @param str
  43. * @param addSpace
  44. * @return
  45. */
  46. public static String removeHTML(String str, boolean addSpace) {
  47. //System.out.println(str);
  48. if(str == null) return "";
  49. StringBuffer ret = new StringBuffer(str.length());
  50. int start = 0;
  51. int beginTag = str.indexOf("<");
  52. int endTag = 0;
  53. if(beginTag == -1) return str;
  54. while(beginTag >= start) {
  55. if(beginTag > 0) {
  56. ret.append(str.substring(start, beginTag));
  57. // replace each tag with a space (looks better)
  58. if(addSpace) ret.append(" ");
  59. }
  60. endTag = str.indexOf(">", beginTag);
  61. // if endTag found move "cursor" forward
  62. if(endTag > -1) {
  63. start = endTag + 1;
  64. beginTag = str.indexOf("<", start);
  65. }
  66. // if no endTag found, get rest of str and break
  67. else {
  68. ret.append(str.substring(beginTag));
  69. break;
  70. }
  71. }
  72. // append everything after the last endTag
  73. if(endTag >-1 && endTag + 1 < str.length()) {
  74. ret.append(str.substring(endTag + 1));
  75. }
  76. //System.out.println(ret.toString());
  77. return ret.toString().trim();
  78. }
  79. /**
  80. * Remove occurences of html, defined as any text
  81. * between the characters "<" and ">".
  82. * Replace any HTML tags with a space.
  83. * @param str
  84. * @return
  85. */
  86. public static String removeHTML(String str) {
  87. return removeHTML(str, true);
  88. }
  89. }

import org.apache.log4j.Logger;
import org.compass.core.Property;
import org.compass.core.converter.ConversionException;
import org.compass.core.converter.basic.AbstractBasicConverter;
import org.compass.core.mapping.ResourcePropertyMapping;
import org.compass.core.marshall.MarshallingContext;

import com.compass.example.utils.StringUtil;

public class HtmlPropertyConverter extends AbstractBasicConverter<String> {

private static Logger logger = Logger.getLogger(HtmlPropertyConverter.class);

public HtmlPropertyConverter() {
super();
// called by application server starting
logger.info("----------HtmlPropertyConverter Initializing ...");
}

/**
* 搜索的时候被调用
*/
@Override
protected String doFromString(String str,
ResourcePropertyMapping resourcePropertyMapping,
MarshallingContext context) throws ConversionException {
logger.info("----------calling doFromString...");
return str;
}

/**
* 创建索引的时候被调用,此时将文本中的HTML标签过滤
*/
@Override
protected Property createProperty(String value,
ResourcePropertyMapping resourcePropertyMapping,
MarshallingContext context) {
logger.info("----------calling createProperty...");
//过滤html标签
value = StringUtil.removeHTML(value);

return super.createProperty(value, resourcePropertyMapping, context);
}

public class StringUtil {
/**
* Remove occurences of html, defined as any text
* between the characters "<" and ">". Optionally
* replace HTML tags with a space.
* @param str
* @param addSpace
* @return
*/
public static String removeHTML(String str, boolean addSpace) {

//System.out.println(str);

if(str == null) return "";
StringBuffer ret = new StringBuffer(str.length());
int start = 0;
int beginTag = str.indexOf("<");
int endTag = 0;
if(beginTag == -1) return str;

while(beginTag >= start) {
if(beginTag > 0) {
ret.append(str.substring(start, beginTag));

// replace each tag with a space (looks better)
if(addSpace) ret.append(" ");
}
endTag = str.indexOf(">", beginTag);

// if endTag found move "cursor" forward
if(endTag > -1) {
start = endTag + 1;
beginTag = str.indexOf("<", start);
}

// if no endTag found, get rest of str and break
else {
ret.append(str.substring(beginTag));
break;
}
}
// append everything after the last endTag
if(endTag >-1 && endTag + 1 < str.length()) {
ret.append(str.substring(endTag + 1));
}

//System.out.println(ret.toString());

return ret.toString().trim();
}

/**
* Remove occurences of html, defined as any text
* between the characters "<" and ">".
* Replace any HTML tags with a space.
* @param str
* @return
*/
public static String removeHTML(String str) {
return removeHTML(str, true);
}
}

5.配置文件

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans
  3. xmlns="http://www.springframework.org/schema/beans"
  4. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  5. xmlns:p="http://www.springframework.org/schema/p"
  6. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
  7. <bean id="annotationConfiguration" class="org.compass.annotations.config.CompassAnnotationsConfiguration"/>
  8. <bean id="compass" class="org.compass.spring.LocalCompassBean">
  9. <property name="resourceDirectoryLocations">
  10. <list>
  11. <value>classpath:com/compass</value>
  12. </list>
  13. </property>
  14. <!--  如果compass有单独的配置文件,可以从这里引入
  15. <property name="configLocation" value="classpath:compass.cfg.xml"/>
  16. -->
  17. <!-- 数据索引存储位置 -->
  18. <property name="connection" value="/lucene/indexes"/>
  19. <property name="classMappings">
  20. <list>
  21. <value>com.compass.example.model.Product</value>
  22. <value>com.compass.example.model.Article</value>
  23. </list>
  24. </property>
  25. <property name="compassConfiguration" ref="annotationConfiguration"/>
  26. <property name="compassSettings">
  27. <props>
  28. <!-- 建立索引位置的另一种方式
  29. <prop key="compass.engine.connection">
  30. file://${user.home}/lucene/indexes
  31. </prop>
  32. -->
  33. <prop key="compass.transaction.factory">
  34. org.compass.spring.transaction.SpringSyncTransactionFactory
  35. </prop>
  36. <!-- 指定摘要文本的长度 -->
  37. <prop key="compass.engine.highlighter.default.fragmenter.simple.size">
  38. 200
  39. </prop>
  40. <!-- 搜索内容高亮显示 -->
  41. <prop  key="compass.engine.highlighter.default.formatter.simple.pre">
  42. <![CDATA[<span style='background-color:yellow;color:red;'>]]>
  43. </prop>
  44. <prop  key="compass.engine.highlighter.default.formatter.simple.post">
  45. <![CDATA[</span>]]>
  46. </prop>
  47. <!--定义分词器-->
  48. <!--
  49. <prop
  50. key="compass.engine.analyzer.default.type">
  51. org.wltea.analyzer.lucene.IKAnalyzer
  52. </prop>
  53. -->
  54. <!--
  55. <prop key="compass.engine.analyzer.MMAnalyzer.CustomAnalyzer">
  56. org.wltea.analyzer.lucene.IKAnalyzer
  57. jeasy.analysis.MMAnalyzer
  58. net.paoding.analysis.analyzer.PaodingAnalyzer
  59. </prop>
  60. -->
  61. <prop key="compass.engine.analyzer.default.type">
  62. org.wltea.analyzer.lucene.IKAnalyzer
  63. </prop>
  64. </props>
  65. </property>
  66. <property name="transactionManager" ref="transactionManager"/>
  67. <property name="convertersByName">
  68. <map>
  69. <entry key="htmlPropertyConverter">
  70. <bean class="com.compass.converter.HtmlPropertyConverter"/>
  71. </entry>
  72. </map>
  73. </property>
  74. </bean>
  75. <bean id="hibernateGpsDevice" class="org.compass.gps.device.hibernate.HibernateGpsDevice">
  76. <property name="name" value="hibernateDevice"/>
  77. <property name="sessionFactory" ref="sessionFactory"/>
  78. <property name="mirrorDataChanges" value="true"/>
  79. </bean>
  80. <!-- 数据库中的记录变化后同步更新索引 -->
  81. <bean id="compassGps" class="org.compass.gps.impl.SingleCompassGps" init-method="start" destroy-method="stop">
  82. <property name="compass" ref="compass"/>
  83. <property name="gpsDevices">
  84. <list>
  85. <!-- compass2.1
  86. <bean class="org.compass.spring.device.SpringSyncTransactionGpsDeviceWrapper">
  87. <property name="gpsDevice" ref="hibernateGpsDevice"/>
  88. </bean>
  89. -->
  90. <!-- compass2.2 -->
  91. <ref local="hibernateGpsDevice"/>
  92. </list>
  93. </property>
  94. </bean>
  95. <!-- compass模板 -->
  96. <bean id="compassTemplate" class="org.compass.core.CompassTemplate">
  97. <property name="compass" ref="compass"/>
  98. </bean>
  99. <!-- 定时重建索引(利用quartz)或随Spring ApplicationContext启动而重建索引 -->
  100. <bean id="compassIndexBuilder" lazy-init="false" class="com.compass.example.utils.CompassIndexBuilder">
  101. <property name="compassGps" ref="compassGps"/>
  102. <property name="buildIndex" value="true"/>
  103. <property name="lazyTime" value="10"/>
  104. </bean>
  105. </beans>

<?xml version="1.0" encoding="UTF-8"?>
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">

<bean id="annotationConfiguration" class="org.compass.annotations.config.CompassAnnotationsConfiguration"/>

<bean id="compass" class="org.compass.spring.LocalCompassBean">
<property name="resourceDirectoryLocations">
<list>
<value>classpath:com/compass</value>
</list>
</property>
<!-- 如果compass有单独的配置文件,可以从这里引入
<property name="configLocation" value="classpath:compass.cfg.xml"/>
-->
<!-- 数据索引存储位置 -->
<property name="connection" value="/lucene/indexes"/>

<property name="classMappings">
<list>
<value>com.compass.example.model.Product</value>
<value>com.compass.example.model.Article</value>
</list>
</property>
<property name="compassConfiguration" ref="annotationConfiguration"/>
<property name="compassSettings">
<props>
<!-- 建立索引位置的另一种方式
<prop key="compass.engine.connection">
file://${user.home}/lucene/indexes
</prop>
-->
<prop key="compass.transaction.factory">
org.compass.spring.transaction.SpringSyncTransactionFactory
</prop>

<!-- 指定摘要文本的长度 -->
<prop key="compass.engine.highlighter.default.fragmenter.simple.size">
200
</prop>

<!-- 搜索内容高亮显示 -->
<prop key="compass.engine.highlighter.default.formatter.simple.pre">
<![CDATA[<span style='background-color:yellow;color:red;'>]]>
</prop>
<prop key="compass.engine.highlighter.default.formatter.simple.post">
<![CDATA[</span>]]>
</prop>
<!--定义分词器-->
<!--
<prop
key="compass.engine.analyzer.default.type">
org.wltea.analyzer.lucene.IKAnalyzer
</prop>
-->
<!--
<prop key="compass.engine.analyzer.MMAnalyzer.CustomAnalyzer">
org.wltea.analyzer.lucene.IKAnalyzer
jeasy.analysis.MMAnalyzer
net.paoding.analysis.analyzer.PaodingAnalyzer
</prop>
-->

<prop key="compass.engine.analyzer.default.type">
org.wltea.analyzer.lucene.IKAnalyzer
</prop>

</props>
</property>
<property name="transactionManager" ref="transactionManager"/>
<property name="convertersByName">
<map>
<entry key="htmlPropertyConverter">
<bean class="com.compass.converter.HtmlPropertyConverter"/>
</entry>
</map>
</property>
</bean>

<bean id="hibernateGpsDevice" class="org.compass.gps.device.hibernate.HibernateGpsDevice">
<property name="name" value="hibernateDevice"/>
<property name="sessionFactory" ref="sessionFactory"/>
<property name="mirrorDataChanges" value="true"/>
</bean>

<!-- 数据库中的记录变化后同步更新索引 -->
<bean id="compassGps" class="org.compass.gps.impl.SingleCompassGps" init-method="start" destroy-method="stop">
<property name="compass" ref="compass"/>
<property name="gpsDevices">
<list>
<!-- compass2.1
<bean class="org.compass.spring.device.SpringSyncTransactionGpsDeviceWrapper">
<property name="gpsDevice" ref="hibernateGpsDevice"/>
</bean>
-->
<!-- compass2.2 -->
<ref local="hibernateGpsDevice"/>
</list>
</property>
</bean>

<!-- compass模板 -->
<bean id="compassTemplate" class="org.compass.core.CompassTemplate">
<property name="compass" ref="compass"/>
</bean>

<!-- 定时重建索引(利用quartz)或随Spring ApplicationContext启动而重建索引 -->
<bean id="compassIndexBuilder" lazy-init="false" class="com.compass.example.utils.CompassIndexBuilder">
<property name="compassGps" ref="compassGps"/>
<property name="buildIndex" value="true"/>
<property name="lazyTime" value="10"/>
</bean>
</beans>

6.效果(英文)

中文

四、问题总结

1.异常there are more terms than documents in field "XXX", but it's impossible to sort on tokenized fields.

在Luncene的API中对Sort的说明中有以下描述:

The fields used to determine sort order must be carefully chosen. Documents must contain a single term in such a field, and the value of the term should indicate the document's relative position in a given sort order. The field must be indexed, but should not be tokenized, and does not need to be stored

(unless you happen to want it back with the rest of your document data). In other words:

document.add (new Field ("byNumber", Integer.toString(x), Field.Store.NO, Field.Index.NOT_ANALYZED));

描述中红色部分需特别注意,排序的字段必须被索引,并且不应该被tokennized,也就是在注解@SearchableProperty中的index=Index.NOT_ANALYZED, store=Store.NO,括号中的说明不是很明白,希望知道的可以给我点提示,再此谢谢了。

2.异常java.lang.RuntimeException: field "XXX" does not appear to be indexed

对多个表建索引后进行搜索,在添加排序条件时,如果不指定SortPropertyType,那么在没有指定converter的字段上排序时会抛以上异常, 但如果只对单个表建索引,不会有这个问题。

五、本次学习在网上查找各种资料的汇总,对引用到别处博客内容的博主表示感谢!文章尚有很多不完善之处,望指正,本人不胜感激!

六、其他资料

Compass入门

http://www.yeeach.com/2008/03/23/compass-%E5%85%A5%E9%97%A8%E6%8C%87%E5%8D%97/

关于高亮显示的解决方案

http://jdkcn.com/entry/the-better-revolution-about-the-compass-lucene-highlight.html,此网站开放源码,有助于大家学习。

Lucene、Compass学习以及与SSH的整合的更多相关文章

  1. SSH项目整合教学Eclipse搭建SSH(Struts2+Spring3+Hibernate3)

    这篇博文的目的 尝试搭建一个完整的SSH框架项目. 给以后的自己,也给别人一个参考. 读博文前应该注意: 本文提纲:本文通过一个用户注册的实例讲解SSH的整合.创建Struts项目,整合Hiberna ...

  2. ssh框架整合之登录以及增删改查

    1.首先阐述一下我用得开发工具,myeclipse2017+oracle,所以我的基本配置步骤可能不一样,下面我用几张图来详解我的开发步骤. ---1先配置structs (Target 选择apac ...

  3. MVC+Spring.NET+NHibernate .NET SSH框架整合 C# 委托异步 和 async /await 两种实现的异步 如何消除点击按钮时周围出现的白线? Linq中 AsQueryable(), AsEnumerable()和ToList()的区别和用法

    MVC+Spring.NET+NHibernate .NET SSH框架整合   在JAVA中,SSH框架可谓是无人不晓,就和.NET中的MVC框架一样普及.作为一个初学者,可以感受到.NET出了MV ...

  4. FreeMarker与SSH项目整合流程

    FreeMarker与SSH项目整合流程 学习了SSH之后,一般为了减少数据库的压力,会使用FreeMarker来生成静态HTML页面.下面简单说一下FreeMarker与SSH项目的整合全过程~ 前 ...

  5. jackson学习之九:springboot整合(配置文件)

    欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos 系列文章汇总 jackson学习之一:基本信息 jac ...

  6. SSH框架整合

    SSH框架整合 一.原理图 action:(struts2) 1.获取表单的数据 2.表单的验证,例如非空验证,email验证等 3.调用service,并把数据传递给service Service: ...

  7. Git学习笔记与IntelliJ IDEA整合

    Git学习笔记与IntelliJ IDEA整合 一.Git学习笔记(基于Github) 1.安装和配置Git 下载地址:http://git-scm.com/downloads Git简要使用说明:h ...

  8. ssh注解整合

    ssh注解整合 导入java包 配置struts2环境 1. 创建struts.xml配置文件 <?xml version="1.0" encoding="UTF- ...

  9. 【Todo】Lucene系统学习

    之前已经写过一篇关于Lucene安装学习的文章:http://www.cnblogs.com/charlesblc/p/5980525.html 还有一篇关于Solr安装使用的文章:http://ww ...

随机推荐

  1. [转]Android 如何监听返回键,弹出一个退出对话框

    本文转自:http://blog.csdn.net/sunnyfans/article/details/8094349 Android 如何监听返回键点击事件,并创建一个退出对话框, 防止自己写的应用 ...

  2. iOS中使用 Reachability 检测网络区分手机网络类型 WiFi 和2 3 4 G

    如果你想在iOS程序中提供一仅在wifi网络下使用(Reeder),或者在没有网络状态下提供离线模式(Evernote).那么你会使用到Reachability来实现网络检测. 写本文的目的 了解Re ...

  3. 版本号比较versioncompare方法,java实现

    测试

  4. Proteus与Keil连接及其仿真(有例子哦!)

    记录一下Proteus仿真的一些设置和使用,以方便自己以后复习和大家交流!如有错误,希望大家指正. 1.Proteus软件的安装,这里就不作说明了.

  5. JavaScript 的垃圾回收与内存泄露

    JavaScript采用垃圾自动回收机制,运行时环境会自动清理不再使用的内存,因此javascript无需像C++等语言一样手动释放无用内存. 在这之前先说一下垃圾回收的两种方式:引用计数与标记清除. ...

  6. 为什么ABAP整型的1转成string之后,后面会多个空格

    有同事问这个问题:lv_s是从int4转过来的,长度为2,和硬编码的lv_s2(长度为1)相比,后面多了个空格. 为什么?查SAP ABAP的编程帮助F1 help. 帮助文档说的很清楚了:如果赋值操 ...

  7. Qt 为QPushButton、QLabel添加鼠标移入移出事件

    QT 为QPushButton.QLabel添加鼠标移入移出事件**要实现的效果:**鼠标移入QPushButton时与移出时按钮变换字体颜色,鼠标移入QLabel时显示上面的文字,移出时不显示.** ...

  8. Python3简明教程(四)—— 流程控制之分支

    我们通过 if-else 语句来做决定,来改变程序运行的流程. if语句 语法如下: if expression: do this 如果表达式 expression 的值为真(不为零的任何值都为真), ...

  9. Asp.Net Core 入门(三) —— 自定义中间件

    上一篇我们讲了Startup文件,其中着重介绍了中间件,现在我们就来自定义我们自己的中间件吧. 中间件通常封装在一个类中,并使用扩展方法进行暴露.它需要拥有一个类型为RequestDelegate的成 ...

  10. ES6 第三章 变量的解构赋值 具体参照http://es6.ruanyifeng.com

    1.基本用法 let [a, b, c] = [1, 2, 3];左右两边解构格式要保持一致. 2.默认值 let [x, y = 'b'] = ['a']; // x='a', y='b' let ...