JWorld@TW the best professional Java site in Taiwan
      註冊 | 登入 | 全文檢索 | 排行榜  

» JWorld@TW » Object Relational Mapping » Hibernate  

按列印兼容模式列印這個話題 列印話題    把這個話題寄給朋友 寄給朋友    訂閱主題
reply to postflat modego to previous topicgo to next topic
本主題所含的標籤
作者 Hibernate Search的一些體驗
kentyeh





發文: 649
積分: 6
於 2012-12-28 15:47 user profilesend a private message to userreply to postreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
從JPA到資料庫搜尋

相信很多人使用ORM時,大多採用了JPA(以前我比較喜歡用Hibernate,可供查詢的型別比較豐富,像ENum都可以拿來直接當作查詢的參數,不過後來也不的不隨波逐流);
以前想要用Luence(最後沒辦法用,因為很多的搜尋術語與技術都不懂)找資料庫,應該是會寫到昏天闇地吧?
感謝Hibernet Search,讓我這些只會用JPA的人可以輕易對資料庫進行搜尋。
首先來個範例 程式吧!附件中的範例程式是我修改過的,畢竟,使用原版的程式不好進行說明。

建立專案
首先,用過Hiberenate的都知道Hibernate會使用一堆 hibernate.xxx...的屬性來對Hibernate進行設定,首先必須要設定兩個屬性
hibernate.search.default.directory_provider: 設為filesystem表示將索引值儲存到檔案系統,其它值請參考文件說明
hibernate.search.default.indexBase:設定索引所要儲存的目錄路徑

JPA物件說明與設定
整個專案只有兩個JPA物件,一個是Book(書籍),一個是Author(作者);兩個是多對多的關係,也就是一個作者可能寫好幾本書,或者多個作者合著一本書,不過,專案內並未設置多對多的關系,只是可以由書籍找到多個作者。
首先要注意的是Hibernet Search利用了JPA的物件架構,所以查詢所得都是JPA物件,因為我們所要搜尋的主體是Book(書籍),所以我們必須先將Book 用@Indexed標記起來,表示Book會是搜尋所得之結果,因為我們不會對將Author(作者)作為查詢結果,所以不會對Author以@Indexed進行標記。
另外,在程式設計之前就必須考慮:物件的那些欄位將來是要用來搜尋之用?這裡原設計者的考慮是,只將書籍的 標題、次標題、出版日期與作者等四個欄位拿來作為搜尋之用,所以先看一下Book.java
1
2
3
4
5
6
7
8
9
10
11
12
13
@Indexed
public class Book implements Serializable {@Field(index = Index.YES, analyze = Analyze.YES, store = Store.NO)
    public String getTitle() {
        return title;
   }
   @Field(index = Index.YES, analyze = Analyze.NO, store = Store.YES)
   @DateBridge(resolution = Resolution.DAY)
    @Temporal(javax.persistence.TemporalType.DATE)
    public Date getPublicationDate() {
        return publicationDate;
    }

@Field是用來告訴Hibernet Search這個欄位會用來搜尋,其中屬性index用來說明這個欄位是否要加以索引,analyze是用來指示該欄位是否須加以解悉,大部分的情形是YES,除非該欄位的內容是不可分割,例如作者人名。store則是說明該欄位值是否會被儲專到索引裡面,以供未來投影(project:官方的說法是直接將儲存的欄位內容回寫到物件)之用。
除了@Field之外,我們看到了一個出版日期標記了一個@DateBridge,為什麼會有這個東西,那是因為Luence內的索引只能是字串,對於不是字串的欄位,Hibernet Search就必須使用一些小技巧來進行字串與非字串物件之間的轉換,而這個轉換就是透過Bridge來完成(通常Bridge欄位不會進行內容解悉,且會將值存到索引內)。
另外看一下關於作者的欄位部分:
1
2
3
4
5
6
7
8
@Indexed
public class Book implements Serializable {@IndexedEmbedded
    @ManyToMany
    public Set<Author> getAuthors() {
        return authors;
    }

@IndexedEmbedded是用來標記@ManyToMany, @*ToOne, @Embedded 與 @ElementCollection等屬性之用的,表示也要索引在這些個別物件之內的欄位,在此也就是Author的欄位name也要加以索引
1
2
3
4
5
6
public class Author implements Serializable {
   …
    @Field(index = Index.YES, analyze = Analyze.YES, store = Store.NO)
    public String getName() {
        return name;
   }

到此為止,只要JPA開始對資料物件進行增刪改,就會進行索引變更。

索引
所謂的搜尋,就是事先把資料建好索引,然後在必要的時候快速找出所有的資料,上述說過,JPA資料增刪改的,索引會自動加以變更,但是對已經有許多資料的時候,要如何建立整個索引呢?
首先我們要取得Hibernet的全文檢索物件,我們可以取得FullTextSession(用Hibernat的人所使用)或者FullTextEntityManager(用JPA的人使用),方法如下:
1
2
FullTextSession fullTextSession = Search.getFullTextSession(session);
fullTextSession.createIndexer().startAndWait();


1
2
3
EntityManager em = entityManagerFactory.createEntityManager();
FullTextEntityManager fullTextEntityManager = Search.getFullTextEntityManager(em);
fullTextEntityManager.createIndexer().startAndWait();


原始範例程式,在每次測試方法前,都重新建立索引,其實沒有必要,所以我修改後的程式只在一開始的時候建立一次索引。
另外,範例程式在測試結束前,都把索引清空,您可以考慮把範例程式中的purge() remark起來,然後用工具luke去看看Hibernet Search倒底索引了那些內容。

解悉欄位內容
所謂的解析就是要不要把欄位的內容加以拆解(分詞),例如:據路透社報導,印度尼西亞社會事務部一官員星期二(29日)表示,日惹市附近當地時間27日晨5時53分發生的裡氏6.2級地震已經造成至少5427人死亡,20000餘人受傷,近20萬人無家可歸。可以拆解為據 | 路透社 | 路透 | 社 | 報導 | 印度尼西亞 | 印度 | 尼 | 西亞 | 社會事務 | 社會 | 事務部 | 事務 | 部 | 一 | 官員 | 星期二 | 星期 | 二 | 29 | 日 | 表示 | 日 | 惹 | 市 | 附近 | 當地時間 | 當地 | 時間 | 27 | 日 | 晨 | 5 | 時 | 53 | 分發 | 分 | 發生 | 發 | 生 | 的 | 裡氏 | 6.2 | 級 | 地震 | 已經 | 造成 | 至少 | 5427 | 人 | 死亡 | 20000 | 餘人 | 受傷 | 近 | 20 | 萬人 | 萬 | 人 | 無家可歸 。

看了一些資料後,決定選用ik-analyze作為中文分詞器,然後想說再找一個中文詞庫資料來搭配,可惜找了找,都沒有正體中文的免費詞庫資料可用,所以決定找一些簡體中文的詞庫(包含ik-analyze盤古分詞je-analysis)轉成正體中文後再用新同文堂的詞庫轉換一下,產生新的文檔(附件內附有一個chword.sql.zip可轉入資料庫sql)。

ik-analyze的用法說明如下:
首先必須在classpath下放置一個 IKAnalyzer.cfg.xml 設定檔,目前內容設南如下:
1
2
3
4
5
6
7
8
9
10
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">  
<properties>  
  <comment>IK Analyzer 擴展配置</comment>
  <!--用戶可以在這裡配置自己的擴展字典 -->
  <entry key="ext_dict">ext.dic;</entry> 
  <!--用戶可以在這裡配置自己的擴展停止詞字典-->
  <entry key="ext_stopwords">stopword.dic;</entry> 
  
</properties>

由上面的設定可知,同樣地,在Classpath下有兩個檔案,第一個是 ext.dic,我把一些ik-analyze沒有內建的詞(含正體中文)放在這個檔內,另外一個則是擴展的停止詞(也就是一 些沒有義意而要忽略的詞)。

我們再回頭看看Book.java的設定
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Entity
@AnalyzerDef(name = "customanalyzer",
tokenizer =
@TokenizerDef(factory = IKTokenizerFactory.class),
filters = {
    @TokenFilterDef(factory = SmartChineseWordTokenFilterFactory.class),
    @TokenFilterDef(factory = LowerCaseFilterFactory.class),
    @TokenFilterDef(factory = PositionFilterFactory.class),
    @TokenFilterDef(factory = CJKWidthFilterFactory.class),
    @TokenFilterDef(factory = SnowballPorterFilterFactory.class, params = {
        @Parameter(name = "language", value = "English")
    })
})
@Indexed
public class Book implements Serializable {
    ...
    @Field(index = Index.YES, analyze = Analyze.YES, store = Store.NO)
    @Analyzer(definition = "customanalyzer")
    public String getTitle() {
        return title;
    }
}

這裡使用了一個@AnalyzerDef,自定一個分詞器(若不指定預設使用StandardTokenizer),裡面包含若干個@TokenFilterDef(官方的說法是預先處理要處理的文字內容,例如同義詞就是可以在此時加入),老實講,上面那幾個配置,我不是很清楚在它們的行為在做啥,只知例如LowerCaseFilterFactory是負責把英文大寫內容轉成小寫。所以配置方面可能要再討論討論)。

最後我們使用@Analyzer告訴Hibernet Search,書本的標題,要使用自定義的斷詞設定來斷詞。

搜尋
執行"mvn test"看看IndexAndSearchTest.java是如何來進行搜尋(不同的搜尋方式參考官方文件)。

待解
老實說,我還不瞭解中文斷詞的配置是否有錯,因為從測試結果來看,使用"設計模式"搜尋的結果,找出了一些好像沒有關聯的資料。
有關這部分,可能還得請一些大得幫忙補充一下知識。
另外遺憾的是我也不知道如何加入同義字,不知道是否有人可以指教指教的。

ps. 沒辦法,由於附件程式太大,JW只允許放1MB,所請只好放在其它的地方下載。


reply to postreply to post
話題樹型展開
人氣 標題 作者 字數 發文時間
2563 Hibernate Search的一些體驗 kentyeh 8497 2012-12-28 15:47
1312 Re:Hibernate Search的一些體驗 anthonychen 16 2013-01-08 14:53
» JWorld@TW »  Object Relational Mapping » Hibernate

reply to postflat modego to previous topicgo to next topic
  已讀文章
  新的文章
  被刪除的文章
Jump to the top of page

JWorld@TW 本站商標資訊

Powered by Powerful JuteForum® Version Jute 1.5.8