- 浏览: 483952 次
- 性别:
- 来自: 武汉
最新评论
-
zyzyzy123:
请问有工程吗,我现在正在实现打电话的功能,但是一直不通,怀疑是 ...
实用的java 串口通信程序 -
wuhaitong:
引用[img][/img][*][url][/url] ...
jbpm -
迷糊_le:
maven命令, 蛮好的,谢谢
maven eclipse -
Wuaner:
不错的文章 , 谢谢分享!
Hadoop -
yuqihengsheng:
strong 很细
HighLighter
Compass 指南
在新架构中打算选择Compass或Hibernate Search作为搜索引擎框架,比较后,感觉Hibernate Search上还是没有Compass成熟,另外考虑到后期对网页的爬取及搜索需求,决定还是基于Compass来作为架构缺省的搜索引擎。网上关于Compass的文档很多,但说得相对完整其详细的入门文档基本上没有,Compass的官方文档倒是说得很详细,但是例子一塌糊涂,存在很大问题。记录一下搭建的过程,作为入门的指南。
Compass 通过OSEM(Object/Search Engine Mapping)允许把应用对象的领域模型映射到搜索引擎,最终通过访问common meta data来达到访问对象的目的。
1、几个核心概念
1.1、annotation vs. xml配置文件
Compass的配置文件主要分成三类:
第一类:*.cmd.xml文件*
.cmd.xml文件是对common meta data进行定义,定义了最终搜索的结果中的最基本的元数据。
第二类:*.cpm.xml文件
*.cpm.xml是Object/Search Engine Mapping,提供了POJO到common meta data的映射。
第三类:*.cfg.xml文件
Compass的*.cfg.xml定义了Compass的Index存放路径、搜索引擎分词等相关信息。
与采用xml配置文件相比较,采用Annonation方式还是相对简单,尤其是采用Spring时候,不用写*.cmd.xml文件、*.cpm.xml、*.cfg.xml,相对很方便,而且不像Hibernate的Annonation很多,Compass的Annonation的核心标注只有@Searchable、@SearchableId、@SearchableProperty、@SearchableComponent个,很容易记忆。因此推荐使用Annonation方式
1.2、Compass核心API
Compass的核心API借鉴了Hibernate的术语,因此在操作上基本上与Hibernate类似,以下为Compass的几个核心接口:
CompassConfiguration(类似Hibernate Configuration):用来在一些设置参数、配置文件和映射定义上配置Compass。通常用来创建Compass接口。
Compass(类似Hibernate SessionFactory):为单线程使用,创建线程安全的实例来打开Compass Seesion。同样还提供了一些搜索引擎索引级别的操作。
CompassSesssion(类似Hibernate Session):用来执行像保存、删除、查找、装载这样的搜索操作。很轻量但是并不是线程安全的。
CompassTransaction(类似Hibernate Transaction):管理Compass事务的接口。使用它并不需要事务管理环境(像Spring、JTA)。
1.3、Compass与Spring集成
Compass已经对对spring集成做了很好的封装,同时与Spring对Hibernate的支持类似,Compass也提供了CompassTemplate来简化诸如对Session、Transaction、Exception等操作,尽量充分使用此工具,可以有效提高效率。例如:
CompassTemplate ct = (CompassTemplate) context.getBean("compassTemplate");
Article article = new Article();
article.setTitle("Compass Test");
article.setPublishDate(new Date());
article.setAuthor(1);
ct.save(article); //存储对象需要索引的数据到Compass的索引中。
2、软件环境
Spring :2.5
Compas:1.2.1
Hibernate:3.2.5
Mysql :5.0.5
3、数据库脚本
CREATE TABLE `article` (
`Id` int(11) NOT NULL auto_increment,
`title` varchar(40) NOT NULL default '',
`author` int(11) default '0',
`publish_date` date NOT NULL default '0000-00-00',
PRIMARY KEY (`Id`) ) TYPE=MyISAM;
CREATE TABLE `author` (
`Id` int(11) NOT NULL auto_increment,
`username` varchar(20) NOT NULL default '',
`password` varchar(20) NOT NULL default '',
`age` smallint(6) default '0',
PRIMARY KEY (`Id`) ) TYPE=MyISAM;
4、测试用例
从测试用例讲起比较容易把关系理清楚,不然一堆术语和概念很让人晕乎。
import org.apache.log4j.Logger;
import java.util.Date;
import junit.framework.TestCase;
import org.compass.core.Compass;
import org.compass.core.CompassDetachedHits;
import org.compass.core.CompassHit;
import org.compass.core.CompassHits;
import org.compass.core.CompassSession;
import org.compass.core.CompassTemplate;
import org.compass.core.CompassTransaction;
import org.compass.core.support.search.CompassSearchCommand;
import org.compass.core.support.search.CompassSearchResults;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.mobilesoft.esales.dao.hibernate.ArticleDAO;
import com.mobilesoft.esales.dao.hibernate.AuthorDAO;
import com.mobilesoft.esales.model.Article;
import com.mobilesoft.esales.model.Author;
import com.mobilesoft.framework.search.service.CompassSearchService;
/**
* Compass服务使用的测试用例
*
* @author liangchuan@mobile-soft.cn
*
*/
public class TestCompass extends TestCase {
private static final Logger logger = Logger.getLogger(TestCompass.class);
private static ClassPathXmlApplicationContext context = null;
private static CompassTemplate ct;
static {
context = new ClassPathXmlApplicationContext(new String[] {
"applicationContext.xml", "applicationContext-resources.xml",
"applicationContext-dao.xml", "applicationContext-service.xml",
"applicationContext-compass.xml" });
ct = (CompassTemplate) context.getBean("compassTemplate");
}
protected void setUp() throws Exception {
}
/**
* 插入测试数据
*/
public void testInsert() {
ArticleDAO articleDao = (ArticleDAO) context.getBean("articleDAO");
AuthorDAO authorDao = (AuthorDAO) context.getBean("authorDAO");
Article article = new Article();
Author author = new Author();
author.setAge((short) 27);
author.setUsername("liangchuan");
author.setPassword("liangchuan");
article.setTitle("Compass Test");
article.setPublishDate(new Date());
article.setAuthor(1);
authorDao.save(author);
articleDao.save(article);
ct.save(article);
ct.save(author);
}
/**
* 用于测试使用CompassTransaction事务方式
*/
public void testTransactionalFind() {
Compass compass = ct.getCompass();
CompassSession session = compass.openSession();
CompassTransaction tx = null;
try {
tx = session.beginTransaction();
CompassHits hits = session.find("Compass*");
logger.error("testTransactionalFind() - CompassHits hits="
+ hits.getLength());
for (int i = 0; i < hits.getLength(); i++) {
Object hit = hits.data(i);
if (hit instanceof Article) {
Article item = (Article) hit;
logger.error("testTransactionalFind() - article hits="
+ item.getTitle());
} else if (hit instanceof Author) {
Author item = (Author) hit;
logger.error("testTransactionalFind() - author hits="
+ item.getUsername());
} else {
logger.error("testTransactionalFind() - error hits=");
}
}
tx.commit();
} catch (Exception e) {
if (tx != null) {
tx.rollback();
}
} finally {
session.close();
}
}
/**
* 用于演示CompassDetachedHits的使用。
* 由于CompassTempalte得到的结果集必须在transactionalcontext中才能使用,
* 因此必须使用CompassDetachedHits方式测试CompassDetachedHits方式
*/
public void testDetachedFind() {
// 由于CompassTempalte得到的结果集必须在transactional
// context中才能使用,因此必须使用CompassDetachedHits方式
// 测试CompassDetachedHits方式
CompassDetachedHits hits = ct.findWithDetach("Compass*");
logger.error("testDetachedFind() - CompassHits hits="
+ hits.getLength());
for (int i = 0; i < hits.getLength(); i++) {
Object hit = hits.data(i);
if (hit instanceof Article) {
Article item = (Article) hit;
logger.error("testDetachedFind() - article hits="
+ item.getTitle());
} else if (hit instanceof Author) {
Author item = (Author) hit;
logger.error("testDetachedFind() - author hits="
+ item.getUsername());
} else {
logger.error("testDetachedFind() - error hits=");
}
}
}
/**
* 用于演示com.mobilesoft.framework.search.service.CompassSearchService的使用
*
*/
class CompassSearch extends CompassSearchService{
CompassSearch(){
Compass compass = ct.getCompass();
CompassSession session = compass.openSession();
CompassTransaction tx = null;
try {
tx = session.beginTransaction();
CompassSearchCommand command = new CompassSearchCommand();
command.setQuery("Compass");
CompassSearchResults results= performSearch(command,session);
logger.error("CompassSearch() - CompassHit TotalHits value=" +results.getTotalHits());
for (int i = 0; i < results.getHits().length; i++) {
CompassHit hits=results.getHits()[i];
Object hit=hits.getData();
logger.error("CompassSearch() - CompassHit hit=" + hit); //$NON-NLS-1$
if (hit instanceof Article) {
Article item = (Article) hit;
logger.error("testCompassSearchService() - article hits="
+ item.getTitle());
} else if (hit instanceof Author) {
Author item = (Author) hit;
logger.error("testCompassSearchService() - author hits="
+ item.getUsername());
} else {
logger.error("testCompassSearchService() - error hits=");
}
tx.commit();
}
} catch (Exception e) {
if (tx != null) {
tx.rollback();
}
} finally {
session.close();
}
}
}
public void testCompassSearchService() {
new CompassSearch();
}
protected void tearDown() throws Exception {
}
}
5、配置文件
applicationContext-compass.xml
<?xml version="1.0"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN"
"http://www.springframework.org/dtd/spring-beans-2.0.dtd">
<beans default-lazy-init="true">
<bean id="compassTemplate" class="org.compass.core.CompassTemplate">
<property name="compass" ref="compass"/>
</bean>
<bean id="annotationConfiguration"
class="org.compass.annotations.config.CompassAnnotationsConfiguration">
</bean>
<bean id="compass" class="org.compass.spring.LocalCompassBean">
<property name="classMappings">
<list>
<value>com.mobilesoft.esales.model.Article</value>
<value>com.mobilesoft.esales.model.Author</value>
</list>
</property>
<property name="compassConfiguration" ref="annotationConfiguration"/>
<property name="compassSettings">
<props>
<prop key="compass.engine.connection"> file://compass </prop>
<prop key="compass.transaction.factory">
org.compass.spring.transaction.SpringSyncTransactionFactory
</prop>
<prop
key="compass.engine.highlighter.default.formatter.simple.pre">
<![CDATA[<font color="red"><b>]]>
</prop>
<prop
key="compass.engine.highlighter.default.formatter.simple.post">
<![CDATA[</b></font>]]>
</prop>
</props>
</property>
<property name="transactionManager" ref="transactionManager"/>
</bean>
<bean id="hibernateGpsDevice"
class="org.compass.gps.device.hibernate.HibernateGpsDevice">
<property name="name">
<value>hibernateDevice</value>
</property>
<property name="sessionFactory" ref="sessionFactory"/>
<property name="mirrorDataChanges">
<value>true</value>
</property>
</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>
<bean
class="org.compass.spring.device.SpringSyncTransactionGpsDeviceWrapper">
<property name="gpsDevice" ref="hibernateGpsDevice"/>
</bean>
</list>
</property>
</bean>
<bean id="compassSearchService" class="com.mobilesoft.framework.search.service.CompassSearchService">
<property name="compass" ref="compass"/>
<property name="pageSize" value="15"/>
</bean>
<!-- 定时重建索引(利用quartz)或随Spring ApplicationContext启动而重建索引 -->
<bean id="compassIndexBuilder" class="com.mobilesoft.framework.search.service.CompassIndexBuilder" lazy-init="false">
<property name="compassGps" ref="compassGps"/>
<property name="buildIndex" value="false"/>
<property name="lazyTime" value="10"/>
</bean>
</beans>
applicationContext-dao.xml、applicationContext-service.xml、applicationContext-resources.xml等略去。
6、Service层(参考了SpringSide实现)
AdvancedSearchCommand.java
package com.mobilesoft.framework.search.service;
import java.util.HashSet;
import java.util.Set;
import org.apache.commons.lang.StringUtils;
import org.compass.core.CompassQuery.SortDirection;
import org.compass.core.CompassQuery.SortPropertyType;
import org.compass.core.support.search.CompassSearchCommand;
import org.springframework.util.Assert;
public class AdvancedSearchCommand extends CompassSearchCommand {
/**
* 封装基于Compass 的排序参数.
*/
class CompassSort {
private String name;
private SortPropertyType type;
private SortDirection direction;
public CompassSort() {
}
public CompassSort(String sortParamName, String paramType,
boolean isAscend) {
Assert.isTrue(StringUtils.isNotBlank(sortParamName));
setName(sortParamName);
if ("int".equalsIgnoreCase(paramType)) {
setType(SortPropertyType.INT);
} else if ("float".equalsIgnoreCase(paramType)) {
setType(SortPropertyType.FLOAT);
} else if ("string".equalsIgnoreCase(paramType)) {
setType(SortPropertyType.STRING);
} else {
setType(SortPropertyType.AUTO);
}
if (isAscend) {
setDirection(SortDirection.AUTO);
} else {
setDirection(SortDirection.REVERSE);
}
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public SortPropertyType getType() {
return type;
}
public void setType(SortPropertyType type) {
this.type = type;
}
public SortDirection getDirection() {
return direction;
}
public void setDirection(SortDirection direction) {
this.direction = direction;
}
}
/**
* 搜索结果排序表.
*/
private Set<CompassSort> sortMap = new HashSet<CompassSort>();
private String[] highlightFields;
/**
* @param paramType 现定义了三种类型: int string 以及 float。<br>
* 除去这三种外,其他会被自动定义为SortPropertyType.AUTO 具体的可见{@link org.compass.core.CompassQuery.SortPropertyType}
* @param isAscend 顺序还是倒序排序
* @see org.compass.core.CompassQuery.SortPropertyType#AUTO
* @see org.compass.core.CompassQuery.SortPropertyType#INT
* @see org.compass.core.CompassQuery.SortPropertyType#STRING
* @see org.compass.core.CompassQuery.SortPropertyType#FLOAT
* @see org.compass.core.CompassQuery.SortDirection#AUTO
* @see org.compass.core.CompassQuery.SortDirection#REVERSE
*/
public void addSort(String sortParamName, String paramType, boolean isAscend) {
this.sortMap.add(new CompassSort(sortParamName, paramType, isAscend));
}
public Set<CompassSort> getSortMap() {
return sortMap;
}
public void setSortMap(Set<CompassSort> sortMap) {
this.sortMap = sortMap;
}
public String[] getHighlightFields() {
return highlightFields;
}
public void setHighlightFields(String[] highlightFields) {
this.highlightFields = highlightFields;
}
}
CompassIndexBuilder.java
package com.mobilesoft.framework.search.service;
import org.apache.log4j.Logger;
import org.compass.gps.CompassGps;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;
/**
* 通过quartz定时调度定时重建索引或自动随Spring ApplicationContext启动而重建索引的Builder.
* 会启动后延时数秒新开线程调用compassGps.index()函数.
* 默认会在Web应用每次启动时重建索引,可以设置buildIndex属性为false来禁止此功能.
* 也可以不用本Builder, 编写手动调用compassGps.index()的代码.
*
*/
public class CompassIndexBuilder implements InitializingBean {
private static final Logger log = Logger.getLogger(CompassIndexBuilder.class);
// 是否需要建立索引,可被设置为false使本Builder失效.
private boolean buildIndex = false;
// 索引操作线程延时启动的时间,单位为秒
private int lazyTime = 10;
// Compass封装
private CompassGps compassGps;
// 索引线程
private Thread indexThread = new Thread() {
@Override
public void run() {
try {
Thread.sleep(lazyTime * 1000);
log.info("begin compass index...");
long beginTime = System.currentTimeMillis();
// 重建索引.
// 如果compass实体中定义的索引文件已存在,索引过程中会建立临时索引,
// 索引完成后再进行覆盖.
compassGps.index();
long costTime = System.currentTimeMillis() - beginTime;
log.info("compss index finished.");
log.info("costed " + costTime + " milliseconds");
} catch (InterruptedException e) {
// simply proceed
}
}
};
/**
* 实现<code>InitializingBean</code>接口,在完成注入后调用启动索引线程.
*
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
*/
public void afterPropertiesSet() throws Exception {
if (buildIndex) {
Assert.notNull(compassGps, "CompassIndexBuilder not set CompassGps yet.");
indexThread.setDaemon(true);
indexThread.setName("Compass Indexer");
indexThread.start();
}
}
public void setBuildIndex(boolean buildIndex) {
this.buildIndex = buildIndex;
}
public void setLazyTime(int lazyTime) {
this.lazyTime = lazyTime;
}
public void setCompassGps(CompassGps compassGps) {
this.compassGps = compassGps;
}
}
CompassSearchService.java
package com.mobilesoft.framework.search.service;
import org.compass.core.Compass;
import org.compass.core.CompassCallback;
import org.compass.core.CompassDetachedHits;
import org.compass.core.CompassHits;
import org.compass.core.CompassQuery;
import org.compass.core.CompassSession;
import org.compass.core.CompassTemplate;
import org.compass.core.CompassTransaction;
import org.compass.core.support.search.CompassSearchCommand;
import org.compass.core.support.search.CompassSearchResults;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;
import com.mobilesoft.framework.search.service.AdvancedSearchCommand.CompassSort;
/**
* 仿照 {@link org.compass.spring.web.mvc.CompassSearchController}
* 中的代码,构建了一个Service,方便不使用Spring MVC
*
* @see org.compass.spring.web.mvc.CompassSearchController
* @see org.compass.spring.web.mvc.AbstractCompassCommandController
*/
public class CompassSearchService implements InitializingBean {
//每页显示的条目数量
private Integer pageSize = 15;
private Compass compass;
private CompassTemplate compassTemplate;
/**
* 公开的搜索接口,返回匹配的搜索结果,与
* {@link org.compass.spring.web.mvc.CompassSearchController#handle(javax.servlet.http.HttpServletRequest,
*javax.servlet.http.HttpServletResponse,Object,org.springframework.validation.BindException) 处理相似
*
* @see org.compass.spring.web.mvc.CompassSearchController#handle(javax.servlet.http.HttpServletRequest,
*javax.servlet.http.HttpServletResponse,java.lang.Object,org.springframework.validation.BindException)
*/
public CompassSearchResults search(final CompassSearchCommand command) {
return (CompassSearchResults) getCompassTemplate().execute(
CompassTransaction.TransactionIsolation.READ_ONLY_READ_COMMITTED, new CompassCallback() {
public Object doInCompass(CompassSession session) {
return performSearch(command, session);
}
});
}
/**
* 通过此方法调用搜索引擎,进行结果匹配搜索.
*
* @see org.compass.spring.web.mvc.CompassSearchController#performSearch(
*org.compass.spring.web.mvc.CompassSearchCommand,org.compass.core.CompassSession)
*/
protected CompassSearchResults performSearch(CompassSearchCommand searchCommand, CompassSession session) {
long time = System.currentTimeMillis();
CompassQuery query = buildQuery(searchCommand, session);
CompassHits hits = query.hits();
CompassDetachedHits detachedHits;
CompassSearchResults.Page[] pages = null;
if (pageSize == null) {
doProcessBeforeDetach(searchCommand, session, hits, -1, -1);
detachedHits = hits.detach();
} else {
int iPageSize = pageSize;
int page = 0;
int hitsLength = hits.getLength();
if (searchCommand.getPage() != null) {
page = searchCommand.getPage();
}
int from = page * iPageSize;
if (from > hits.getLength()) {
// 如果起始的条目大于搜索到的条目
from = hits.getLength() - iPageSize;
doProcessBeforeDetach(searchCommand, session, hits, from, hitsLength);
detachedHits = hits.detach(from, hitsLength);
} else if ((from + iPageSize) > hitsLength) {
// 结束的条目大于搜索到的结果
doProcessBeforeDetach(searchCommand, session, hits, from, hitsLength);
detachedHits = hits.detach(from, hitsLength);
} else {
// 中间的页码,直接取出相应的条目
doProcessBeforeDetach(searchCommand, session, hits, from, iPageSize);
detachedHits = hits.detach(from, iPageSize);
}
doProcessAfterDetach(searchCommand, session, detachedHits);
int numberOfPages = (int) Math.ceil((float) hitsLength / iPageSize);
pages = new CompassSearchResults.Page[numberOfPages];
for (int i = 0; i < pages.length; i++) {
pages[i] = new CompassSearchResults.Page();
pages[i].setFrom(i * iPageSize + 1);
pages[i].setSize(iPageSize);
pages[i].setTo((i + 1) * iPageSize);
if (from >= (pages[i].getFrom() - 1) && from < pages[i].getTo()) {
pages[i].setSelected(true);
} else {
pages[i].setSelected(false);
}
}
if (numberOfPages > 0) {
CompassSearchResults.Page lastPage = pages[numberOfPages - 1];
if (lastPage.getTo() > hitsLength) {
lastPage.setSize(hitsLength - lastPage.getFrom());
lastPage.setTo(hitsLength);
}
}
}
time = System.currentTimeMillis() - time;
CompassSearchResults searchResults = new CompassSearchResults(detachedHits.getHits(), time, pageSize);
searchResults.setPages(pages);
return searchResults;
}
/**
* 构建Lucene搜索器.
*/
protected CompassQuery buildQuery(CompassSearchCommand searchCommand, CompassSession session) {
CompassQuery query = session.queryBuilder().queryString(searchCommand.getQuery().trim()).toQuery();
if (AdvancedSearchCommand.class.isAssignableFrom(searchCommand.getClass())) {
AdvancedSearchCommand advancedSearchCommand = (AdvancedSearchCommand) searchCommand;
for (CompassSort sort : advancedSearchCommand.getSortMap()) {
query.addSort(sort.getName(), sort.getType(), sort.getDirection());
}
}
return query;
}
/**
* 在detach 之前,可以做一些操作。比如highlighting...
*
* @param from 需要注意的是,如果pageSize 没有指定,那么这里传入的参数为-1
*/
protected void doProcessBeforeDetach(CompassSearchCommand searchCommand, CompassSession session, CompassHits hits,
int from, int size) {
if (AdvancedSearchCommand.class.isAssignableFrom(searchCommand.getClass())) {
if (from < 0) {
from = 0;
size = hits.getLength();
}
String[] highlightFields = ((AdvancedSearchCommand) searchCommand).getHighlightFields();
if (highlightFields == null) {
return;
}
// highlight fields
for (int i = from; i < size; i++) {
for (String highlightField : highlightFields) {
hits.highlighter(i).fragment(highlightField);
}
}
}
}
/**
* An option to perform any type of processing before the hits are detached.
*/
protected void doProcessAfterDetach(CompassSearchCommand searchCommand, CompassSession session,
CompassDetachedHits hits) {
}
public void afterPropertiesSet() throws Exception {
Assert.notNull(compass, "Must set compass property");
this.compassTemplate = new CompassTemplate(compass);
}
public Integer getPageSize() {
return pageSize;
}
public void setPageSize(Integer pageSize) {
this.pageSize = pageSize;
}
public void setCompass(Compass compass) {
this.compass = compass;
}
protected CompassTemplate getCompassTemplate() {
return this.compassTemplate;
}
}
8、Model层
@SearchableId 声明Document的id列;
@SearchableProperty 声明要索引的field;
@SearchableComponent 声明要索引的其他关联对象。
Article.java
package com.mobilesoft.esales.model;
import java.util.Date;
import org.compass.annotations.Searchable;
import org.compass.annotations.SearchableId;
import org.compass.annotations.SearchableProperty;
import org.compass.core.CompassTemplate;
@Searchable
public class Article implements java.io.Serializable {
@SearchableId
private Integer id;
@SearchableProperty(name="title")
private String title;
@SearchableProperty(name="author")
private Integer author;
@SearchableProperty(name="publishDate")
private Date publishDate;
/** default constructor */
public Article() {
}
/** minimal constructor */
public Article(String title, Date publishDate) {
this.title = title;
this.publishDate = publishDate;
}
/** full constructor */
public Article(String title, Integer author, Date publishDate) {
this.title = title;
this.author = author;
this.publishDate = publishDate;
}
public Integer getId() {
return this.id;
}
public void setId(Integer id) {
this.id = id;
}
public String getTitle() {
return this.title;
}
public void setTitle(String title) {
this.title = title;
}
public Integer getAuthor() {
return this.author;
}
public void setAuthor(Integer author) {
this.author = author;
}
public Date getPublishDate() {
return this.publishDate;
}
public void setPublishDate(Date publishDate) {
this.publishDate = publishDate;
}
}
Author.java
package com.mobilesoft.esales.model;
import org.compass.annotations.Searchable;
import org.compass.annotations.SearchableId;
import org.compass.annotations.SearchableProperty;
import org.compass.core.CompassTemplate;
@Searchable
public class Author implements java.io.Serializable {
@SearchableId
private Integer id;
@SearchableProperty(name="username")
private String username;
private String password;
@SearchableProperty(name="age")
private Short age;
public Author() {
}
/** minimal constructor */
public Author(String username, String password) {
this.username = username;
this.password = password;
}
/** full constructor */
public Author(String username, String password, Short age) {
this.username = username;
this.password = password;
this.age = age;
}
// Property accessors
public Integer getId() {
return this.id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return this.username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return this.password;
}
public void setPassword(String password) {
this.password = password;
}
public Short getAge() {
return this.age;
}
public void setAge(Short age) {
this.age = age;
}
}
9、DAO层
ArticleDAO.java和AuthorDAO.java省略
直接用MyEclipse生成的,没有什么特别的。
10、参考文档
http://www.compass-project.org/docs/1.2.1/reference/html/
The Compass Framework Search made easy.pdf
InfoQ: Compass: Integrate Search into your apps
InfoQ: Compass: Simplifying and Extending Lucene to Provide Google-like Search
Compass 指南
在新架构中打算选择Compass或Hibernate Search作为搜索引擎框架,比较后,感觉Hibernate Search上还是没有Compass成熟,另外考虑到后期对网页的爬取及搜索需求,决定还是基于Compass来作为架构缺省的搜索引擎。网上关于Compass的文档很多,但说得相对完整其详细的入门文档基本上没有,Compass的官方文档倒是说得很详细,但是例子一塌糊涂,存在很大问题。记录一下搭建的过程,作为入门的指南。
Compass 通过OSEM(Object/Search Engine Mapping)允许把应用对象的领域模型映射到搜索引擎,最终通过访问common meta data来达到访问对象的目的。
1、几个核心概念
1.1、annotation vs. xml配置文件
Compass的配置文件主要分成三类:
第一类:*.cmd.xml文件*
.cmd.xml文件是对common meta data进行定义,定义了最终搜索的结果中的最基本的元数据。
第二类:*.cpm.xml文件
*.cpm.xml是Object/Search Engine Mapping,提供了POJO到common meta data的映射。
第三类:*.cfg.xml文件
Compass的*.cfg.xml定义了Compass的Index存放路径、搜索引擎分词等相关信息。
与采用xml配置文件相比较,采用Annonation方式还是相对简单,尤其是采用Spring时候,不用写*.cmd.xml文件、*.cpm.xml、*.cfg.xml,相对很方便,而且不像Hibernate的Annonation很多,Compass的Annonation的核心标注只有@Searchable、@SearchableId、@SearchableProperty、@SearchableComponent个,很容易记忆。因此推荐使用Annonation方式
1.2、Compass核心API
Compass的核心API借鉴了Hibernate的术语,因此在操作上基本上与Hibernate类似,以下为Compass的几个核心接口:
CompassConfiguration(类似Hibernate Configuration):用来在一些设置参数、配置文件和映射定义上配置Compass。通常用来创建Compass接口。
Compass(类似Hibernate SessionFactory):为单线程使用,创建线程安全的实例来打开Compass Seesion。同样还提供了一些搜索引擎索引级别的操作。
CompassSesssion(类似Hibernate Session):用来执行像保存、删除、查找、装载这样的搜索操作。很轻量但是并不是线程安全的。
CompassTransaction(类似Hibernate Transaction):管理Compass事务的接口。使用它并不需要事务管理环境(像Spring、JTA)。
1.3、Compass与Spring集成
Compass已经对对spring集成做了很好的封装,同时与Spring对Hibernate的支持类似,Compass也提供了CompassTemplate来简化诸如对Session、Transaction、Exception等操作,尽量充分使用此工具,可以有效提高效率。例如:
CompassTemplate ct = (CompassTemplate) context.getBean("compassTemplate");
Article article = new Article();
article.setTitle("Compass Test");
article.setPublishDate(new Date());
article.setAuthor(1);
ct.save(article); //存储对象需要索引的数据到Compass的索引中。
2、软件环境
Spring :2.5
Compas:1.2.1
Hibernate:3.2.5
Mysql :5.0.5
3、数据库脚本
CREATE TABLE `article` ( `Id` int(11) NOT NULL auto_increment, `title` varchar(40) NOT NULL default '', `author` int(11) default '0', `publish_date` date NOT NULL default '0000-00-00', PRIMARY KEY (`Id`) ) TYPE=MyISAM; CREATE TABLE `author` ( `Id` int(11) NOT NULL auto_increment, `username` varchar(20) NOT NULL default '', `password` varchar(20) NOT NULL default '', `age` smallint(6) default '0', PRIMARY KEY (`Id`) ) TYPE=MyISAM;
4、测试用例
从测试用例讲起比较容易把关系理清楚,不然一堆术语和概念很让人晕乎。
import org.apache.log4j.Logger; import java.util.Date; import junit.framework.TestCase; import org.compass.core.Compass; import org.compass.core.CompassDetachedHits; import org.compass.core.CompassHit; import org.compass.core.CompassHits; import org.compass.core.CompassSession; import org.compass.core.CompassTemplate; import org.compass.core.CompassTransaction; import org.compass.core.support.search.CompassSearchCommand; import org.compass.core.support.search.CompassSearchResults; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.mobilesoft.esales.dao.hibernate.ArticleDAO; import com.mobilesoft.esales.dao.hibernate.AuthorDAO; import com.mobilesoft.esales.model.Article; import com.mobilesoft.esales.model.Author; import com.mobilesoft.framework.search.service.CompassSearchService; /** * Compass服务使用的测试用例 * * @author liangchuan@mobile-soft.cn * */ public class TestCompass extends TestCase { private static final Logger logger = Logger.getLogger(TestCompass.class); private static ClassPathXmlApplicationContext context = null; private static CompassTemplate ct; static { context = new ClassPathXmlApplicationContext(new String[] { "applicationContext.xml", "applicationContext-resources.xml", "applicationContext-dao.xml", "applicationContext-service.xml", "applicationContext-compass.xml" }); ct = (CompassTemplate) context.getBean("compassTemplate"); } protected void setUp() throws Exception { } /** * 插入测试数据 */ public void testInsert() { ArticleDAO articleDao = (ArticleDAO) context.getBean("articleDAO"); AuthorDAO authorDao = (AuthorDAO) context.getBean("authorDAO"); Article article = new Article(); Author author = new Author(); author.setAge((short) 27); author.setUsername("liangchuan"); author.setPassword("liangchuan"); article.setTitle("Compass Test"); article.setPublishDate(new Date()); article.setAuthor(1); authorDao.save(author); articleDao.save(article); ct.save(article); ct.save(author); } /** * 用于测试使用CompassTransaction事务方式 */ public void testTransactionalFind() { Compass compass = ct.getCompass(); CompassSession session = compass.openSession(); CompassTransaction tx = null; try { tx = session.beginTransaction(); CompassHits hits = session.find("Compass*"); logger.error("testTransactionalFind() - CompassHits hits=" + hits.getLength()); for (int i = 0; i < hits.getLength(); i++) { Object hit = hits.data(i); if (hit instanceof Article) { Article item = (Article) hit; logger.error("testTransactionalFind() - article hits=" + item.getTitle()); } else if (hit instanceof Author) { Author item = (Author) hit; logger.error("testTransactionalFind() - author hits=" + item.getUsername()); } else { logger.error("testTransactionalFind() - error hits="); } } tx.commit(); } catch (Exception e) { if (tx != null) { tx.rollback(); } } finally { session.close(); } } /** * 用于演示CompassDetachedHits的使用。 * 由于CompassTempalte得到的结果集必须在transactionalcontext中才能使用, * 因此必须使用CompassDetachedHits方式测试CompassDetachedHits方式 */ public void testDetachedFind() { // 由于CompassTempalte得到的结果集必须在transactional // context中才能使用,因此必须使用CompassDetachedHits方式 // 测试CompassDetachedHits方式 CompassDetachedHits hits = ct.findWithDetach("Compass*"); logger.error("testDetachedFind() - CompassHits hits=" + hits.getLength()); for (int i = 0; i < hits.getLength(); i++) { Object hit = hits.data(i); if (hit instanceof Article) { Article item = (Article) hit; logger.error("testDetachedFind() - article hits=" + item.getTitle()); } else if (hit instanceof Author) { Author item = (Author) hit; logger.error("testDetachedFind() - author hits=" + item.getUsername()); } else { logger.error("testDetachedFind() - error hits="); } } } /** * 用于演示com.mobilesoft.framework.search.service.CompassSearchService的使用 * */ class CompassSearch extends CompassSearchService{ CompassSearch(){ Compass compass = ct.getCompass(); CompassSession session = compass.openSession(); CompassTransaction tx = null; try { tx = session.beginTransaction(); CompassSearchCommand command = new CompassSearchCommand(); command.setQuery("Compass"); CompassSearchResults results= performSearch(command,session); logger.error("CompassSearch() - CompassHit TotalHits value=" +results.getTotalHits()); for (int i = 0; i < results.getHits().length; i++) { CompassHit hits=results.getHits()[i]; Object hit=hits.getData(); logger.error("CompassSearch() - CompassHit hit=" + hit); //$NON-NLS-1$ if (hit instanceof Article) { Article item = (Article) hit; logger.error("testCompassSearchService() - article hits=" + item.getTitle()); } else if (hit instanceof Author) { Author item = (Author) hit; logger.error("testCompassSearchService() - author hits=" + item.getUsername()); } else { logger.error("testCompassSearchService() - error hits="); } tx.commit(); } } catch (Exception e) { if (tx != null) { tx.rollback(); } } finally { session.close(); } } } public void testCompassSearchService() { new CompassSearch(); } protected void tearDown() throws Exception { } }
5、配置文件
applicationContext-compass.xml
<?xml version="1.0"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd"> <beans default-lazy-init="true"> <bean id="compassTemplate" class="org.compass.core.CompassTemplate"> <property name="compass" ref="compass"/> </bean> <bean id="annotationConfiguration" class="org.compass.annotations.config.CompassAnnotationsConfiguration"> </bean> <bean id="compass" class="org.compass.spring.LocalCompassBean"> <property name="classMappings"> <list> <value>com.mobilesoft.esales.model.Article</value> <value>com.mobilesoft.esales.model.Author</value> </list> </property> <property name="compassConfiguration" ref="annotationConfiguration"/> <property name="compassSettings"> <props> <prop key="compass.engine.connection"> file://compass </prop> <prop key="compass.transaction.factory"> org.compass.spring.transaction.SpringSyncTransactionFactory </prop> <prop key="compass.engine.highlighter.default.formatter.simple.pre"> <![CDATA[<font color="red"><b>]]> </prop> <prop key="compass.engine.highlighter.default.formatter.simple.post"> <![CDATA[</b></font>]]> </prop> </props> </property> <property name="transactionManager" ref="transactionManager"/> </bean> <bean id="hibernateGpsDevice" class="org.compass.gps.device.hibernate.HibernateGpsDevice"> <property name="name"> <value>hibernateDevice</value> </property> <property name="sessionFactory" ref="sessionFactory"/> <property name="mirrorDataChanges"> <value>true</value> </property> </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> <bean class="org.compass.spring.device.SpringSyncTransactionGpsDeviceWrapper"> <property name="gpsDevice" ref="hibernateGpsDevice"/> </bean> </list> </property> </bean> <bean id="compassSearchService" class="com.mobilesoft.framework.search.service.CompassSearchService"> <property name="compass" ref="compass"/> <property name="pageSize" value="15"/> </bean> <!-- 定时重建索引(利用quartz)或随Spring ApplicationContext启动而重建索引 --> <bean id="compassIndexBuilder" class="com.mobilesoft.framework.search.service.CompassIndexBuilder" lazy-init="false"> <property name="compassGps" ref="compassGps"/> <property name="buildIndex" value="false"/> <property name="lazyTime" value="10"/> </bean> </beans>
applicationContext-dao.xml、applicationContext-service.xml、applicationContext-resources.xml等略去。
6、Service层(参考了SpringSide实现)
AdvancedSearchCommand.java
package com.mobilesoft.framework.search.service; import java.util.HashSet; import java.util.Set; import org.apache.commons.lang.StringUtils; import org.compass.core.CompassQuery.SortDirection; import org.compass.core.CompassQuery.SortPropertyType; import org.compass.core.support.search.CompassSearchCommand; import org.springframework.util.Assert; public class AdvancedSearchCommand extends CompassSearchCommand { /** * 封装基于Compass 的排序参数. */ class CompassSort { private String name; private SortPropertyType type; private SortDirection direction; public CompassSort() { } public CompassSort(String sortParamName, String paramType, boolean isAscend) { Assert.isTrue(StringUtils.isNotBlank(sortParamName)); setName(sortParamName); if ("int".equalsIgnoreCase(paramType)) { setType(SortPropertyType.INT); } else if ("float".equalsIgnoreCase(paramType)) { setType(SortPropertyType.FLOAT); } else if ("string".equalsIgnoreCase(paramType)) { setType(SortPropertyType.STRING); } else { setType(SortPropertyType.AUTO); } if (isAscend) { setDirection(SortDirection.AUTO); } else { setDirection(SortDirection.REVERSE); } } public String getName() { return name; } public void setName(String name) { this.name = name; } public SortPropertyType getType() { return type; } public void setType(SortPropertyType type) { this.type = type; } public SortDirection getDirection() { return direction; } public void setDirection(SortDirection direction) { this.direction = direction; } } /** * 搜索结果排序表. */ private Set<CompassSort> sortMap = new HashSet<CompassSort>(); private String[] highlightFields; /** * @param paramType 现定义了三种类型: int string 以及 float。<br> * 除去这三种外,其他会被自动定义为SortPropertyType.AUTO 具体的可见{@link org.compass.core.CompassQuery.SortPropertyType} * @param isAscend 顺序还是倒序排序 * @see org.compass.core.CompassQuery.SortPropertyType#AUTO * @see org.compass.core.CompassQuery.SortPropertyType#INT * @see org.compass.core.CompassQuery.SortPropertyType#STRING * @see org.compass.core.CompassQuery.SortPropertyType#FLOAT * @see org.compass.core.CompassQuery.SortDirection#AUTO * @see org.compass.core.CompassQuery.SortDirection#REVERSE */ public void addSort(String sortParamName, String paramType, boolean isAscend) { this.sortMap.add(new CompassSort(sortParamName, paramType, isAscend)); } public Set<CompassSort> getSortMap() { return sortMap; } public void setSortMap(Set<CompassSort> sortMap) { this.sortMap = sortMap; } public String[] getHighlightFields() { return highlightFields; } public void setHighlightFields(String[] highlightFields) { this.highlightFields = highlightFields; } }
CompassIndexBuilder.java
package com.mobilesoft.framework.search.service; import org.apache.log4j.Logger; import org.compass.gps.CompassGps; import org.springframework.beans.factory.InitializingBean; import org.springframework.util.Assert; /** * 通过quartz定时调度定时重建索引或自动随Spring ApplicationContext启动而重建索引的Builder. * 会启动后延时数秒新开线程调用compassGps.index()函数. * 默认会在Web应用每次启动时重建索引,可以设置buildIndex属性为false来禁止此功能. * 也可以不用本Builder, 编写手动调用compassGps.index()的代码. * */ public class CompassIndexBuilder implements InitializingBean { private static final Logger log = Logger.getLogger(CompassIndexBuilder.class); // 是否需要建立索引,可被设置为false使本Builder失效. private boolean buildIndex = false; // 索引操作线程延时启动的时间,单位为秒 private int lazyTime = 10; // Compass封装 private CompassGps compassGps; // 索引线程 private Thread indexThread = new Thread() { @Override public void run() { try { Thread.sleep(lazyTime * 1000); log.info("begin compass index..."); long beginTime = System.currentTimeMillis(); // 重建索引. // 如果compass实体中定义的索引文件已存在,索引过程中会建立临时索引, // 索引完成后再进行覆盖. compassGps.index(); long costTime = System.currentTimeMillis() - beginTime; log.info("compss index finished."); log.info("costed " + costTime + " milliseconds"); } catch (InterruptedException e) { // simply proceed } } }; /** * 实现<code>InitializingBean</code>接口,在完成注入后调用启动索引线程. * * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet() */ public void afterPropertiesSet() throws Exception { if (buildIndex) { Assert.notNull(compassGps, "CompassIndexBuilder not set CompassGps yet."); indexThread.setDaemon(true); indexThread.setName("Compass Indexer"); indexThread.start(); } } public void setBuildIndex(boolean buildIndex) { this.buildIndex = buildIndex; } public void setLazyTime(int lazyTime) { this.lazyTime = lazyTime; } public void setCompassGps(CompassGps compassGps) { this.compassGps = compassGps; } }
CompassSearchService.java
package com.mobilesoft.framework.search.service; import org.compass.core.Compass; import org.compass.core.CompassCallback; import org.compass.core.CompassDetachedHits; import org.compass.core.CompassHits; import org.compass.core.CompassQuery; import org.compass.core.CompassSession; import org.compass.core.CompassTemplate; import org.compass.core.CompassTransaction; import org.compass.core.support.search.CompassSearchCommand; import org.compass.core.support.search.CompassSearchResults; import org.springframework.beans.factory.InitializingBean; import org.springframework.util.Assert; import com.mobilesoft.framework.search.service.AdvancedSearchCommand.CompassSort; /** * 仿照 {@link org.compass.spring.web.mvc.CompassSearchController} * 中的代码,构建了一个Service,方便不使用Spring MVC * * @see org.compass.spring.web.mvc.CompassSearchController * @see org.compass.spring.web.mvc.AbstractCompassCommandController */ public class CompassSearchService implements InitializingBean { //每页显示的条目数量 private Integer pageSize = 15; private Compass compass; private CompassTemplate compassTemplate; /** * 公开的搜索接口,返回匹配的搜索结果,与 * {@link org.compass.spring.web.mvc.CompassSearchController#handle(javax.servlet.http.HttpServletRequest, *javax.servlet.http.HttpServletResponse,Object,org.springframework.validation.BindException) 处理相似 * * @see org.compass.spring.web.mvc.CompassSearchController#handle(javax.servlet.http.HttpServletRequest, *javax.servlet.http.HttpServletResponse,java.lang.Object,org.springframework.validation.BindException) */ public CompassSearchResults search(final CompassSearchCommand command) { return (CompassSearchResults) getCompassTemplate().execute( CompassTransaction.TransactionIsolation.READ_ONLY_READ_COMMITTED, new CompassCallback() { public Object doInCompass(CompassSession session) { return performSearch(command, session); } }); } /** * 通过此方法调用搜索引擎,进行结果匹配搜索. * * @see org.compass.spring.web.mvc.CompassSearchController#performSearch( *org.compass.spring.web.mvc.CompassSearchCommand,org.compass.core.CompassSession) */ protected CompassSearchResults performSearch(CompassSearchCommand searchCommand, CompassSession session) { long time = System.currentTimeMillis(); CompassQuery query = buildQuery(searchCommand, session); CompassHits hits = query.hits(); CompassDetachedHits detachedHits; CompassSearchResults.Page[] pages = null; if (pageSize == null) { doProcessBeforeDetach(searchCommand, session, hits, -1, -1); detachedHits = hits.detach(); } else { int iPageSize = pageSize; int page = 0; int hitsLength = hits.getLength(); if (searchCommand.getPage() != null) { page = searchCommand.getPage(); } int from = page * iPageSize; if (from > hits.getLength()) { // 如果起始的条目大于搜索到的条目 from = hits.getLength() - iPageSize; doProcessBeforeDetach(searchCommand, session, hits, from, hitsLength); detachedHits = hits.detach(from, hitsLength); } else if ((from + iPageSize) > hitsLength) { // 结束的条目大于搜索到的结果 doProcessBeforeDetach(searchCommand, session, hits, from, hitsLength); detachedHits = hits.detach(from, hitsLength); } else { // 中间的页码,直接取出相应的条目 doProcessBeforeDetach(searchCommand, session, hits, from, iPageSize); detachedHits = hits.detach(from, iPageSize); } doProcessAfterDetach(searchCommand, session, detachedHits); int numberOfPages = (int) Math.ceil((float) hitsLength / iPageSize); pages = new CompassSearchResults.Page[numberOfPages]; for (int i = 0; i < pages.length; i++) { pages[i] = new CompassSearchResults.Page(); pages[i].setFrom(i * iPageSize + 1); pages[i].setSize(iPageSize); pages[i].setTo((i + 1) * iPageSize); if (from >= (pages[i].getFrom() - 1) && from < pages[i].getTo()) { pages[i].setSelected(true); } else { pages[i].setSelected(false); } } if (numberOfPages > 0) { CompassSearchResults.Page lastPage = pages[numberOfPages - 1]; if (lastPage.getTo() > hitsLength) { lastPage.setSize(hitsLength - lastPage.getFrom()); lastPage.setTo(hitsLength); } } } time = System.currentTimeMillis() - time; CompassSearchResults searchResults = new CompassSearchResults(detachedHits.getHits(), time, pageSize); searchResults.setPages(pages); return searchResults; } /** * 构建Lucene搜索器. */ protected CompassQuery buildQuery(CompassSearchCommand searchCommand, CompassSession session) { CompassQuery query = session.queryBuilder().queryString(searchCommand.getQuery().trim()).toQuery(); if (AdvancedSearchCommand.class.isAssignableFrom(searchCommand.getClass())) { AdvancedSearchCommand advancedSearchCommand = (AdvancedSearchCommand) searchCommand; for (CompassSort sort : advancedSearchCommand.getSortMap()) { query.addSort(sort.getName(), sort.getType(), sort.getDirection()); } } return query; } /** * 在detach 之前,可以做一些操作。比如highlighting... * * @param from 需要注意的是,如果pageSize 没有指定,那么这里传入的参数为-1 */ protected void doProcessBeforeDetach(CompassSearchCommand searchCommand, CompassSession session, CompassHits hits, int from, int size) { if (AdvancedSearchCommand.class.isAssignableFrom(searchCommand.getClass())) { if (from < 0) { from = 0; size = hits.getLength(); } String[] highlightFields = ((AdvancedSearchCommand) searchCommand).getHighlightFields(); if (highlightFields == null) { return; } // highlight fields for (int i = from; i < size; i++) { for (String highlightField : highlightFields) { hits.highlighter(i).fragment(highlightField); } } } } /** * An option to perform any type of processing before the hits are detached. */ protected void doProcessAfterDetach(CompassSearchCommand searchCommand, CompassSession session, CompassDetachedHits hits) { } public void afterPropertiesSet() throws Exception { Assert.notNull(compass, "Must set compass property"); this.compassTemplate = new CompassTemplate(compass); } public Integer getPageSize() { return pageSize; } public void setPageSize(Integer pageSize) { this.pageSize = pageSize; } public void setCompass(Compass compass) { this.compass = compass; } protected CompassTemplate getCompassTemplate() { return this.compassTemplate; } }
8、Model层
@SearchableId 声明Document的id列;
@SearchableProperty 声明要索引的field;
@SearchableComponent 声明要索引的其他关联对象。
Article.java
package com.mobilesoft.esales.model; import java.util.Date; import org.compass.annotations.Searchable; import org.compass.annotations.SearchableId; import org.compass.annotations.SearchableProperty; import org.compass.core.CompassTemplate; @Searchable public class Article implements java.io.Serializable { @SearchableId private Integer id; @SearchableProperty(name="title") private String title; @SearchableProperty(name="author") private Integer author; @SearchableProperty(name="publishDate") private Date publishDate; /** default constructor */ public Article() { } /** minimal constructor */ public Article(String title, Date publishDate) { this.title = title; this.publishDate = publishDate; } /** full constructor */ public Article(String title, Integer author, Date publishDate) { this.title = title; this.author = author; this.publishDate = publishDate; } public Integer getId() { return this.id; } public void setId(Integer id) { this.id = id; } public String getTitle() { return this.title; } public void setTitle(String title) { this.title = title; } public Integer getAuthor() { return this.author; } public void setAuthor(Integer author) { this.author = author; } public Date getPublishDate() { return this.publishDate; } public void setPublishDate(Date publishDate) { this.publishDate = publishDate; } }
Author.java
package com.mobilesoft.esales.model; import org.compass.annotations.Searchable; import org.compass.annotations.SearchableId; import org.compass.annotations.SearchableProperty; import org.compass.core.CompassTemplate; @Searchable public class Author implements java.io.Serializable { @SearchableId private Integer id; @SearchableProperty(name="username") private String username; private String password; @SearchableProperty(name="age") private Short age; public Author() { } /** minimal constructor */ public Author(String username, String password) { this.username = username; this.password = password; } /** full constructor */ public Author(String username, String password, Short age) { this.username = username; this.password = password; this.age = age; } // Property accessors public Integer getId() { return this.id; } public void setId(Integer id) { this.id = id; } public String getUsername() { return this.username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return this.password; } public void setPassword(String password) { this.password = password; } public Short getAge() { return this.age; } public void setAge(Short age) { this.age = age; } }
9、DAO层
ArticleDAO.java和AuthorDAO.java省略
直接用MyEclipse生成的,没有什么特别的。
10、参考文档
http://www.compass-project.org/docs/1.2.1/reference/html/
The Compass Framework Search made easy.pdf
InfoQ: Compass: Integrate Search into your apps
InfoQ: Compass: Simplifying and Extending Lucene to Provide Google-like Search
发表评论
-
安装和使用memcached
2014-04-16 16:24 641如何将 memcached 融入到 ... -
applicationContext.xml
2013-08-09 09:05 941<?xml version="1.0&quo ... -
注释驱动的 Spring cache 缓存介绍
2013-08-08 07:04 659概述 Spring 3.1 引入了激动人心的基于注释(an ... -
Spring2.5 Annotations
2013-08-08 06:33 854完成setXxxx功能,即配置文件的 <propert ... -
Spring基于注解的缓存配置--EHCache AND OSCache
2013-08-07 23:21 1026本文将构建一个普通工程来说明spring注解缓存的使用方式, ... -
Ehcache 整合Spring 使用页面、对象缓存
2013-08-07 22:51 893Ehcache 整合Spring 使用页面、对象缓存 ... -
javassist教程和示例
2013-05-18 08:57 2008Javassist是一个执行字节 ... -
ZooKeeper官方文档
2013-05-16 17:09 1559介绍(源自ZooKeeper官方文档) 学习HBase过程 ... -
ZooKeeper -例子
2013-05-16 17:08 1206ZooKeeper ZooKeepe ... -
Spring整合Hessian访问远程服务
2013-05-15 13:44 853Spring整合Hessian访问远程服务 目录 1.1 ... -
redis
2013-05-14 11:44 767redis是一个key-value存储系统。和Memcach ... -
spring 资源访问
2013-05-13 08:26 996spring在java基础上封装了资源访问,简单易用。 R ... -
ZooKeeper——入门
2013-05-08 16:12 909ZooKeeper——入门 博客分类: ZooK ... -
分布式服务框架 Zookeeper -- 管理分布式环境中的数据(IBM)
2013-05-08 14:07 784安装和配置详解 本文 ... -
分布式协调服务---Zookeeper
2013-05-08 14:05 7741、Zookeeper overview Zookee ... -
Hibernate
2013-03-28 13:04 923一、简述 Hibernate 和 JD ... -
Apache+Tomcat集群配置详解
2013-02-01 10:52 890Apache + Tomcat集群配置详解(1) 一、 ... -
Apache+Jboss集群基于反向代理的负载均衡
2013-02-01 10:40 2490假设三台机器IP分别为172.29.128.100、172. ... -
spring + ibatis 多数据源事务(分布式事务)管理配置方法
2012-12-17 15:18 1265spring + ibatis 多数据源事务(分布式事务 ... -
Hessian序列化不设SerializerFactory性能问题
2012-10-31 09:47 1492Hessian序列化不设SerializerFactor ...
相关推荐
Sass和 Compass设计师指南是中文扫描完整版
在Android平台上,开发一款指南针应用,如"指南针Compass",涉及到多个技术领域,包括传感器使用、UI设计、坐标转换以及实时数据更新等。本文将深入探讨这些关键知识点。 1. **Android传感器系统**:Android设备...
在Android平台上,指南针(Compass)是一种常见的应用,它能够指示出设备当前的地理方向。指南针基于地球磁场原理,通过内置的磁力计传感器来确定设备相对于地磁北的方位。 Arrow通常指的是指针,是指南针中的关键...
"compass(指南针)仪表图形控件"是一种专门用于创建直观且吸引人的交互式UI组件的工具,尤其适用于导航、定位或者监控系统中。这个控件提供了一个模拟真实世界指南针的视觉效果,使用户能够轻松理解并操作应用程序...
《Sass和Compass设计师指南》是一本专为前端开发者和设计师编写的深入解析Sass和Compass技术的专业书籍。Sass(Syntactically Awesome Style Sheets)是一种强大的CSS预处理器,它扩展了CSS语法,增加了变量、嵌套...
Sass和Compass设计师指南 AZW3文字版本,更清晰,非PDF版 Sass and Compass for Designers
《Sass和Compass设计师指南源代码》是一个深入解析Sass和Compass技术的资源集合,包含多个章节的源码示例,旨在帮助设计师更好地理解和应用这两种强大的CSS预处理器。Sass(Syntactically Awesome Style Sheets)是...
Compass Navigator Pro 2 v1.9Unity指南针导航仪 Pro 2 支持Unity版本2021.3.16或更高 终极UI导航系统,包括指南针栏、战争迷雾迷你地图、雷达模式、屏幕指示器、信标效果、全屏滚动地图等。 描述 Compass Navigator...
《3D立体指南针 3D Compass v2.1.apk》是一款专为Android设备设计的应用程序,旨在提供直观、精确的导航功能。这款指南针软件利用手机的内置传感器,如磁力计和陀螺仪,来确定设备的地理位置和方向,从而在3D环境中...
### Compass入门指南:深入理解与实战应用 #### 核心概念解析 **1.1 Annotation vs. XML配置文件** 在Compass中,开发者可以选择使用Annotation或XML配置文件来进行配置。这两种方式各有优劣: - **Annotation...
在Android平台上,开发一款名为"Compass_手机指南针_安卓指南针_"的应用,主要目的是为用户提供一个简单易用的指南针工具。该应用利用了手机内置的磁力传感器(也称为磁力计),来检测地球磁场并指示出方向。在深入...
标题:PhoneGap,API帮助文档翻译整理4-Compass(指南针) 描述:本文档将深入探讨PhoneGap中Compass API的功能与应用,包括如何获取设备的指南针方向、监听指南针变化以及如何处理相关事件。 ### 一、Compass API...
在本项目中,"compass(指南针示例).zip" 是一个包含 C++ 编写的指南针程序的压缩文件。这个程序可能旨在演示如何利用编程技术实现一个基本的计算机图形学应用,显示一个虚拟的指南针指示方向。让我们深入探讨一下...
**入门指南** 对于初学者来说,理解Compass的基本概念和用法是关键。首先,你需要配置Compass,指定数据源和索引位置。接着,定义对象-搜索映射文件(`.cmap.xml`),将你的Java类与Lucene字段对应起来。然后,你...
PT漫步者是一款复古风格的城市用车,Grand Voyager是豪华MPV,而Compass指南者则是一款SUV,展示了克莱斯勒在不同细分市场的产品布局。 6. **培训目标和方式**: 课程旨在帮助学员了解自身知识水平,明确学习需求...
七、初学者指南 对于初学者,可以从以下方面开始学习Compass: 1. 熟悉Sass基础语法,包括变量、嵌套规则、混合、函数等。 2. 学习Compass的核心概念,如网格系统、样式库和混入。 3. 实践创建简单项目,应用Compass...
compass-animation, 指南针的css3动画插件 这里插件主要用于将动画添加到较老版本的指南针中。 Compass版本的Compass在合并了这个代码,所以这个插件不需要。 我们不会再更新它了。如果你正在寻找 Animate.css的...
Compass Bar Pro是一个功能强大的UI组件,可让您通过所有有用的功能(例如标记(兴趣点)或可自定义的north)在游戏中显示精美的Compass。它完全适合您的冒险,RPG,FPS或生存/探索游戏。 它提供了两种类型的指南针...