2015-04-20 132 views
0

爲了能夠有效地執行聚合,我對文檔的設計有疑問。我將採取的文件虛擬例如:MongoDB - 引用字段上的聚合

{ 
    product: "Name of the product", 
    description: "A new product", 
    comments: [ObjectId(xxxxx), ObjectId(yyyy),....] 
} 

正如你可以看到,我有一個簡單的文件,它描述了產品和包裝一些意見就可以了。想象一下,這款產品非常受歡迎,因此它包含數百萬條評論。評論是一個簡單的文檔,包含日期,文本以及最終的其他功能。問題在於,這樣的產品可能容易超過16MB,所以我不需要在產品中嵌入評論,但需要單獨收集。

我現在想要做的是在產品集合上執行聚合,第一步可能是例如選擇各種產品並按日期對評論進行排序。對嵌入式文檔來說這是一個相當簡單的操作,但我怎麼能用這樣的設計呢?我只有註釋的ObjectId而不是它們的內容。當然,我希望在一次操作中執行這種聚合,即我不想執行聚合的第一部分,然後查詢結果並執行另一次聚合。

我不知道這是否足夠清楚? ^^

+0

能否請您提供一些真實的樣本數據,你希望輸出的上面可以一步完成? – Vishwas

+0

恐怕沒有。這更多是一個概念性問題,但我認爲給定的示例文檔是一個很好的例子。問題是如何在使用MongoDB進行聚合時處理引用的文檔。答案通常是嵌入引用的文檔,但如果由於大小限制而無法完成,該怎麼辦? – GuillaumeA

+0

對於您的示例,您只需在評論集合中的「產品」字段上進行選擇,然後進行排序 - 它甚至不需要聚合。如果您想獲取產品文檔和評論文檔,則需要執行應用程序級聯接。我會搜索評論,然後解決產品參考而不是其他方式。一次操作中無法從多個集合中獲取信息。 – wdberkeley

回答

0

我會這樣做:創建一個臨時集合,它是產品集合的確切副本,唯一的例外是評論數組上的模式更改,它將被修改爲包含評論對象而不是對象ID。評論對象將只有_id和日期字段。

var comments = []; 
db.product.find().forEach(function (doc){ 
    doc.comments.forEach(function(x) { 
     var obj = {"_id": x }; 
     var comment = db.comment.findOne(obj); 
     obj["date"] = comment.date; 
     comments.push(obj); 
    }); 
    doc.comments = comments; 
    db.temp.insert(doc); 
}); 

然後,您可以運行鍼對臨時收集您彙總查詢:

db.temp.aggregate([ 
    { 
     $match: { 
      // your match query 
     } 
    }, 
    { 
     $unwind: "$comments" 
    },  
    { 
     $sort: { "comments.date": 1 } // sort the pipeline by comments date 
    } 
]); 
+1

感謝您的建議。我可以得出結論是,在一個步驟中不可能這樣做。模式必須改變,或者我們必須使用適合我們想要做的聚合的臨時文件。我希望這是可能的,但我恐怕它不是:( – GuillaumeA