如何在$ geoNear之后填充子文档

时间:2014-06-13 03:29:18

标签: javascript node.js mongodb mongoose

我正在使用NodeJs和Mongoose,并建立一个功能来列出附近的交易。

   Deal.db.db.command({
    "geoNear": Deal.collection.name,
    "near": [23,67],
    "spherical": true,
    "distanceField": "dis"
   }, function (err, documents) {
    if (err) {
      return next(err);
    }
    console.log(documents);
  });

交易架构:

var dealSchema = new mongoose.Schema({
  title: String,
  merchant: {
    type: mongoose.Schema.Types.ObjectId,
    ref: Merchant,
    index: true
  }
});

在这里,我获得所有优惠及其与当前位置的距离。 Inside Deal架构我有一个商家作为参考对象。

如何使用每个返回的Deal对象填充商家? 我是否需要遍历所有返回的Deal对象并手动填充?

1 个答案:

答案 0 :(得分:3)

这实际上有点有趣。当然你不想真正做的是“深入”本机驱动程序部分,尽管你可能在解决方案2上。这导致有几种方法可以解决这个问题,而无需实际滚动自己的查询来手动匹配

首先进行一点设置。我没有复制你的数据集,而是选择了我之前测试过的东西。原则是相同的,所以基本设置:

var mongoose = require('mongoose'),
    async = require('async'),
    Schema = mongoose.Schema;

mongoose.connect('mongodb://localhost');

var infoSchema = new Schema({
  "description": String
});

var shapeSchema = new Schema({
  "_id": String,
  "amenity": String,
  "shape": {
    "type": { "type": String },
    "coordinates": []
  },
  "info": { "type": Schema.Types.ObjectId, "ref": "Info" }
});

var Shape = mongoose.model( "Shape", shapeSchema );
var Info = mongoose.model( "Info", infoSchema );

基本上是一个带有“geo”信息的模型,另一个带有我们想要填充的一些信息。还有我懒惰的数据更改:

{ 
    "_id" : "P1", 
    "amenity" : "restaurant", 
    "shape" : { "type" : "Point", "coordinates" : [ 2, 2 ] }
}
{ 
    "_id" : "P3",
    "amenity" : "police",
    "shape" : { "type" : "Point", "coordinates" : [ 4, 2 ] }
}
{ 
    "_id" : "P4",
    "amenity" : "police",
    "shape" : { "type" : "Point", "coordinates" : [ 4, 4 ] }
}
{ 
    "_id" : "P2", 
    "amenity" : "restaurant",
    "shape" : { "type" : "Point", "coordinates" : [ 2, 4 ] },
    "info" : ObjectId("539b90543249ff8d18e863fb")
}

因此,有一件事我们可以期待填充。事实证明, $near 查询非常简单,按预期排序:

var query = Shape.find(
  {
    "shape": {
      "$near": {
        "$geometry": {
          "type": "Point",
          "coordinates": [ 2, 4 ]
        }
      }
    }
  }
);

query.populate("info").exec(function(err,shapes) {
    if (err) throw err;
    console.log( shapes );
});

这只是标准的.find(),并且调用了 $near 运算符。这是MongoDB 2.6语法,所以就是这样。但结果排序正确并按预期填充:

[ 
    { _id: 'P2',
      amenity: 'restaurant',
      info:
      { _id: 539b90543249ff8d18e863fb,
        description: 'Jamies Restaurant',
        __v: 0 },
      shape: { type: 'Point', coordinates: [ 2, 4 ] } },
    { _id: 'P4',
       amenity: 'police',
       info: null,
       shape: { type: 'Point', coordinates: [ 4, 4 ] } },
    { _id: 'P1',
      amenity: 'restaurant',
      info: null,
      shape: { type: 'Point', coordinates: [ 2, 2 ] } },
    { _id: 'P3',
      amenity: 'police',
      info: null,
      shape: { type: 'Point', coordinates: [ 4, 2 ] } }
]

这非常好,并且是一种简单的调用方式。抓到了吗?遗憾的是,将运算符更改为 $geoNear 以将球形几何体考虑在内会开始抛出错误。所以,如果你想要那么你就不能像过去那样做事。

另一种方法是mongoose具有支持的.geoNear()功能。但就像db.command调用一样,这不会返回一个mongoose文档或其他接受.populate()的Model类型对象。解?只需稍微播放一下输出:

var query = Shape.geoNear({
  "type": "Point",
  "coordinates": [ 2, 4 ]
},{spherical: true},function(err,shapes) {
  if (err) throw err;

  shapes = shapes.map(function(x) {
    delete x.dis;
    var a = new Shape( x.obj );

    return a;
  });
  Shape.populate( shapes, { path: "info" }, function(err,docs) {
    if (err) throw err;

    console.log( docs );

  });

所以这里返回的结果是一个原始对象数组。但是通过一些操作,您可以将这些转换为可以使用.populate()方法的方法,该方法也可以从模型类中调用,如图所示。

结果当然是相同的,尽管字段顺序可能略有不同。而且您不需要自己迭代调用查询。这就是.populate()实际上正在做的所有事情,但我认为我们可以同意使用.populate()方法至少看起来更清洁,并且不会为此目的重新发明轮子。