Compass入門及其與Spring、iBatis的整合

  • 開始之前
  • 什麼是Compass
  • 與Spring、iBatis的整合
  • 與Lucene的比較
  • 經驗總結
  • 相關資源


開始之前

本文是Compass的入門指引,通過實例介紹了Compass與iBatis、Spring的整合,適合不瞭解Compass的讀者,但要求讀者瞭解Lucene、Spring和iBatis,寫過一些簡單的應用。
文中使用的軟件包:

 

 

什麼是Compass

Compass是一個Java搜索框架。它封裝了Lucene,增加了一些Lucene不支持的特性(例如實時更新索引),支持各種數據(Java對象、xml、json)到索引的映射,支持各種數據源(JDBC, Hibernate, iBatis)。

 

 

圖解(看得煩的直接跳過看下面的例子吧):

  • Compass - 一般在程序啓動時建立並被整個程序共享,主要用於建立CompassSession並通過其管理索引數據。
  • CompassSession - 用於處理數據的session。
  • CompassTransaction - 手動進行事務管理,如果不使用,Compass會自動管理事務。
  • CompassTemplate - 將session和transaction透明化。
  • 數據到索引的各種映射 - OSEM, XSEM, JSEM, RSEM。支持通過程序、XML、JSON進行配置。
  • CompassGps - Gps的核心模塊,管理GpsDevice,有兩種實現:SingleCompassGps和DualCompassGps。
  • CompassGpsDevice - 處理各種數據源到索引的操作:JDBC, Hibernate, iBatis等。不能獨立使用而必須融合到CompassGps中。

 

與Spring、iBatis的整合

建索引

1、假設Spring + iBatis的框架已經搭建好。

2、配置Domain的OSEM

Java代碼  收藏代碼
  1. @Searchable (alias= "user" )  
  2. public   class  User {  
  3.   
  4.   @SearchableId   
  5.   private   int  id;  
  6.   
  7.   @SearchableProperty (index=Index.ANALYZED, store=Store.YES)  
  8.   private  String name;  // 姓名   
  9.   
  10.   @SearchableProperty (index=Index.NOT_ANALYZED, store=Store.YES)  
  11.   private  String gender;  // 性別   
  12.   
  13.   @SearchableProperty (index=Index.NOT_ANALYZED, store=Store.YES)  
  14.   private   int  age;  // 年齡   
  15.   
  16.   public  User() {  
  17.   }  
  18.   
  19.   public  User(String name, String gender,  int  age) {  
  20.     setName(name);  
  21.     setGender(gender);  
  22.     setAge(age);  
  23.   }  
  24.   
  25.   public   int  getId() {  
  26.     return  id;  
  27.   }  
  28.   
  29.   public   void  setId( int  id) {  
  30.     this .id = id;  
  31.   }  
  32.   
  33.   public  String getName() {  
  34.     return  name;  
  35.   }  
  36.   
  37.   public   void  setName(String name) {  
  38.     this .name = name;  
  39.   }  
  40.   
  41.   public  String getGender() {  
  42.     return  gender;  
  43.   }  
  44.   
  45.   public   void  setGender(String gender) {  
  46.     this .gender = gender;  
  47.   }  
  48.   
  49.   public   int  getAge() {  
  50.     return  age;  
  51.   }  
  52.   
  53.   public   void  setAge( int  age) {  
  54.     this .age = age;  
  55.   }  
  56.   
  57. }  

 

其實就是加幾個Annotation而已。看到Index.ANALYZED、Store.YES這些東西,用過Lucene的應該大概都明白了吧。

  • @Searchable - 指明該類可被映射至索引,alias參數是這一類索引對象的別名。
  • @SearchableId - 索引對象的id,在同一類索引對象(同一個alias)中唯一標識一個對象。
  • @SearchableProperty - 指示一個類屬性如何被索引,index和store參數類似Lucene。

3、建立LocalCompassBean,配置索引文件存放路徑和進行映射的domain。

Xml代碼  收藏代碼
  1. < bean   id = "compass"   class = "org.compass.spring.LocalCompassBean" >   
  2.   < property   name = "compassSettings" >   
  3.     < props >   
  4.       <!-- 索引文件保存路徑 -->   
  5.       < prop   key = "compass.engine.connection" > /home/index/compasstest </ prop >   
  6.     </ props >   
  7.   </ property >   
  8.   < property   name = "classMappings" >   <!-- 進行映射的domain -->   
  9.     < list >   
  10.       < value > ren.domain.User </ value >   
  11.       < value > ren.domain.Book </ value >   
  12.     </ list >   
  13.   </ property >   
  14. </ bean >   

 

4、建立SqlMapClientGpsDevice,配置iBatis的sqlMapClient和獲取數據進行索引的SQL語句id。

Xml代碼  收藏代碼
  1. < bean   id = "ibatisGpsDevice"   class = "org.compass.gps.device.ibatis.SqlMapClientGpsDevice" >   
  2.   < property   name = "name"   value = "ibatis"   />   
  3.   < property   name = "sqlMapClient" >   
  4.     < ref   bean = "sqlMapClient"   />   <!-- 引用項目中已經定義的ibatis的sqlMapClient -->   
  5.   </ property >   
  6.   < property   name = "selectStatementsIds" >   <!-- 對這些SQL查詢的結果進行索引 -->   
  7.     < list >   
  8.       < value > user.getAllUsers </ value >   
  9.       < value > book.getAllBooks </ value >   
  10.     </ list >   
  11.   </ property >   
  12. </ bean >   

 

5、建立CompassGps(SingleCompassGps或DualCompassGps),引用前面的compass和device。

Xml代碼  收藏代碼
  1. < bean   id = "compassGps"   class = "org.compass.gps.impl.SingleCompassGps"   
  2.   init-method = "start"   destroy-method = "stop" >   
  3.   < property   name = "compass"   ref = "compass"   />   
  4.   < property   name = "gpsDevices" >   
  5.     < list >   
  6.       < ref   local = "ibatisGpsDevice" />   
  7.     </ list >   
  8.   </ property >   
  9. </ bean >   


6、最後,直接調用CompassGps.index()方法建立索引。

Java代碼  收藏代碼
  1. @Component   
  2. @Qualifier ( "indexBuilder" )  
  3. public   class  IndexBuilder {  
  4.   
  5.   @Autowired   
  6.   @Qualifier ( "compassGps" )  
  7.   private  CompassGps compassGps;  
  8.     
  9.   public   void  buildIndex() {  
  10.     compassGps.index(); // 一行代碼搞定   
  11.   }  
  12.   
  13. }  

 

查索引

1、建立CompassTemplate,引用LocalCompassBean。

Xml代碼  收藏代碼
  1. < bean   id = "compassTemplate"   class = "org.compass.core.CompassTemplate" >   
  2.   < property   name = "compass" >   
  3.     < ref   bean = "compass"   />   
  4.   </ property >   
  5. </ bean >   
 

 

2、使用CompassTemplate.execute(CompassCallback<T>)進行查詢。

Java代碼  收藏代碼
  1. @Component   
  2. @Qualifier ( "indexSearcher" )  
  3. public   class  IndexSearcher {  
  4.     
  5.   @Autowired   
  6.   @Qualifier ( "compassTemplate" )  
  7.   private  CompassTemplate compassTemplate;  
  8.     
  9.   /**  
  10.    * 搜索用戶  
  11.    */   
  12.   public  List<User> searchUser( final  String name,  final  String gender,  final   int  age) {  
  13.     return  compassTemplate.execute( new  CompassCallback<List<User>>() {  
  14.       public  List<User> doInCompass(CompassSession session)  throws  CompassException {  
  15.         CompassQueryBuilder builder = session.queryBuilder();  
  16.         String queryString = "" ;  
  17.         if  (!StringUtils.isBlank(name)) {  
  18.           queryString += "and user.name:"  + name;  
  19.         }  
  20.         if  (!StringUtils.isBlank(gender)) {  
  21.           queryString += "and user.gender:"  + gender;  
  22.         }  
  23.         if  (age >  0 ) {  
  24.           queryString += "and user.age:"  + age;  
  25.         }  
  26.         CompassQuery query = builder.queryString(queryString).toQuery();  
  27.         query.addSort("user.age" , SortPropertyType.INT, SortDirection.REVERSE);  
  28.         CompassHits hits = query.hits();  
  29.         List<User> list = new  ArrayList<User>();  
  30.         for  (CompassHit hit : hits) {  
  31.           list.add((User)hit.data());  
  32.         }  
  33.         return  list;  
  34.       }  
  35.     });  
  36.   }  
  37.   
  38. }  

 

拼查詢字符串這裏寫得比較累贅,小朋友不要學~

 

與Lucene的比較

1、Compass有比Lucene更易用的API(廢話,封裝了Lucene嘛),例如支持直接更新記錄(因爲resource類似數據庫記錄,含有主鍵)。像上面的建索引過程,如果用Lucene,肯定得寫很多Java代碼。

2、支持整合各種ORM框架和Spring,減少了代碼量。例如上面例子中整合iBatis,直接幾行配置就搞定了。

3、效率問題?感覺Lucene的API用起來老是不順手,Compass這樣封裝雖然方便了,但有些擔心會不會降低了性能,於是做了個簡單的測試,分別索引4萬條記錄,結果是
         Compass: 12203 ms.
         Lucene: 9797 ms.

     Compass比Lucene慢了大約25%,當然這個測試十分粗略,結果僅供參考。

 

經驗總結

1、對多個表建索引後進行搜索,在添加排序條件時,如果不指定SortPropertyType,那麼在沒有指定converter的字段上排序時會拋Exception:
    java.lang.RuntimeException: field "gender" does not appear to be indexed
    但如果只對單個表建索引,不會有這個問題。應該是Compass的一個bug,不知道新版本有沒有解決。

 

2、最好自己封裝排序字段和分頁。

 

3、總結,Compass比較適用於邏輯不太複雜的應用,會比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

 

大型網站的Lucene應用:http://www.luanxiang.org/blog/archives/605.html

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章