中文全文檢索的實現以及一些經驗(Java)
最近在項目中面臨中文全文檢索的需求,關鍵需求如下:
1 支持中文、英文字詞的全文檢索,待檢索文本是古文言文。2 全文檢索表達式支持: AND,OR,NOT,NEAR,BEFORE 運算符,支持()。
3 速度要求:400M文本,要求在2-5秒內能夠檢索完畢。
嘗試Lucene以及放棄原因:
在嘗試Lucene和不同的中文Analyzer后,終告放棄。原因如下:
由于待檢索文本是古文,中文分詞技術無法派上用場。在將分隔存儲每個漢字后,發現從Lucene中檢索到的文本遠遠少于關鍵字實際匹配的文本,這一問題對于較長的檢索關鍵字尤其明顯。
因為對于檢索準確程度要求很高,故此放棄,但是Lucene出的這個問題的原因尚不清楚,希望能夠有人提出解答。
自行實現中文全文檢索原理以及方法:
1 構建過程,忽略標點符號,自行計算每個漢字在每個文本文件中的偏移量,并保存。2 檢索過程,定位每個漢字的偏移量,如果檢索表達式中每個漢字的預期偏移量與實際偏移量吻合,則匹配成功。
3 采用 MappedByteBuffer 加快檢索速度,采用二分查找加快偏移量匹配速度,3個左右的關鍵字復合檢索能夠在1秒內完成匹配(要求操作系統有足夠大的緩存)。
目前實現的一些局限和優勢:
0 中文檢索速度足夠,準確度比Lucene高(如果有高手能夠解決這個問題,我會很高興的廢棄掉這些類的)1 合適于中文,不適用英文文本
2 全文檢索索引文件與原始文本文件的大小大約為2:3-3:4之間,300M大小,比Lucene大約多30M。
3 索引文件的構建時間長,400M大約需要3小時,同時由于如果任何文本文件更新,都需要重新構建索引文件,
因此不合適要經常變化的文本索引。
全文檢索代碼示例(TestFullTextQuery.java):
File storeDir = new File("C:\\temp\\fulltext\\index");
StoreSearcher searcher = new StoreSearcher(storeDir);
String str = "大?藏 & 阿難"; //同時出現 "大?藏" 和 "阿難", ?代表任意字符
searcher.queryBegin(str, true);
while(true){
StoreSearcherResult ssr = searcher.getNextQueryResult();
if ( ssr == null ){
break;
}
System.out.println("ID "+ssr.docId+":"+ssr.matchedCount);
}
searcher.queryEnd();
searcher.close();
運行結果
ID T01n0001.TXT:320
ID T01n0002.TXT:3
ID T01n0004.TXT:2
ID T01n0005.TXT:202
ID T01n0006.TXT:131
....
附:全文檢索表達式舉例
關鍵字中間可以出現?,表明匹配任意字符。
運算符名稱:運算符字符
AND:&
OR:,
BEFORE:*
NEAR:+
NOT:-
表達式舉例:
(KEY1 <AND|OR|BEFORE|NEAR> KEY2) & (NOT KEY3)
KEY1 KEY2 (關鍵字之間無運算符假設為AND)
附:全文檢索文件格式信息
DocInfoStore(文檔信息)
--HEAD--
DocCount:Integer 文檔數目
--DOC HEAD(PER DOC)--
DocSeq: Integer 文檔順序號,內部使用
DocId: Char[128] 文檔唯一ID,字符串格式
DocSepOfs: Integer 文檔分隔符數組的Ofs
--DOC SEP OFS(PER DOC)--
DocOfs: ArrayOfInteger 文檔分隔數組
WordInfoStore(每個漢字信息)
--HEAD--
WordCount:Integer 漢字數
--WORD IDX(Per Word)--
WordChar:Integer 漢字的Unicode值
WordInfoOfs:Integer 漢字信息在文件中的偏移量
WordInfoSize:Integer 漢字信息大小
--WORD INFO(Per Word)--
DocCount: 漢字出現的文檔數
DocSeq(Per Doc): 每個文檔的順序號
WordInDocs:ArrayOfInteger 每個文檔中出現的漢字的偏移數組,從小到大排列
源文件及CLASS下載地址:
http://m.tkk7.com/Files/zhugf000/foreader2_ftsearch.zip