延迟响应直到所有查询完成
Delay response until all queries finished
我的数据库包含项目和阶段。项目可以有多个阶段。这些模型类似于:
相位:
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()
只是 (ab) 使用 async's parallel
等待响应直到查找完成。
或者我可以将阶段对象传递给回调,但是在 parallel
我应该遍历阶段和项目以找到适合当前阶段的项目。
我是否陷入了这种设计的常见陷阱,出于某种原因,最好再次迭代项目和阶段,或者我应该采取完全不同的方法?
我实际上认为在这种情况下,您最好 运行 一个查询来匹配所有可能的结果。对于 "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;
});
});
也像你说的那样添加 "projects can have multiple phases",所以逻辑将是 "array" 而不是单个值的赋值。
更高效的 $lookup
另一方面,如果您有 MongoDB 3.2 可用,那么 $lookup
聚合管道运算符似乎适合您。在这种情况下,您将只使用 Projects
模型,但在 `"phases" 集合 上执行 $lookup
。 "collection" 是这里的操作术语,因为它是服务器端操作,因此只知道集合而不是应用程序 "models":
// 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()
的 "reverse case" 而不是将 "phases" 作为对 "project" 对象的引用该项目改为在 "phase".
中列出
在这种情况下,"lookup" 中的任何一种形式都应该是您要查找的内容。您可以通过 $in
和 "mapping" 阶段模拟连接,或者直接使用聚合框架 $lookup
运算符。
无论哪种方式,这都会将服务器联系减少到 "one" 操作,因为您当前的方法将创建大量连接,每个连接都会占用大量资源。也不需要"Wait for all responses"。我敢打赌两者都快得多。
我的数据库包含项目和阶段。项目可以有多个阶段。这些模型类似于:
相位:
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()
只是 (ab) 使用 async's parallel
等待响应直到查找完成。
或者我可以将阶段对象传递给回调,但是在 parallel
我应该遍历阶段和项目以找到适合当前阶段的项目。
我是否陷入了这种设计的常见陷阱,出于某种原因,最好再次迭代项目和阶段,或者我应该采取完全不同的方法?
我实际上认为在这种情况下,您最好 运行 一个查询来匹配所有可能的结果。对于 "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;
});
});
也像你说的那样添加 "projects can have multiple phases",所以逻辑将是 "array" 而不是单个值的赋值。
更高效的 $lookup
另一方面,如果您有 MongoDB 3.2 可用,那么 $lookup
聚合管道运算符似乎适合您。在这种情况下,您将只使用 Projects
模型,但在 `"phases" 集合 上执行 $lookup
。 "collection" 是这里的操作术语,因为它是服务器端操作,因此只知道集合而不是应用程序 "models":
// 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()
的 "reverse case" 而不是将 "phases" 作为对 "project" 对象的引用该项目改为在 "phase".
在这种情况下,"lookup" 中的任何一种形式都应该是您要查找的内容。您可以通过 $in
和 "mapping" 阶段模拟连接,或者直接使用聚合框架 $lookup
运算符。
无论哪种方式,这都会将服务器联系减少到 "one" 操作,因为您当前的方法将创建大量连接,每个连接都会占用大量资源。也不需要"Wait for all responses"。我敢打赌两者都快得多。