摘要:說到檔案系統,選文檔數據庫再合適不過了。熟悉的人看這個會很眼熟,沒錯,這就是從借鑒過來,并用在我的關系數據庫查詢上。接口規范接口的主要目是為了傳遞數據,數據結構已經在上面給出。為便于不同系統不同終端的數據交換,也將應當在接口支持之內。
說到“檔案”系統,選文檔數據庫再合適不過了。談到文檔數據庫一般想到的是 MongoDB、CouchDB 之類的,可這里要說的不是這些,而是另一個 NoSQL “文檔數據庫” —— Lucene。之所以要打引號,是因為暫時還沒聽到別人這樣說。
需求
最近公司要弄一個內部搜索,對比各種方案后,決定用 Lucene。當做出第一個原型后,考慮到公司另外幾個項目將來也許用的上,而再寫一遍代碼可不是我的風格;又試用了開箱即用的 Solr,覺得那也不是我的菜。因為我項目內已經有類似 Solr 的 Schame 的配置在用了,我打算復用這個模塊;接口規范我也打算復用我現有的規范。
基礎的增刪改查比較簡單,很快就做出了原型。此時我想到公司另一個大模塊:檔案(或叫簡歷)。這部分我已計劃與另一個項目的類似模塊做整合,考慮用 MongoDB 重構。既然 Lucene 可以存取較復雜的數據結構,何不借此機會研究一下用 Lucene 作為檔案系統的底層支撐呢。
那這里說的檔案是什么樣子呢?舉一個簡單例子,一份個人簡歷:
姓名:XXX 性別:男 照片:xxx/xxx.jpg 興趣愛好 興趣:跑步、游泳、XX自定義 簡介:是浪費時間的服務吉林省地方就,受到法律書籍地方 教育經歷 經歷1 日期區間: 2014/1/1~2015/1/1 學校: Jiali.Dun 專業: 挖掘機 學位:沒士 經歷2……
大概的文檔結構就是就是這樣,字段、層級是不確定的,需要保持此結構,能存、能取,大部分字段可查詢、排序。
結構化數據
總結以上檔案結構,組成上可分為:
a. 基礎板塊(名字,性別,照片) b. 其他板塊(同上,但被區分開) c. 列表板塊(教育經歷)
上面特意將基礎信息稱為基礎“板塊”,也就是說,一般情況下一份檔案是由多個板塊組成的。也許您的檔案還會更復雜,比如興趣愛好下再分為運動、娛樂,這種劃分方式從存儲上來說與兩層設計沒什么區別,多了一個父級板塊的指向而已,但這增加了展現的復雜度。現在大家都在談“扁平化”,我所理解的扁平不僅僅是把圖標拍扁了,更是信息獲取的渠道扁平了,能一下給我看的,不要讓我點一層菜單進去又點一層;能用標簽、搜索篩選的,不要讓我點目錄樹查找。
一個板塊就是一組鍵值對,此處我們將這一組規則稱為表單。那么,列表板塊就是由多個可重復表單組成的板塊。
字段上可以有:
a. 文本 b. 數字 c. 文件 d. 日期、時間(區間) e. 單選、多選 f. 多條數據(文本、數字、日期等)
從 a~e 都是很常見的類型,文件可以轉儲到文件服務器上,這里只存 URL;日期、時間可以轉換成時間戳。而 f 是指這個字段的值可以輸入多個,通常用來記錄一些需要多條記錄東西,存儲上與多選一樣。
Lucene 原本就是一個字段可以存多個值,這太妙了。
表單及驗證
前面談到我自己有一個數據校驗模塊,對數據結構的描述如下:
表單1 字段1:類型,是否必填,是否重復,其他校驗參數 字段2…… 枚舉1 取值1:名稱 取值2……
舉一個栗子:
簡歷表單 姓名:文本,必填,不重復,最大長度100 性別:選項,必填,不重復,性別枚舉 照片:圖片,選填,可重復,類型(jpg,png) 興趣愛好:表單,選填,不重復,興趣愛好表單 教育經歷:表單,選填,可重復,教育經歷表單 性別枚舉 0:女 1:男 2:中性 興趣愛好表單 興趣:文本,必填,可重復,最大長度50 簡介:文本,選填,不重復,多行文本 教育經歷表單 日期區間:日期區間,必填,不重復 學校:文本,必填,不重復 專業:文本,必填,不重復
此表單描述上也是為了方便編輯和解析,設計成了 表單->字段 兩層結構,未使用代碼嵌套而是使用鏈接嵌套的方式。校驗器在校驗的時候,發現字段類型為表單,取出對應表單遞歸下去就行了。那這么多表單都堆積在一起,怎么解決命名空間的問題呢?我設計為每個模塊(同一應用主題)一個這樣的配置,校驗器在處理表單時如果沒給出模塊名(配置名),則取當前模塊的指定名字的表單,有則取指定模塊下的表單。
數據在校驗成功后,會將數據清理為類似以下 JSON 的結構:
{ "name": "XXX", "gender": 1, "photo": "upload/photo/xxxxxx.jpg", "hobby": { "interest": [ "ljsdfsdfsd", "sldfj2ef" ], "comment": "sjldfjsldfsdlfjsldfsdfsdfsdfsdfsdf" }, "education": [ { "date": { "begin": Date(2014/1/1), "end": Date(2015/1/1) }, "university": "lwnfdsfwe", "professional": "slwef" } ] }
輸入的數據結構與此一致,對于使用 application/x-www-form-urlencoded 格式提交的數據,可以根據"."、"["和"]"解析成上面的數據結構,就像 PHP 的請求參數解析方式。
存儲方式
OK,上面已經扯了很多了,這開始進入正題了。數據都清理好了,可是這樣一個結構的數據怎么存到 Lucene 檢索庫里呢?Lucene 可不是 MongoDB 能存儲 BSON 那樣的復雜結構呀。難道像設計關系數據庫的 ERM 一樣,建幾個索引目錄當表使,然后用外鍵做關聯,然后自己實現關聯查詢。或者,把整個數據序列化扔到一個字段里,自己寫 Filter 、Query 來實現對復雜結構的查詢?
我可不想這么費勁。
為解決這些問題,先梳理一下,Lucene 的基本字段類型有:
StringField: 基礎文本字段,可指定是否索引 StoredField: 僅存儲不索引(也就是不能搜索、查詢只能跟著文檔取出來看) TextField : 會在這上面應用分詞器,用來做全文檢索的
還有其他的 IntField,FloatField…… 可以存數字的(關鍵的是可以按數字值大小來排序),ByteField 存二進制數據等。還有,Lucene 支持一個字段存儲多個值,當只需要一個值得時候拿一個就是了,需要多個就取多個值。
現在,我可以假定默認的情況下基礎數據要能獨立索引以方便查詢的,他們用多帶帶的字段存放。其他數據可以在字段名上用一個分隔符連接板塊名和字段名。如果這些字段的字段名是不重復的(比如隨機生成的),直接用字段名即可。這樣做的好處是展現和存儲分離,當一個字段的數據從A板塊遷移到B板塊時,不用去修改過去已經存儲的數據,因為這個遷移僅僅是視覺上的遷移而已。目前我用 RDMS 實現的一套檔案系統就是這么干的。
比較麻煩的是列表板塊。
如果不需要對這部分的數據做查詢,那就直接序列化存起來。
如果需要對里面獨立的字段做搜索和排序,那就再序列化的基礎上,多加一個字段獨立存儲要索引的字段。比如添加字段 教育經歷-學校,就可以對曾就讀過某個學校的檔案做搜索了。
如果還想完成需求:查詢某個日期范圍內就讀某某學校的檔案,還是另行存儲吧。查詢時可以用外鍵關聯,查出一個再 IN 去查另一個(注:Lucene沒有IN的操作,需要聯合使用MUST和SHOULD)。可以另外作為一個檔案存在當前索引目錄內,更好的方式是獨立開個附屬目錄存儲,這樣做可以確保主數據更干凈。
完整的存儲結構為:
主要數據存儲 記錄ID 字段1:值1,值2…… 字段2…… 列表數據存儲 主記錄ID 行記錄ID 序號 字段1:值1,值2…… 字段2……
查詢規則
我有一套已經應用在 RDBMS 模型上的查詢規則,需要做的是將規則解析成 Lucene 的 Query。查詢規則如下:
{ "id": "xxx", // 等于 "star": [1, 2], // IN, Lucene 的 Must + Should "f1": { "-gt": 18, // 大于 "-le": 35 // 小于或等于 }, "f2": { "-ne": "zzz" // 不等于 }, "f3": { "-or": "zzz" // OR, 對應 Lucene 的 Should }, "f4": { "-ni": [3, 4] // NOT IN, 對應 Lucene 的 Must_Not }, "f5": { "-ai": [1, 2] // ALL IN, 對應 Lucene 的 Must }, "f6": { "-oi": [5, 6] // OR IN, 對應 Lucene 的 Should } }
用 application/x-form-urlencode 可表示為:
id=xxx&star[]=1&star[]=2&f1[-gt]=18&f1[-le]=35&f6[-oi][]=5&f6[-oi][]=6
系統會以類似 PHP 的請求參數解析方式解析類似上面 JSON 的數據結構。為了方便看和寫,也可支持將[]換成.,如:f6.-oi.=6 與 f6[-oi][]=6 是相同的。
熟悉 MongoDB 的人看這個會很眼熟,沒錯,這就是從 MongoDB 借鑒過來,并用在我的關系數據庫查詢上。這里的 -or 和 -oi 是 Lucene 特有的,可以影響到排序,這對搜索那些可有可無的字段很有幫助。-ai 類似于 Mongo 的 containsAll。
注:[2015/12/01] 以上"-"已換成"!"符號。
接口規范
接口的主要目是為了傳遞數據,數據結構已經在上面給出。接口以 REST 風格給出,請求數據支持 application/x-form-urlencode,json,返回數據為 json。
如果你熟悉 Protobuf,也許意識到了上面的表單跟 proto 的描述很像,沒錯,這也是借鑒的。只是 Protobuf 沒法加更多的描述,所以我沒去用。這里的表單配置可以轉換為 proto 描述。為便于不同系統、不同終端的數據交換,protobuf 也將(應當)在接口支持之內。
后注
如果不去考慮 Lucene 寫鎖的“問題”,我真心覺得這是個相當不錯的嵌入式文檔數據庫;雖然用 Lucene 存儲復雜結構數據的可行性還有待商榷,但折騰一下對了解 Lucene 還是有價值的。不必強求必須用什么語言、框架或工具才能完成某件事,其實能辦成一件事的途徑有很多,多嘗試一下思路就更清晰一點。
我在 github 上有個項目,不過還沒有搭建演示,日后有了再將鏈接添加到這里。
部分代碼:
Lucene CRUD 封裝:https://github.com/ihongs/Hon...
表單校驗程序:https://github.com/ihongs/Hon...
表單配置規范:https://github.com/ihongs/Hon...
參考資料:
MongoDB 查詢:http://docs.mongodb.org/manua...
Lucene 查詢:https://lucene.apache.org/cor...
REST 簡介:http://baike.baidu.com/view/5...
PHP 請求參數解析(見第一條 Note):http://php.net/manual/zh/rese...
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/20914.html
摘要:說到檔案系統,選文檔數據庫再合適不過了。熟悉的人看這個會很眼熟,沒錯,這就是從借鑒過來,并用在我的關系數據庫查詢上。接口規范接口的主要目是為了傳遞數據,數據結構已經在上面給出。為便于不同系統不同終端的數據交換,也將應當在接口支持之內。 說到檔案系統,選文檔數據庫再合適不過了。談到文檔數據庫一般想到的是 MongoDB、CouchDB 之類的,可這里要說的不是這些,而是另一個 NoSQL...
摘要:倒排索引是基于詞的搜索。關于倒排索引要學習搜索引擎,就需要了解倒排索引,要更加深刻地理解倒排索引,就要先了解什么是正排索引表。由于不是由記錄來確定屬性值,而是由屬性值來確定記錄的位置,因而稱為倒排索引。 Lucene是什么? Lucene是apache軟件基金會4 jakarta項目組的一個子項目,是一個開放源代碼的全文檢索引擎工具包,但它不是一個完整的全文檢索引擎,而是一個全文檢索引...
摘要:基本概念在深入解讀之前,先了解下的幾個基本概念,以及這幾個概念背后隱藏的一些東西。如圖是一個內的基本組成,內數據只是一個抽象表示,不代表其內部真實數據結構。即詞典,是根據條件查找的基本索引。 前言 Apache Lucene是一個開源的高性能、可擴展的信息檢索引擎,提供了強大的數據檢索能力。Lucene已經發展了很多年,其功能越來越強大,架構也越來越精細。它目前不僅僅能支持全文索引,也...
摘要:基本概念在深入解讀之前,先了解下的幾個基本概念,以及這幾個概念背后隱藏的一些東西。如圖是一個內的基本組成,內數據只是一個抽象表示,不代表其內部真實數據結構。即詞典,是根據條件查找的基本索引。 前言 Apache Lucene是一個開源的高性能、可擴展的信息檢索引擎,提供了強大的數據檢索能力。Lucene已經發展了很多年,其功能越來越強大,架構也越來越精細。它目前不僅僅能支持全文索引,也...
閱讀 2209·2021-09-02 15:11
閱讀 1517·2019-08-30 15:43
閱讀 2082·2019-08-29 13:48
閱讀 2801·2019-08-26 13:55
閱讀 2108·2019-08-23 15:09
閱讀 2905·2019-08-23 14:40
閱讀 3437·2019-08-23 14:23
閱讀 2645·2019-08-23 14:20