我的数据库包含项目和阶段。项目可以有多个阶段。模型类似于:
阶段:
var phaseSchema = new mongoose.Schema({
project: { type: mongoose.Schema.Types.ObjectId, ref: 'Project' }
});
项目:
var projectSchema = new mongoose.Schema({
name : { type: String }
});
目前我正在使用以下方法检索每个项目的阶段:
var calls = [];
var projects = _.each(projects, function (p) {
calls.push(function (callback) {
req.app.db.models.Phase.find({ project: p._id }, function (err, doc) {
if (err) {
callback(err);
} else {
p.phases = doc;
callback();
}
});
})
});
async.parallel(calls, function (err) {
workflow.outcome.projects = projects;
return workflow.emit('response');
});
正如您所看到的那样,我没有通过callback()
使用async's parallel
向parallel
传递任何内容以等待响应,直到查找完成。
或者我可以将阶段对象传递给回调但是然后在{{1}}中我应该遍历阶段和项目以找到当前阶段的适当项目。
我是否陷入了这种设计的常见陷阱,由于某种原因,再次迭代项目和阶段会更好,或者我应采取完全不同的方法?
答案 0 :(得分:1)
我实际上认为在这种情况下,您最好运行一个查询以匹配所有可能的结果。对于“test”查询,您可以将所有_id
值作为$in
子句发出,然后只需对结果进行一些匹配即可分配匹配(ed)文档:
// Make a hash from the source for ease of matching
var pHash = {};
_.each(projects,function(p) {
pHash[p._id.toString()] = p;
});
// Run the find with $in
req.app.db.models.Phase.find({ "project": { "$in": _.keys(pHash) } },function(err,response) {
_.each(response,function(r) {
// Assign phases array if not already there
if (!phash[r.project.toString()].hasOwnProperty("phases")
pHash[r.project.toString()].phases = [];
// Append to array of phases
pHash[r.project.toString()].phases.push(r)
});
// Now return the altered hash as orginal array
projects = _.mapObject(pHash,function(val,key) {
return val;
});
});
同样添加就像你说“项目可以有多个阶段”,所以逻辑将是一个“数组”而不是单个值的赋值。
另一方面,如果您有MongoDB 3.2可用,那么$lookup
聚合管道运算符似乎适合您。在这种情况下,您只需使用Projects
模型,但在“阶段”集合上执行$lookup
。 “集合”是这里的操作术语,因为它是服务器端操作,因此只知道集合而不是应用程序“模型”:
// BTW all models are permanently registered with mongoose
mongoose.model("Project").aggregate(
[
// Whatever your match conditions were for getting the project list
{ "$match": { .. } },
// This actually does the "join" (but really a "lookup")
{ "$lookup": {
"from": "phases",
"localField": "_id",
"foreignField": "project",
"as": "phases"
}}
],function(err,projects) {
// Now all projects have an array containing any matched phase
// or an empty array. Just like a "left join"
})
);
这是处理此问题的最有效方法,因为所有工作都在服务器上完成。
所以你在这里要问的基本上是.populate()
的“反向情况”,而不是将“阶段”作为对“项目”对象的引用,而是将项目的引用列在“阶段”。
在这种情况下,任何形式的“查找”应该是您正在寻找的。您可以通过$in
和“映射”阶段模拟加入的位置,也可以直接使用聚合框架$lookup
运算符。
无论哪种方式,这都会将服务器联系减少到“一个”操作,因为当前的方法将创建大量连接并且每个都需要相当多的资源。也无需“等待所有回复”。我打赌两者的速度要快得多。