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

How do I populate sub-document after $geoNear

本文关键字:填充 文档 之后 geoNear      更新时间:2023-09-26

我正在使用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
  }
});

在这里,我可以获得所有交易以及它们与当前位置的距离。在Deal模式中,我有一个商人作为参考对象。

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

事实证明这有点有趣。当然,您不想真正"深入"本机驱动程序部分,尽管您可能会在解决方案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 );

本质上,一个模型包含"地理"信息,另一个模型仅引用了我们想要填充的一些信息。还有我对数据的懒惰修改:

{ 
    "_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 );
});

这只是调用了$near运算符的标准.find()。这是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调用一样,它不会返回接受.populate()的mongoose文档或其他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()方法至少看起来干净得多,并且不会为此目的重新发明轮子。