Denny's profileJava DreamsBlogLists Tools Help

Blog


    July 07

    Compass读文档笔记

    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"?>
    <!DOCTYPE compass-core-mapping PUBLIC
    "-//Compass/Compass Core Mapping DTD 1.0//EN"
    "http://www.opensymphony.com/compass/dtd/compass-core-mapping.dtd">

    <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>

    ok! 相信你对Compass有一定的了解了。 thanks

    Compass: http://www.opensymphony.com/compass/
    Compass文档:http://www.opensymphony.com/compass/content/documentation.html

    trac:http://www.writely.com/View.aspx?docid=ajgd3tgv958v_0fmhxk9