Mongo返回undefined但数据存在

时间:2016-03-02 20:32:38

标签: javascript node.js mongodb mongoose

我有以下代码。使用$ geoNear,我找到最接近给定gps坐标的中转站点。正如你们可能知道的那样,$ geoNear只返回最近点的位置(loc)。要获取最近的停靠点的详细信息,我使用位置查询STOPS集合。

问题是,它随机返回undefined。当我从mongo shell查询停止集合时,使用agency_key和loc参数确认数据存在。我怀疑这个问题可能是由异步引起的,但我无法找出原因。 我试图简化我的问题 ' 在forEach循环中调用异步函数'但到目前为止我什么都没有。

为什么它返回undefined?

 ...
    TmStop.aggregate([{
              $geoNear: {
                near: [lon, lat],
                maxDistance: radiusInDegrees,
                includeLocs: "distance.location",
                distanceField: "distance.calculated",
                query: {
                  agency_key: {
                    $in: agencyKeys
                  }
                }
              }
            }, {
              $project: {
                route_id: 1,
                route_type: 1,
                direction_id: 1,
                "distance.calculated": 1,
                "distance.location": 1
              }
            }])
            .exec(function(e, results) {
          if (e) {
            console.log('e->', e.errmsg);
            var res = "Something went wrong with the database: " + e;
            cb(e, res);
          } else if (!e) {
            if (results.length) {
              console.log('results->', results.length);
              var i = 0;
              results.forEach(function(result, index) {
                console.log(index, result);
                Stop.find({
                    agency_key: {
                      $in: agencyKeys
                    },
                    loc: result.distance.location
                  })
                  .exec(function(e, stop) {
                    if (e) {
                      throw new Error('Error getting stop due to:' + e);
                    }

                    var obj = {};
                    obj.route_id = result.route_id;
                    obj.route_type = result.route_type;
                    obj.direction_id = result.direction_id;
                    obj.distance = result.distance.calculated;
                    obj.locationUsed = result.distance.location;

                    // console.log('### ', index, ' ####');
                    // console.log('@Stop.find agencyKeys-> ', agencyKeys);
                    // console.log('@Stop.find loc->', result.distance.location);
                    // console.log(stop[0]);

                    obj.stop = stop[0];
                    objArr.push(obj);
                    i++;
                    if (i === results.length) {
                      cb(e, objArr);
                    }
                  });
              }); //end of forEach
            } else {
              cb(e, []);
            }
          }
        });

1 个答案:

答案 0 :(得分:1)

当然。你有一个.forEach()循环调用异步函数而不等待任何(或更重要的是"所有"调用)完成。所以你的循环迭代需要尊重被调用的内部函数的完成调用。

一个很好的工具就是async库,出于安全考虑,我们将调用async.mapLimit(因为你发出了一个数组),这将允许指定数量的操作同时运行,而不是直到堆栈限制(或者可能超出不受控制的.forEach()for循环)。

因此,在聚合操作的回调中,将代码清单改为:

        // If aggregation query returned anything
        if (results.length) {
          console.log('results->', results.length);

          async.mapLimit(results,10,function(result,callback) {
              Stop.findOne({ 
                  "agency_key": { "$in": agency_keys },
                  "loc": result.distance.location
              },function(e,stop) {
                  result.stop = stop;      // remember that result is already a plain object
                  callback(e,result);
              });
          },cb)               // cb is automatically passed e and results
        } else {
            cb(e,results);    // then the array was empty
        }

有一些改进,最值得注意的是,当您处理从常规.find()操作返回的mongoose文档时,您可能已经注意到您无法向它们添加新属性,因为它们是mongoosse文档(一个更复杂的对象)而不是普通对象。他们只是伪装成序列化和辅助访问器方法之一。

.aggregate()的结果实际上只是一个普通对象"。因此,不需要将对象属性复制到新对象只是为了分配新属性。为了将来的参考,您不需要为#34; Mongoose文档"与您在商家信息中一样明确,但只需在文档上调用.toObject()即可。结果返回一个"普通对象"。但是在这里需要的代码中,它只是一个简单的任务。

还有.findOne()而非.find()的使用。在您的代码中注意到,您只是在寻找单数匹配并引用结果的第一个元素。因此只需要一个结果。查询本身也可能无论如何都会返回单个结果,因为来自" near"没有其他查询参数,但我真的没有掌握这些信息。

最后当然还有.map()或更确切地说.mapLimit(),您的所有意图是简单地从{{1}向results数组的每个元素添加一个新属性。然后使用additional属性返回该元素。每次迭代都将使用修改后的数据发出提供的Stop,然后在完成所有迭代时调用最终回调,该回调将包含返回的所有元素的发射数组。这基本上是callback的内置参数,所以它只是以该形式提供,而不是包装在另一个函数中。