在說索引之前,先說說索引是什嗎?為什麼要索引?怎麼索引?
先想想看,假如現在有一個文本,我們會怎麼去搜尋。比如,有一個string = "abcdefghijklmnopqrstuvwxyz",這都是26個字母。現在要看看裡面是不是有a,用IndexOf就可以很方便實現。現在資料量大了,在資料庫裡已經有100多條資料了,當然,利用資料庫提供的操作方法,也可以很方便的尋找。而這裡先拋開資料庫,把這100多條記錄放到N個文字檔中,現在要在裡面搜尋含有“Lucene”這個詞的記錄,那怎麼辦呢?如果只簡單地使用逐個檔案逐字掃描的話,那和用Windows內建的搜尋,搜尋一張圖片名,或者文本沒什麼分別。那麼每次搜尋都會需要大量時間,而google和baidu為什麼能做到那麼快呢?既然剛才我們是拋開資料庫,是不是使用了資料庫就能實現呢?從我長期使用的經驗來看,資料庫肯定是不行的(這裡指關係型資料庫,要是出某些專門為搜尋做的資料庫那就另外說了。)。什麼東西在搜尋的時候足夠快呢?在C#類型裡就有這麼幾個,比如Hashtable,Dictionary。做搜尋引擎是否也可以應用這樣的思想?顯然是可以的!有很多東西在微觀(這裡指一小個演算法或者一個小型的資料結構應用)和宏觀(這裡指架構級或者系統級)上名字不一樣,但是都有很多的相似性和可比性,無疑Lucene.Net就是這樣一個架構,實現了Hashtable更加宏觀的現象!當然也是有很多差別。
Lucene.Net用的就是倒排索引的一種資料結構,記得以前看過一篇文章,講在物件導向時代,資料結構的作用被減弱了,我覺得這個觀點至少在Lucene.Net上必須被拋棄。回答原先的問題,什麼是索引?縱觀搜尋引擎的發展曆史,早期的搜尋引擎都是基於關鍵字和目錄的,而現在已經轉變成全文檢索搜尋。若要問什麼是索引,那就是你有一本書,你看到了第八頁是寫一個故事,你用一張紙把故事名稱記錄下來,那就是索引。書的目錄,是的頁數,書的編號等等都可以算是索引。Lucene.Net用的就是倒排索引,那什麼是倒排索引?那就是對一段文本就行分析,按分析結果,把分析得到的關鍵字建立索引。比如“我在用Lucene.Net。”,用StandardAnalyzer分詞器索引後,就會儲存“我”,“在”,“用”,“Lucene”,“.”,“Net”,“。”這些詞,也就是說,索引是以詞為單位儲存的。同時記錄下了這些詞出現在了哪個文檔中,以及出現的位置和頻率。很像在資料庫裡做冗餘不是嗎?這些需要計算的資料都已經記錄下來,直接讀取就可以看了。從理論上說,在定義使用M種語言的情況下,出現詞的數量總是有限的。從這裡也可以看出分詞是如此重要,因為分詞讓N個字連成了一個整體,用這個整體中的任何一個字是查不到這個整體的,除非以犧牲速度為代價。以前說google水土不服,就是因為google在中文分詞上不如baidu,但是這個差距在縮小。
而另外一個問題,為什麼要索引,也就不難解答了。至於怎麼索引,這又是一個很長的故事了。
1、邏輯儲存結構
詞在倒排索引中是最小的單位,在Lucene.Net中衡量單位是Term,由N個Term構成了Filed,而又有N個Filed組成了Document,N個Document又會組成一個Segment,N個Segment會被寫到Lucene.Net的檔案系統。檔案系統留到後面再講,因為Lucene.Net自己實現了自己的檔案系統,而這個系統的最小單位由3個檔案組成,可以放到一個目錄下,也可以放到記憶體中。總的來說Lucene.Net的檔案系統可以理解為一個個的檔案,在Windows下就是一個目錄,裡麵包含了三個檔案,但是從Lucene.Net的邏輯上來說,這就是一個檔案。然後檔案裡的文本分詞N個章節,那就是Segment,每個段落又會有N個段落(Document),段落裡的每句話就是Filed,而Term就是每個字。和我們處理習慣很像不是嗎?而其中最重要的就是Term,其他都是為它排版用的。
而這個索引相對於分詞,也可以用另外一個類來衡量,那就是——Token,是不是很熟悉?Term和Token的文本是一樣的,只是記錄的關於這個文本的屬性不一樣。
前面寫到了兩次寫入索引的操作,代碼大同小異。都是先建立一個分詞器,然後把分詞器交給IndexWriter。接著建立N個Document,往Docuemnt裡填充Field,再把Document交給IndexWriter操作,就完成了整個索引過程。關於Segment的處理被黑箱子掉了,而Term的處理也僅僅能從分詞器看到個大概。
(PS:不行了,睡覺了。ZZzzzzz~~)