2011-07-07 39 views
5

短版:MongoDB的查詢使用嵌入文檔作爲關鍵

如果我有一個指數{"category": 1}和文檔{"category": {type: "memory", class: "DDR400"},我該怎麼辦這樣的查詢爲使用我的索引{"category.type": "memory"}

龍版本:

隨着MongoDB中,我想使用嵌入式文件作爲索引的關鍵。

例如,我可能有一些文件,如這(爲一個虛構的產品數據庫):

{"category": {"type": "hard-drive", "form_factor": "2.5in", "size": "500GB"}, ...} 
{"category": {"type": "hard-drive", "form_factor": "3.5in", ...}, ...} 
{"category": {"type": "memory", "class": "DDR400", ...}, ...} 

對於上面這個例子,我可能要執行的查詢,如:

{"category.type": "hard-drive"} 
{"category.type": "hard-drive", "category.form_factor": "2.5in"} 
{"category.type": "memory"} 
{"category.type": "memory", "category.class": "DDR400"} 

我的問題是創建一個索引。文檔http://www.mongodb.org/display/DOCS/Indexes#Indexes-DocumentsasKeys描述了兩個選項:

第一個選項是創建一個複合索引,例如{ "category.type": 1, "category.class": 1 }。這不適合我的情況,因爲我可能有許多不同類型的子類別。

第二個選項是使用文檔作爲關鍵字:{ "category": 1 }。現在查詢如{"category": {"type": "memory", "class": "DDR400"}}將使用該索引,但{"category": {"type": "memory"}}將不會返回任何內容,並且{"category.type": "memory"}不會使用該索引。有沒有辦法使用這個索引來進行查詢,這會得到與{"category.type": "memory"}相同的結果?

我懷疑使用像{"category" {"$gt": ..., "$lt": ...}這樣的查詢應該工作,但我應該在那裏的空白處?

回答

3

category.type創建單獨的索引(可能除了category)似乎是最好的選擇。

您可以使用範圍查詢$gt$lt。那些將工作在嵌入的對象,只適用於第一個(在存儲順序)字段的二進制表示,只有當這第一場是在所有文件中相同的,所以它不是很靈活,易折斷。

{"category" : {"$gt": {"type": "memory"}, "$lt": {"type": "memoryX" } } } 

「memoryX」在這裏作爲一個分界點:所有帶「內存」的東西都會在此之前排序。

注意,這需要「類型」字段是二進制表示的第一個爲擁有它的所有文件。它也只適用於「類型」字段(沒有辦法在第一位置等領域進行查詢,你必須選擇一人突前),從而給你通過一個專門的「category.type」指數幾乎沒有任何優勢(只是空間儲蓄)。

我之前正在嘗試這個想法,請參閱this thread on the mailing list。它確實有效,但你必須小心你在做什麼:

它既支持又穩定。許多分片/複製 內部使用嵌入式文檔的_id值。

要留意這裏的唯一的事情就是在 嵌入式元素按鍵的排序。它們按二進制表示排序,所以 {x:1,y:1}與{y:1,x:1}不同,並且排序不同。不是 只是他們排序不同,他們是不同的價值觀。一些 語言默認情況下總是對字典/散列/映射中的鍵進行排序。

再次,考慮在您需要的字段創建額外的索引。

就我而言,我只需要查詢'a','a,b'或'a,b,c'或'a,x,y',其中包含x的文檔從不包含'b'或'c'

那可能會工作。不過,我仍然會做兩個複合索引a,ba,x。或者可能只是bx。由於文檔中包含bx,你可能已經有效地過濾掉不相關的文件,對於a(form_factor = 2.5英寸已經告訴你這是一個硬盤,類= DDR400已經使得它的內存)。 經過a,b過濾後,您可能不需要索引即可在c上深入查看。

通過使用你看你依賴於什麼可以被稱爲一個實現細節的二進制表示這個棘手的查詢。你可能會受到喜歡重新排序字段的驅動程序,或者有關Mongo本身有時重新排列東西的this issue

+0

我的問題是,我可能有內部類許多不同的密鑰,我無法爲它們中的每一個創建索引。我總是從左到右查詢嵌入式文檔中的鍵(因此不只是最後一個鍵)。 – Ralf

+0

您使用$ gt和$ lt的解決方案似乎可行,但在哪些情況下可能會中斷? – Ralf

+0

您只需要在要查詢的鍵上創建索引。如果可以有多種組合,查詢「從左到右的按鍵」可能也不太適用。考慮{a:1,b:1,c:1} vs {a:1,c:1} vs {b:1,c:1}。您無法查詢a,b,c與這些文件的所有組合。 – Thilo

2

如果您正在搜索的每個「類型」,後來乾脆也加入一個基本屬性是作爲一個單獨的領域,並創建一個複合索引,如:

{"category": {"type": "hard-drive", "form_factor": "2.5in", "searchfield: "2.5in", ...}, ...} 
{"category": {"type": "memory", "class": "DDR400", searchfield: "DDR400", ...}, ...} 

如果有幾個田野上你正在尋找,但這些字段的值不同,你可以爲標記添加值,並再次創造一個複合鍵:

{"category": {"type": "hard-drive", "form_factor": "2.5in", "size": "500GB", "tags": ["2.5in", "500GB"]}, ...} 
{"category": {"type": "memory", "class": "DDR400", "tags": ["DDR400"], ...}, ...} 
+0

我喜歡這個解決方案。我甚至可以將所有內容放在標籤中:{「tags」:[「hard-drive」,「2。5in「,」500GB「]},{」tags「:[」memory「,」DDR400「]},或者在可能存在衝突的地方加上標籤」type「,例如」memory-DDR400「。如果我需要搜索多個標籤,我的問題就可以解決我的問題 – Ralf

+0

我接受了Thilo的答案,因爲它回答了我的原始問題並對其進行了解釋,但是,我最終可能會使用這個解決方案 – Ralf

+0

+1。不錯,特別是因爲你可以使用$ all。這是一個很好的例子,它可以對數據進行反規範化處理,使其對查詢友好。 – Thilo