2015-04-20 87 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