[英]How to deal with Many-to-Many relations in MongoDB when Embedding is not the answer?
這是交易。 假設我們在MongoDB中有以下數據模式:
items
:包含大量文檔的集合,其中包含一些數據(與實際情況完全無關)。 item_groups
:包含items._id
文件的文檔集合,名為item_groups.items
加上一些額外數據。 所以,這兩者是以多對多的關系聯系在一起的。 但是有一個棘手的問題:由於某種原因我不能在項目組中存儲項目,所以 - 正如標題所說 - 嵌入不是答案。
我真正擔心的問題是為了找到一些包含某些特定項目的特定組(即我為每個集合設置了一組標准)。 事實上,它還必須說明每個找到的組中的項目符合標准(沒有項目意味着沒有找到組)。
我提出的唯一可行解決方案是使用具有虛擬縮減功能的Map / Reduce方法:
function map () {
// imagine that item_criteria came from the scope.
// it's a mongodb query object.
item_criteria._id = {$in: this.items};
var group_size = db.items.count(item_criteria);
// this group holds no relevant items, skip it
if (group_size == 0) return;
var key = this._id.str;
var value = {size: group_size, ...};
emit(key, value);
}
function reduce (key, values) {
// since the map function emits each group just once,
// values will always be a list with length=1
return values[0];
}
db.runCommand({
mapreduce: item_groups,
map: map,
reduce: reduce,
query: item_groups_criteria,
scope: {item_criteria: item_criteria},
});
問題在於:
item_criteria._id = {$in: this.items};
如果this.items.length == 5000甚至更多怎么辦? 我的RDBMS背景大聲呼喊:
SELECT ... FROM ... WHERE whatever_id IN (over 9000 comma-separated IDs)
絕對不是一個好方法 。
伙計們,謝謝你們的時間!
我希望最好的答案將是“你是愚蠢的,停止思考RDBMS風格,使用最新版本的MongoDB中的$ its_a_kind_of_magicSphere ”:)
我認為你正在努力將域/對象建模與數據庫模式建模分離。 在嘗試使用MongoDb時,我也很掙扎。
為了語義和清晰度,我將使用單詞Categories
替換Groups
基本上你的理論模型是“多對多”關系,因為每個Item
都屬於Categories
,每個Category
可以擁有許多Items
。
這最好在域對象建模中處理,而不是在DB模式中處理,尤其是在實現文檔數據庫(NoSQL)時。 在您的MongoDb架構中,您通過使用頂級文檔模型和嵌入的組合來“偽造”“多對多”關系。
對於來自SQL持久性后端的人來說嵌入很難接受,但它是答案的重要部分。 訣竅是決定它是淺或深,單向還是雙向等。
頂級文檔模型
因為您的Category
文檔包含他們自己的一些數據並且被大量Items
大量引用,所以我同意您在每個Item
中完全嵌入它們是不明智的。
而是將Item
和Category
對象視為頂級文檔。 確保MongoDb模式為每個模式分配一個表,以便每個文檔都有自己的ObjectId
。
下一步是決定嵌入的位置和數量......沒有正確的答案,因為這一切都取決於你如何使用它以及你的擴展目標是什么......
嵌入決策
1.項目
至少,您的Item
對象應該具有其類別的集合屬性。 至少此集合應包含每個Category
的ObjectId
。
我的建議是添加到此集合中,您最常與Item
交互時使用的數據......
例如,如果我想在網格中列出我的網頁上的一堆項目,並顯示它們所屬的類別的名稱。 很明顯,我不需要了解有關Category
所有內容,但如果我只嵌入了ObjectId,則需要第二個查詢來獲取有關它的任何詳細信息。
相反,最有意義的是將Category的Name
屬性與ObjectId
一起嵌入集合中,以便拉回Item
現在可以顯示其類別名稱而無需另一個查詢。
要記住的最重要的事情是, Item
中嵌入的“代表” Category
的鍵/值對象不必與真實的Category
文檔模型匹配......它不是OOP或關系數據庫建模。
2.分類
反過來,您可能選擇單向嵌入,而在Category
文檔中沒有任何Item
信息...或者您可能選擇像上面那樣添加項目數據的集合( ObjectId
或ObjectId
+ Name
)...
在這個方向上,我個人傾向於沒有嵌入任何東西...更有可能的是,如果我想要我的類別的Item
信息,我想要很多,不僅僅是一個名稱...並深入嵌入頂級文檔(項目)毫無意義。 我只想讓自己在數據庫中查詢一個Items集合,其中每個集合都在其Categories類集合中擁有了我的Category的ObjectId。
Phew ......確實令人困惑。 問題的關鍵是,你將有一些數據的重復,你將不得不將模型調整您的使用以獲得最佳性能。 好消息是,這就是MongoDb和其他文檔數據庫擅長的......
為什么不使用相反的設計?
您正在存儲項目和item_groups。 如果你的第一個想法是在item_group條目中存儲項目,那么可能相反並不是一個壞主意:-)
讓我解釋:
在每個項目中,您存儲它所屬的組。 (您在NOSql中,數據復制正常!)例如,假設您在項目條目中存儲了一個名為groups的列表,您的項目如下所示:{_ id:....,name:....,groups:[ ObjectId(...),ObjectId(...),ObjectId(...)]}
那么map reduce的想法需要很多力量:
map = function() {
this.groups.forEach( function(groupKey) {
emit(groupKey, new Array(this))
}
}
reduce = function(key,values) {
return Array.concat(values);
}
db.runCommand({
mapreduce : items,
map : map,
reduce : reduce,
query : {_id : {$in : [...,....,.....] }}//put here you item ids
})
您可以添加一些參數(例如,最終確定以修改map reduce的輸出),但這可能會對您有所幫助。
當然,你需要有另一個集合來存儲item_groups的詳細信息,如果你需要它,但在某些情況下(如果這個關於item_groups的信息不存在,或者沒有改變,或者你不在乎你不喜歡沒有它的最新版本)你根本不需要它們!
這會給你一個關於問題解決方案的暗示嗎?
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.