Compass 是一个功能强大、支持事务的JAVA搜索引擎框架。Compass允许将对象域模型映射到搜索引擎,并能同步搜索引擎索引和数据源。Compass 在低级别的 Lucene API的基础上提供了更高级别的抽象。同时Compass实现了快速索引操作和优化,并使搜索引擎具有事务处理能力。
本文档介绍在SSH(struts+spring+hibernate)环境下配置和使用Compass的方法。
2 下载compass包
创建JAVA web工程,并导入struts、spring、hibernate所需的JAR文件及配置文件。
从官方网站http://www.compass-project.org下载Compass软件包:compass-2.2.0-with-dependencies.zip。解开压缩文件后,将compass-2.2.0.jar和lucene-core-2.4.1.jar添加到项目的Build Path中。
3 OSEM - Object/Search Engine Mapping
Compass 通过使用Java 5 注解或者简单的XML映射文件将Java对象映射到搜索引擎。
这种技术就是OSEM (Object Search Engine Mapping)。Compass 使用OSEM从对象中抽取需要的属性,并将需要的元数据插入到搜索引擎索引中。本文采用XML文件来描述对象和搜索引擎的关系。
1. 域模型的POJO
下面是一个Java POLO类。
public class Book implements java.io.Serializable { // Fields private long bookId; private String bookName; private String isbn; private String publisher; private String author; private long currentStock; private long safeStock; private Double price; private String memo; // 方法略 }
2. OSEM XML映射定义
Book.cpm.xml文件的内容:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE compass-core-mapping PUBLIC "-//Compass/Compass Core Mapping DTD 2.2//EN" "http://www.compass-project.org/dtd/compass-core-mapping-2.2.dtd"> <compass-core-mapping package="cn.ittrain.tcrm.domain"> <class name="Book" alias="${mybooks.book}"> <id name="bookId" /> <property name="bookName"> <meta-data>${mybooks.bookName}</meta-data> </property> <property name="isbn"> <meta-data>${mybooks.isbn}</meta-data> </property> <property name="publisher"> <meta-data>${mybooks.publisher}</meta-data> </property> <property name="author"> <meta-data>${mybooks.author}</meta-data> </property> <property name="currentStock"> <meta-data>${mybooks.currentStock}</meta-data> </property> <property name="safeStock"> <meta-data>${mybooks.safeStock}</meta-data> </property> <property name="price"> <meta-data>${mybooks.price}</meta-data> </property> <property name="memo"> <meta-data>${mybooks.memo}</meta-data> </property> </class> </compass-core-mapping>
其中的元数据名和别名在下面的元数据文件中定义,Compass会自动用元数据名和别名的值来替换${}。
3. 元数据(Meta data)定义
公共元数据文件定义在OSEM映射文件中使用的元数据名和别名。
每一个类的映射定义都在一个别名下注册。别名是类的OSEM定义和类本身之间的桥梁。可以使用别名来引用一个类的映射定义。
元数据把映射定义中属性的值按照名称保存到索引中。
Book.cmd.xml文件的内容:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE compass-core-meta-data PUBLIC "-//Compass/Compass Core Meta Data DTD 2.2//EN" "http://www.compass-project.org/dtd/compass-core-meta-data-2.2.dtd"> <compass-core-meta-data> <meta-data-group id="mybooks" displayName="Mybooks Meta Data"> <description>Book Meta Data</description> <uri>http://compass/mybooks</uri> <alias id="book" displayName="Book"> <description>Book alias</description> <uri>http://compass/mybooks/Book</uri> <name>book</name> </alias> <meta-data id="bookId" displayName="BookId"> <description>BookId alias</description> <uri>http://compass/mybooks/bookId</uri> <name>bookId</name> </meta-data> <meta-data id="bookName" displayName="BookName"> <description>BookName alias</description> <uri>http://compass/mybooks/bookName</uri> <name>bookName</name> </meta-data> <meta-data id="isbn" displayName="isbn"> <description>isbn alias</description> <uri>http://compass/mybooks/isbn</uri> <name>isbn</name> </meta-data> <meta-data id="publisher" displayName="Publisher"> <description>Publisher alias</description> <uri>http://compass/mybooks/publisher</uri> <name>publisher</name> </meta-data> <meta-data id="author" displayName="Author"> <description>Author alias</description> <uri>http://compass/mybooks/author</uri> <name>author</name> </meta-data> <meta-data id="currentStock" displayName="CurrentStock"> <description>CurrentStock alias</description> <uri>http://compass/mybooks/currentStock</uri> <name>currentStock</name> </meta-data> <meta-data id="safeStock" displayName="SafeStock"> <description>SafeStock alias</description> <uri>http://compass/mybooks/safeStock</uri> <name>safeStock</name> </meta-data> <meta-data id="price" displayName="Price"> <description>Price alias</description> <uri>http://compass/mybooks/price</uri> <name>price</name> </meta-data> <meta-data id="memo" displayName="Memo"> <description>Memo alias</description> <uri>http://compass/mybooks/memo</uri> <name>memo</name> </meta-data> </meta-data-group> </compass-core-meta-data>
4 Hibernate配置
对3.1节中Book配置Hibernate的映射文件:Book.hbm.xml。
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="cn.ittrain.tcrm.domain.Book" table="BOOK" schema="LIXIANZHU"> <id name="bookId" type="long"> <column name="BOOK_ID" precision="22" scale="0" /> <generator class="sequence" /> </id> <property name="bookName" type="java.lang.String"> <column name="BOOK_NAME" length="50" /> </property> <property name="isbn" type="java.lang.String"> <column name="ISBN" length="20" /> </property> <property name="publisher" type="java.lang.String"> <column name="PUBLISHER" length="50" /> </property> <property name="author" type="java.lang.String"> <column name="AUTHOR" length="20" /> </property> <property name="currentStock" type="long"> <column name="CURRENT_STOCK" precision="22" scale="0" /> </property> <property name="safeStock" type="long"> <column name="SAFE_STOCK" precision="22" scale="0" /> </property> <property name="price" type="java.lang.Double"> <column name="PRICE" precision="126" scale="0" /> </property> <property name="memo" type="java.lang.String"> <column name="MEMO" /> </property> </class> </hibernate-mapping>
5 Spring配置
1. 在配置Compass 相关的bean之前,必须先配置SessionFactory和TransactionManager:
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> <property name="configLocation" value="classpath:hibernate.cfg.xml"> </property> </bean> <bean id="txManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory"/> </bean>
2. 配置Compass。Compass用来实现搜索功能。
<bean id="compass" class="org.compass.spring.LocalCompassBean"> <property name="resourceLocations"> <list> <value>classpath:Book.cmd.xml</value> <value>classpath:Book.cpm.xml</value> </list> </property> <property name="compassSettings"> <props> <!-- 索引文件在服务器上的存储路径 --> <prop key="compass.engine.connection">target/index</prop> <prop key="compass.transaction.factory">org.compass.spring.transaction.SpringSyncTransactionFactory</prop> </props> </property> <property name="transactionManager" ref="txManager"/> </bean>
3. 配置CompassGps。CompassGps用来实现索引功能。
<!-- 与hibernate的绑定,用Hibernate 3 事件系统,支持Real Time Data Mirroring . 经Hiberante的数据改变会自动被反射到索引里面. --> <bean id="hibernateGpsDevice" class="org.compass.spring.device.hibernate.dep.SpringHibernate3GpsDevice"> <property name="name"> <value>hibernateDevice</value> </property> <property name="sessionFactory" ref="sessionFactory"/> </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> <ref local="hibernateGpsDevice"/> </list> </property> </bean>
4. 配置CompassSearchCommand和CompassSearchHelper。CompassSearchCommand存储搜索命令,CompassSearchHelper执行搜索查询。
<bean id="compassSearchCommand" class="org.compass.core.support.search.CompassSearchCommand"/> <bean id="compassSearchHelper" class="org.compass.core.support.search.CompassSearchHelper"> <constructor-arg value="2" /> <!-- 每页的条数 --> <constructor-arg ref="compass" /> </bean>
6 配置Action
SearchAction用来建立索引和搜索。indexControl方法建立索引,而searchControl方法执行搜索。SearchAction以及compassGps、compassSearchCommand、compassSearchHelper由Spring依赖注入,其在Spring配置文件中的声明不在此赘述。
public class SearchAction extends DispatchAction { private static final Log log = LogFactory.getLog(SearchAction.class); private CompassGps compassGps; private CompassSearchCommand compassSearchCommand; private CompassSearchHelper compassSearchHelper; // Setter 方法略 public ActionForward indexControl(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) { compassGps.start(); compassGps.index(); return null; } public ActionForward searchControl(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) { String query = request.getParameter("query"); String page = request.getParameter("page"); Integer pageFrom = (page == null || page.length() == 0) ? 0:Integer.valueOf(page); compassSearchCommand.setQuery(query); compassSearchCommand.setPage(pageFrom); CompassSearchResults searchResults = compassSearchHelper.search(compassSearchCommand); String commandQuery = compassSearchCommand.getQuery(); request.setAttribute("status", commandQuery); request.setAttribute("searchResults", searchResults); return mapping.findForward("searchResults"); } }
7 搜索页面
<body> <FORM method="GET" action="search.do"> <INPUT type="text" size="20" name="query" value="<c:out value="${status}"/>" /> <input type="hidden" name="method" value="searchControl"/> <INPUT type = "submit" value="Search"/> </FORM> <c:if test="${! empty searchResults}"> 搜索花费 <c:out value="${searchResults.searchTime}" />毫秒 搜索关键字:<c:out value="${status}"/> <c:forEach var="hit" items="${searchResults.hits}"> <c:choose> <c:when test="${hit.alias == 'book'}"> <P> BookId:<a href="<c:url value="/editPet.htm?petId=${hit.data.bookId}"/>"> ${hit.data.bookId} </a> BookName:<c:out value="${hit.data.bookName}" /> Publisher:<c:out value="${hit.data.publisher}" /> ISBN:<c:out value="${hit.data.isbn}" /> <br/> </c:when> </c:choose> </c:forEach> <c:if test="${! empty searchResults.pages}"> <BR><BR><BR> <table><tr> <c:forEach var="page" items="${searchResults.pages}" varStatus="pagesStatus"> <td> <c:choose> <c:when test="${page.selected}"> <c:out value="${page.from}" />-<c:out value="${page.to}" /> </c:when> <c:otherwise> <FORM method="GET" action="search.do"> <INPUT type="hidden" name="query" value="<c:out value="${status}"/>" /> <INPUT type="hidden" name="page" value="<c:out value="${pagesStatus.index}"/>" /> <input type="hidden" name="method" value="searchControl"/> <INPUT type = "submit" value="<c:out value="${page.from}" />-<c:out value="${page.to}" />"/> </FORM> </c:otherwise> </c:choose> </td> </c:forEach> </tr></table> </c:if> </c:if> </body>