簡體   English   中英

嵌入時如何處理MongoDB中的多對多關系不是答案?

[英]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中完全嵌入它們是不明智的。

而是將ItemCategory對象視為頂級文檔。 確保MongoDb模式為每個模式分配一個表,以便每個文檔都有自己的ObjectId

下一步是決定嵌入的位置和數量......沒有正確的答案,因為這一切都取決於你如何使用它以及你的擴展目標是什么......

嵌入決策

1.項目

至少,您的Item對象應該具有其類別的集合屬性。 至少此集合應包含每個CategoryObjectId

我的建議是添加到此集合中,您最常與Item交互時使用的數據......

例如,如果我想在網格中列出我的網頁上的一堆項目,並顯示它們所屬的類別的名稱。 很明顯,我不需要了解有關Category所有內容,但如果我只嵌入了ObjectId,則需要第二個查詢來獲取有關它的任何詳細信息。

相反,最有意義的是將Category的Name屬性與ObjectId一起嵌入集合中,以便拉回Item現在可以顯示其類別名稱而無需另一個查詢。

要記住的最重要的事情是, Item中嵌入的“代表” Category的鍵/值對象不必與真實的Category文檔模型匹配......它不是OOP或關系數據庫建模。

2.分類

反過來,您可能選擇單向嵌入,而在Category文檔中沒有任何Item信息...或者您可能選擇像上面那樣添加項目數據的集合( ObjectIdObjectId + 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM