Xapian 的幾個細節

這篇是延續 幫 Pixnet 做 Fulltext Search 這篇所寫的,這項功能 Pixnet 已經放出來了:[公告] 新增網誌搜尋(Beta)。 最後收尾的階段有一些地雷要注意: Xapian 對大的 Database 更新會很慢。一開始我是對單一 Database 更新,結果更新的速度跟不上條目修改的速度 XD 所以,我跟 far 稍微提過以後,決定用空間換取時間,以 ArticleID 拆開,一萬為一個單位把 Xapian Database 切成 n 個 DB 放在 NetApp 上,更新時是對這些小資料庫更新,最後再跑 xapian-compact 合併。這樣就跟的上速度了:三十萬篇大約需要兩個小時的更新 + 六個小時的合併。 有一天心情不好 (原因就不提了),決定把 xapian-compact 的 -m 拿掉 (multipass,官方建議當你有許多 DB 要合併時,使用這個參數「通常」會比較快),結果莫名其妙把合併的時間省到 40mins... (當場就把「靠腰」罵出來了) 最後,把本來是用 standalone 的版本換成 lighttpd + FastCGI 版,理論上就不會出現因為後端搜尋伺服器沒跑起來,而使得前端出現 0 result 的情況了... (汗) PS:因為當初做的時候有得到 far 的同意,所以等到我閒一點之後,我會用 BSD license 放出來...

GPL 與 PHP License

這陣子在 Xapian 的 mailing list 上剛好提到 PHP binding 的問題,結果引伸出來的 license 問題。查了資料以後,在 Wikipedia 上有張表格:List of FSF approved software licenses。 簡單來說,PHP license 與 OpenSSL license 不相容的原因都一樣:因為廣告條款的關係。 Xapian 想要用其他方式解套 (像是有人提出用 SWIG 過水) 都會因為 GPL 有感染性而不太容易處裡:SWIG 軟體本身是 MIT-style 與 BSD-style license,但是他產生出來的 wrapper code 因為使用了 GPL library,所以包成 binary 時還是會受到 GPL 影響。 反正這類問題滿天飛...

幫 Pixnet 做 Fulltext Search

這幾天總算是比較接近完成的階段了,在問過 far 之後來寫一篇講一下。 大約是在年初的時候 far 就想要將 Pixnet Blog 的部份弄 Fulltext Search,剛好那陣子我也在弄類似的東西,survey 一些對 CJK 支援比較好的 Fulltext Search Engine,當時看起來比較好用是 平林幹雄 維護的 HyperEstraier。(我也寫了 HyperEstraier 與 HyperEstraier 與 qdbm 的一些雜記 兩篇文章) 不過後來 HyperEstraier 將近三個月沒更新,加上 Pixnet 那陣子在忙公司整合的事情,所以這件事情在收了訂金後就一直擱著... (皇上都不急了,太監就不要在旁邊跳腳...) 除了 HyperEstraier 外,我還有訂另外幾個 Fulltext Search Engine 的 mailing list,其中 Gmane 用的 Xapian 也蠻值得關注的。我從 0.9.x 版的時候就在觀察,當時連 UTF8 support 都沒有,但是 Gmane 有丟出 3rd party patch。不過我寫信去問 Gmane 得到的回答是,文章數量很大,但是每天搜尋的人次並不多,平均不到 1 query/sec。(信件在 /var/mail/gslin,隨著硬碟爛掉沒備份到消失了,只能憑映象寫...) 再加上 Gmane 以英文為主,當時沒有很仔細繼續去研究,直到後來在 Xapian 的 mailing list 上看到有對岸的人說中文的部份他用的很好,仔細去研究以後發現其實是因為以前想的太複雜,加上對 Xapian 不熟造成的誤解而沒有仔細去研究 Xapian 的細節。 所以... 以下進入 Xapian 的細節。 Xapian 的每一份文件 (Document) 分成三個部份,文件主體 (Data)、值 (Value)、項目 (Term)。Data 是一組字串,通常就是放文章內容,但是也可以不用放。Value 是 key+value 的集合,key 的部份是數字,而 value 的部份則是一個字串,Value 可以用很多組。(這邊的 "value" 並不是遞迴定義,而只是官方取的名字剛好就是 Value) 不過上面那兩項都是插花用的,重點在 Term。一個 Term 是一個帶有 weight 的字串,會被 Xapian 索引。所以你可以對 Term 下搜尋條件,Xapian 會傳回符合的項目。 對英文來說,假設我要索引 "A apple a day keeps the doctor away." 這個句子,那麼我只要以非英文字的部份切開就可以了,換句話說,最簡單的情況會被切成:['a', 'apple', 'a', 'day', 'keeps', 'the', 'doctor', 'away'],另外內建常用字的詞庫把一些比較沒有意義的詞濾掉,於是 a、the 就會被濾掉變成:['apple', 'day', 'keeps', 'doctor', 'away'],然後再進一步對詞性做處理,於是把 keeps 轉成 keep,最後索引成:['apple', 'day', 'keep', 'doctor', 'away']。 對中文來說 (以及日文、韓文),假設我要索引「端午假期16日、20日兩次夜間暫停收費」這個句子,以單字一個一個切開,切成 ['端', '午', '假', '期', '16', '日', '20', '日', '兩', '次', '夜', '間', '暫', '停', '收', '費'],或是兩個字兩個字切,切成 ['端午', '午假', '假期', '期16', '16日', '20日', '日兩', '兩次', '次夜', '夜間', '間暫', '暫停', '停收', '收費'] 似乎都不太好,前者當搜尋條件很少的時候準確度不太高 (像是以「林洋港」為條件搜尋時可能會找到討論海港旁邊樹林的文章),而後者則會收了不少沒有意義的字,像是 '期16', '日兩', '間暫',而且像是 '次夜' 其實不是這句的應該有的意思。 所以對於 CJK 的斷詞演算法來說,能夠切出符合的 Term 才是重點,於是就有很多論文在討論這些方法... 不過很多方法都是建立在詞頻表上面,換句話說,詞頻表的正確性會影響到搜尋的品質,而為了確保詞頻表的正確性,有很多時候必須讓人力去介入,這點我跟 far 都不太喜歡,我們偏好的是即使不去管,也保持一定的水準的方法。 所以我跟 far 解釋完以後,決定用一字表 + 兩字表硬切產生所有的 Term (也就是上面兩組都取來當 Term),然後看看效果如何。在跑了二十幾個小時將全站所有文章掃過一次後內部測試 (目前最新文章的 id 是 528xxxx,實際的數字當然比這個少,不過 far 說這是商業機密不能公開 XD),far 對於搜尋出來的品質還蠻滿意的,所以就切到兩個字... (如果 far 不滿意的話我打算再把三個字的切詞加上去 XD) 然後用 Catalyst 兜個 HTTP API 出來讓前端的 Web 用,其實就很棒了 :p (我知道有 xapian-tcpsrv,不過...) 所以大概就是這樣啦,Pixnet Blog 的 Fulltext Search 應該有機會在七月底前看到吧?(因為 far 很墮落,所以我實在不能保證六月底前出的來... XD)