延迟响应直到所有查询完成

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"。我敢打赌两者都快得多。