本文是Compass的入門指引,通過實(shí)例介紹了Compass與iBatis、Spring的整合,適合不了解Compass的讀者,但要求讀者了解Lucene、Spring和iBatis,寫過一些簡單的應(yīng)用。
文中使用的軟件包:
Compass是一個Java搜索框架。它封裝了Lucene,增加了一些Lucene不支持的特性(例如實(shí)時更新索引),支持各種數(shù)據(jù)(Java對象、xml、json)到索引的映射,支持各種數(shù)據(jù)源(JDBC, Hibernate, iBatis)。
圖解(看得煩的直接跳過看下面的例子吧):
1、假設(shè)Spring + iBatis的框架已經(jīng)搭建好。
2、配置Domain的OSEM
@Searchable(alias="user")public class User {@SearchableIdprivate int id;@SearchableProperty(index=Index.ANALYZED, store=Store.YES)private String name; // 姓名@SearchableProperty(index=Index.NOT_ANALYZED, store=Store.YES)private String gender; // 性別@SearchableProperty(index=Index.NOT_ANALYZED, store=Store.YES)private int age; // 年齡public User() {}public User(String name, String gender, int age) {setName(name);setGender(gender);setAge(age);}public int getId() {return id;}public void setId(int id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getGender() {return gender;}public void setGender(String gender) {this.gender = gender;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}}
其實(shí)就是加幾個Annotation而已。看到Index.ANALYZED、Store.YES這些東西,用過Lucene的應(yīng)該大概都明白了吧。
3、建立LocalCompassBean,配置索引文件存放路徑和進(jìn)行映射的domain。
<bean id="compass" class="org.compass.spring.LocalCompassBean"><property name="compassSettings"><props><!-- 索引文件保存路徑 --><prop key="compass.engine.connection">/home/index/compasstest</prop></props></property><property name="classMappings"> <!-- 進(jìn)行映射的domain --><list><value>ren.domain.User</value><value>ren.domain.Book</value></list></property></bean>
4、建立SqlMapClientGpsDevice,配置iBatis的sqlMapClient和獲取數(shù)據(jù)進(jìn)行索引的SQL語句id。
<bean id="ibatisGpsDevice" class="org.compass.gps.device.ibatis.SqlMapClientGpsDevice"><property name="name" value="ibatis" /><property name="sqlMapClient"><ref bean="sqlMapClient" /> <!-- 引用項(xiàng)目中已經(jīng)定義的ibatis的sqlMapClient --></property><property name="selectStatementsIds"> <!-- 對這些SQL查詢的結(jié)果進(jìn)行索引 --><list><value>user.getAllUsers</value><value>book.getAllBooks</value></list></property></bean>
5、建立CompassGps(SingleCompassGps或DualCompassGps),引用前面的compass和device。
<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="ibatisGpsDevice"/></list></property></bean>
6、最后,直接調(diào)用CompassGps.index()方法建立索引。
@Component@Qualifier("indexBuilder")public class IndexBuilder {@Autowired@Qualifier("compassGps")private CompassGps compassGps;public void buildIndex() {compassGps.index(); // 一行代碼搞定}}
1、建立CompassTemplate,引用LocalCompassBean。
<bean id="compassTemplate" class="org.compass.core.CompassTemplate"><property name="compass"><ref bean="compass" /></property></bean>
2、使用CompassTemplate.execute(CompassCallback<T>)進(jìn)行查詢。
@Component@Qualifier("indexSearcher")public class IndexSearcher {@Autowired@Qualifier("compassTemplate")private CompassTemplate compassTemplate;/*** 搜索用戶*/public List<User> searchUser(final String name, final String gender, final int age) {return compassTemplate.execute(new CompassCallback<List<User>>() {public List<User> doInCompass(CompassSession session) throws CompassException {CompassQueryBuilder builder = session.queryBuilder();String queryString = "";if (!StringUtils.isBlank(name)) {queryString += "and user.name:" + name;}if (!StringUtils.isBlank(gender)) {queryString += "and user.gender:" + gender;}if (age > 0) {queryString += "and user.age:" + age;}CompassQuery query = builder.queryString(queryString).toQuery();query.addSort("user.age", SortPropertyType.INT, SortDirection.REVERSE);CompassHits hits = query.hits();List<User> list = new ArrayList<User>();for (CompassHit hit : hits) {list.add((User)hit.data());}return list;}});}}
拼查詢字符串這里寫得比較累贅,小朋友不要學(xué)~
1、Compass有比Lucene更易用的API(廢話,封裝了Lucene嘛),例如支持直接更新記錄(因?yàn)閞esource類似數(shù)據(jù)庫記錄,含有主鍵)。像上面的建索引過程,如果用Lucene,肯定得寫很多Java代碼。
2、支持整合各種ORM框架和Spring,減少了代碼量。例如上面例子中整合iBatis,直接幾行配置就搞定了。
3、效率問題?感覺Lucene的API用起來老是不順手,Compass這樣封裝雖然方便了,但有些擔(dān)心會不會降低了性能,于是做了個簡單的測試,分別索引4萬條記錄,結(jié)果是
Compass: 12203 ms.
Lucene: 9797 ms.
Compass比Lucene慢了大約25%,當(dāng)然這個測試十分粗略,結(jié)果僅供參考。
1、對多個表建索引后進(jìn)行搜索,在添加排序條件時,如果不指定SortPropertyType,那么在沒有指定converter的字段上排序時會拋Exception:
java.lang.RuntimeException: field "gender" does not appear to be indexed
但如果只對單個表建索引,不會有這個問題。應(yīng)該是Compass的一個bug,不知道新版本有沒有解決。
2、最好自己封裝排序字段和分頁。
3、總結(jié),Compass比較適用于邏輯不太復(fù)雜的應(yīng)用,會比Lucene少寫很多代碼。但如果需要一些較為特殊的需求,或者對效率要求比較高,還是用Lucene吧。
Compass入門指南:http://www.yeeach.com/2008/03/23/compass-%E5%85%A5%E9%97%A8%E6%8C%87%E5%8D%97/
全文檢索的基本原理:http://blog.csdn.net/forfuture1978/archive/2009/10/22/4711308.aspx
大型網(wǎng)站的Lucene應(yīng)用:http://www.luanxiang.org/blog/archives/605.html
這篇文章是根據(jù)自己的一次演講整理出來的,主要就是PPT內(nèi)容 + 代碼。演講和寫文章確實(shí)很不同,感覺很多地方還說不清楚,因?yàn)閮?nèi)容太多了吧,看來自己的書面組織能力還有待提高啊~