在新架構中打算選擇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、測試用例
從測試用例講起比較容易把關系理清楚,不然一堆術語和概念很讓人暈乎。
5、配置文件
applicationContext-compass.xml
applicationContext-dao.xml、applicationContext-service.xml、applicationContext-resources.xml等略去。
6、Service層(參考了SpringSide實現)
8、Model層
@SearchableId 聲明Document的id列;
@SearchableProperty 聲明要索引的field;
@SearchableComponent 聲明要索引的其他關聯對象。
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
Compass TSSJS Europe 06.pdf
Hello World Tutorial
InfoQ: Compass: Integrate Search into your apps
InfoQ: Compass: Simplifying and Extending Lucene to Provide Google-like Search
InfoQ: Compass: 在你的應用中集成搜索功能
Compass 指南
http://www.kimchy.org/
本文摘自:http://chuanliang2007.spaces.live.com/blog/cns!E5B7AB2851A4C9D2!389.entry
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
Compass TSSJS Europe 06.pdf
Hello World Tutorial
InfoQ: Compass: Integrate Search into your apps
InfoQ: Compass: Simplifying and Extending Lucene to Provide Google-like Search
InfoQ: Compass: 在你的應用中集成搜索功能
Compass 指南
http://www.kimchy.org/
本文摘自:http://chuanliang2007.spaces.live.com/blog/cns!E5B7AB2851A4C9D2!389.entry