天天看點

Compass讀文檔筆錄

Compass是基于Lucene 的更高層的抽象,假如你正打算做關于搜尋方面的子產品的話,那我建議你使用Compass,他提供了可配置方案,而且比Lucene更加容易使用。如果你的系統中使用Spring, Hibernate,JDO, IBatis。。。 Compass是最好的選擇,他能夠非常友善的內建到現有系統中去。

1. Compass的framework的系統結構。

感覺Compass的代碼的結構簡直就是剽竊Hibernate的,可能Compass的最初目的是用來整合Hibernate的,

CompassConfiguration conf = new CompassConfiguration().configure().addClass(Author.class);

Compass compass = conf.buildCompass();

CompassSession session = compass.openSession();

CompassTransaction tx = null;

try {

tx = session.beginTransaction();

session.save(author);

CompassHits hits = session.find("jack london");

Author a = (Author) hits.data(0);

Resource r = hits.getResource(0);

tx.commit();

} catch (CompassException ce) {

if (tx != null) tx.rollback();

} finally {

session.close();

}

假如你對Hibernate有了解的話,相信你對Compass會比較容易了解的,你可以把Hibernate的思想轉移到Compass上。現在讓我們看看他們之間的相似吧。

compass.cfg.xml

<compass-core-config xmlns="http://www.opensymphony.com/compass/schema/core-config"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://www.opensymphony.com/compass/schema/core-config

http://www.opensymphony.com/compass/schema/compass-core-config.xsd">

<compass name="default">

<connection><file path="target/test-index"/></connection>

<mappings><class name="test.Author" /></mappings>

</compass>

</compass-core-config>

這個是Compass總的配置檔案,其中定義了索引檔案存儲的位置(這裡是用檔案系統,Compass有多種選擇,你也可以選資料庫或其他),Compass索引的對象是面向PoJo的,這裡的是Author,對應的檔案是test/Author.cpm.xml.

當然這裡面的配置屬性不止這麼多,更多的屬性見Configure屬性。

CompassConfiguration conf = new CompassConfiguration()

.setSetting(CompassEnvironment.CONNECTION, "my/index/dir")

.addResource(DublinCore.cmd.xml).addClass(Author.class);

Compass compass = conf.buildCompass();

這裡我們CompassConfiguration會讀取預設的在classpath中的compass.cfg.xml初始化,然後得到Compass對象,可能你會馬上意思到這個Compass肯定對應于Hibenate中的SessionFactory,是的,這是一個重量級的對象,

我們需要通過這個對象得到CompassSession,然後進行CRUD操作,CompassSession跟Hibernate中的Session一樣是個lightweight對象。關于對Search domain的配置(Author.cpm.xml),大家可以檢視cpm檔案配置。在那裡面主要是定義了哪些properties是需要被索引的。

<?xml version="1.0"?>

<compass-core-mapping package="eg">

<class name="Author" alias="author">

<id name="id" />

<constant>

<meta-data>type</meta-data>

<meta-data-value>person</meta-data-value>

<meta-data-value>author</meta-data-value>

</constant>

<property name="name">

<meta-data>name</meta-data>

<meta-data>authorName</meta-data>

</property>

<property name="birthday">

<meta-data>birthday</meta-data>

</property>

<component name="books" ref-alias="book" />

<!-- can be a reference instead of component

<reference name="books" ref-alias="book" />

-->

</class>

<class name="Book" alias="book">

</class>

</compass-core-mapping>

2. 索引檔案結構

---[index dir]/index

|

|-- [subIndex1]

| |

| |--- segments

| |--- [segment1]

| |--- [segment2]

|

|-- [subIndex2]

| |

| |--- segments

| |--- [segment1]

| |--- [segment2]

| |--- [segment3]

|

...

基本上是一個search domain放到一個subIndex檔案夾中,更确切的說是相同alias name的search domain放到相同的sub index folder中。

3. Compass中的操作

通過CompassSession我們可以進行save,delete, get,load。假如我們有兩個domain Object,Author 和 Book,假如我們想要query Book的話要怎樣做呢? 我們需要使用alias(這個屬性定義在cmp檔案中),

通過CompassQueryBuilder去構造CompassQuery, CompassQueryBuilder非常靈活,非常像Hibernate的Criteria查詢。具體的sample請看 Working with objects

CompassHits hits = session.createQueryBuilder()

.queryString("+name:jack +familyName:london")

.setAnalyzer("an1") // use a different analyzer

.toQuery()

.addSort("familyName", CompassQuery.SortPropertyType.STRING)

.addSort("birthdate", CompassQuery.SortPropertyType.INT)

.hits();

4. CompassGps and CompassGpsDevice

CompassGps像是一個Service,他需要在application startup時啟動服務, application shutdown停止服務,CompassGpsDevice不能獨立的存在,他需要依賴CompassGps, CompassGps為CompassGpsDevice提供Compass對象,他們一起為程式提供Index的實時更新。 Compass整合Hibernate 等等 persitance framework的代碼就在CompassGpsDevice裡,你需要提供不同的Device,如HibernateDevice, JDODevice。你也

可以實作自己的Device, CompassGpsDevice會把domain object的更新事件通過CompassGps去通知Compass去更新索引檔案,這樣就是可以實時更新index了。有興趣的話可以看看Hibernate3GpsDevice的registerEventsForHibernate31()方法,他給Hibernate的save,delete,update操作增加listener。當然我們可以使用aop自己去實作這塊。CompassGps and CompassGpsDevice

Compass compass = // configure compass

CompassGps gps = new SingleCompassGps(compass);

CompassGpsDevice device1 = // configure the first device

device1.setName("device1");

gps.addDevice(device1);

CompassGpsDevice device2 = // configure the second device

device2.setName("device2");

gps.addDevice(device2);

gps.start();

.

.

//on application shutdown

gps.stop();

5. 整合Spring,Hibenate

在Compass的lib裡面就有非常好的一個sample了(petclinic),裡面有對Spring,Hibenate的整合,其實對spring來說也就是通過ioc把CompassGps 和 Compass定義好。CompassGps主要負責re-index和index實時更新

, Compass主要提供了自定義Search部分的入口(CompassTemplate)。Spring提供了對Compass的DAO的整合,在CompassDaoSupport 中拿到CompassTemplate,這個跟spring對hibernatedao的支援是一緻的。

public class LibraryCompassDao extends CompassDaoSupport {

public int getNumberOfHits(final String query) {

Integer numberOfHits = (Integer)getCompassTemplate().execute(

new CompassCallback() {

public Object doInCompass(CompassSession session) {

CompassHits hits = session.find(query);

return new Integer(hits.getLength());

}

}

);

}

return numberOfHits.intValue();

}

<beans>

<bean id="libraryCompass" class="LibraryCompassDao">

<property name="compass">

<ref local="compass" />

</property>

</bean>

</beans>

<!-- COMPASS START -->

<bean id="compass" class="org.compass.spring.LocalCompassBean">

<property name="resourceLocations">

<list>

<value>classpath:org/compass/sample/petclinic/petclinic.cmd.xml</value>

<value>classpath:petclinic.cpm.xml</value>

</list>

</property>

<property name="compassSettings">

<props>

<prop key="compass.engine.connection">file://${user.home}/compass/petclinic</prop>

<prop key="compass.transaction.factory">org.compass.spring.transaction.SpringSyncTransactionFactory</prop>

</props>

</property>

<property name="transactionManager">

<ref local="transactionManager" />

</property>

</bean>

<bean id="hibernateGpsDevice" class="org.compass.spring.device.hibernate.SpringHibernate3GpsDevice">

<property name="name"><value>hibernateDevice</value></property>

<property name="sessionFactory"><ref local="sessionFactory" /></property>

</bean>

<bean id="compassGps" class="org.compass.gps.impl.SingleCompassGps" init-method="start" destroy-method="stop">

<property name="compass"><ref bean="compass" /></property>

<property name="gpsDevices">

<list>

<ref local="hibernateGpsDevice" />

</list>

</property>

</bean>

<!-- COMPASS END -->

<!-- Transaction manager for a single Hibernate SessionFactory (alternative to JTA) -->

<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">

<property name="sessionFactory"><ref local="sessionFactory"/></property>

</bean>

<!----------------------------一下是對anototion的配置。-->

<bean id="annotationConfiguration"

class="org.compass.annotations.config.CompassAnnotationsConfiguration">

</bean>

<!-- 核心Compass Bean,search及index時使用 -->

<bean id="compass" class="org.compass.spring.LocalCompassBean">

<!-- anontaition式設定 -->

<property name="classMappings">

<list>

<value>com.dengyin.compass.sample.domain.Book</value>

</list>

</property>

<property name="compassConfiguration" ref="annotationConfiguration"/>

<!-- xml 檔案式設定

<property name="resourceLocations">

<list>

<value>classpath:compass-springside.cmd.xml</value>

<value>classpath:compass-springside.cpm.xml</value>

</list>

</property>

-->

<property name="compassSettings">

<props>

<prop key="compass.engine.connection">

file://${user.home}/springside/compass

</prop>

<prop key="compass.transaction.factory">

org.compass.spring.transaction.SpringSyncTransactionFactory

</prop>

</props>

</property>

<property name="transactionManager" ref="transactionManager"/>

</bean>

<!--Compass的GPS綁定,在index時使用-->

<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.hibernate.SpringHibernate3GpsDevice">

<property name="name">

<value>hibernateDevice</value>

</property>

<property name="sessionFactory" ref="sessionFactory"/>

</bean>

</list>

</property>

</bean>