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

时间:2016-03-10 12:38:39

标签: node.js mongodb mongoose mongodb-query node-async

我的数据库包含项目和阶段。项目可以有多个阶段。模型类似于:

阶段:

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 parallelparallel传递任何内容以等待响应,直到查找完成。

或者我可以将阶段对象传递给回调但是然后在{{1}}中我应该遍历阶段和项目以找到当前阶段的适当项目。

我是否陷入了这种设计的常见陷阱,由于某种原因,再次迭代项目和阶段会更好,或者我应采取完全不同的方法?

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;
       });

});

同样添加就像你说“项目可以有多个阶段”,所以逻辑将是一个“数组”而不是单个值的赋值。

更有效的$ lookup

另一方面,如果您有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运算符。

无论哪种方式,这都会将服务器联系减少到“一个”操作,因为当前的方法将创建大量连接并且每个都需要相当多的资源。也无需“等待所有回复”。我打赌两者的速度要快得多。