由於大量的數據使用MongoDB作為數據庫進行存儲,現在需要對外提供文本搜索功能,在實踐工程中,調研並嘗試了多種方案,在本文中做個記錄。
1、使用正則表達式
即使用find("textField":/搜索文本/}的方式對文本字段進行包含匹配。
這種方法的好處在於不需要進行額外的操作,現在常用的MongoDB服務器版本都能夠支持,非常方便。
但是同時,缺點也非常明顯,每次搜索必須遍歷所有的數據,消耗的時間與數據量成正比,且基本無法進行優化。在大數據量情況下每次搜索小號的時間太長,可能無法接受。
2、分詞存儲
基本的步驟如下:
1、使用分詞引擎對目標文本字段,例如textField進行分詞
2、將分詞的結果的每個詞按照數組的形式存儲在一個新的字段,例如searchField中
3、對searchField建立索引
4、搜索時,對請求串進行同樣的分詞,並將分詞結果作為參數進行find({ searchField: {$all: splittedTextArray} })
這種方法的好處在於,適用於各個版本的MongoDB,同時基於索引和MongoDB對於數組的專門優化,在一定數據量的情況下速度也還可以
如果要使用這種方法,需要在新數據入庫時進行處理
功能上比較簡單,很多搜索常用的需求無法實現,效率也比不上專業的搜索引擎。可以作為一種無法增加組件時的參考方案。
3、使用MongoDB自帶的文本搜索功能
MongoDB在較早的版本中就已經將文本搜索功能作為實驗性功能加入,當時需要使用runCommand作為系統命令進行調用。
在后來的版本中作為正式的功能引入,直接就可以使用find({"$text":{"$search":"搜索文本"})進行搜索
在搜索之前,需要對文本字段建立索引:
db.coll.createIndex( { textField: "text" } )
對於一個集合,只能建立一個索引,但是一個索引中可以包含多個字段
文本搜索的參數有如下幾個:
$search: <string>,
$language: <string>,
$caseSensitive: <boolean>,
$diacriticSensitive: <boolean>$search后面的關鍵詞可以有多個,並且可以使用一些符號表示與或非的關系
使用$caseSensitive設置是否區分大小寫,$language指示搜索的語言類型
$diacriticSensitive設置是否區別發音符號,例如,CAFÉ於Café是同一語義,只是重音不一樣。
並且在結果中,可以使用{score:{$meta:"textScore"}來獲得相似度
不過,只有在Mongodb 3.2之后的企業版中才開始加入了對中文的支持,之前或者社區的版本在建立全文索引時會自動過濾中文字符。
在Mongodb 3.2企業版中,對中文建立全文索引后,默認使用的是與英文同樣的分詞規則,即以空格與符號作為詞與詞之間分隔的界限。
所以,當使用“福爾摩斯”作為搜索詞時,無法搜索到“福爾摩斯探案全集”,只能搜索到類似“福爾摩斯(1)”之類的結果
如果想解決這種情況,可以有兩種方法:
一、在存儲文本時,自行先對該文本進行分詞處理,將需要搜索的文本加上空格即可
二、為MongoDB添加中文分詞的支持
MongoDB企業版集成了基礎技術Rosette 語義平台(RLP)根據語言來進行正規化、分詞、斷句、詞干和分詞。所以,可以使用RLP來實現對中文分詞的支持。
如果想在MongoDB中使用RLP,MongoDB需要一個RLP基礎語義組件的許可證。具體如何獲得RLP的許可證,需要聯系basistech公司。
當從basistech公司的郵件中獲取了RLP證書文件(rlp-license.xml)后,解壓拷貝到對應的目錄中
<BT_ROOT>/rlp/rlp/licenses
然后使用參數指定該目錄,重啟MongoDB即可
--basisTechRootDirectory=<BT_ROOT>
