为什么我的数组不使用 Mongoose 填充

Why isn't my array populating using Mongoose

我有两个架构:设备和地图。

设备:

   var DeviceSchema = mongoose.Schema({
        deviceName:{
            type: String,
            required: true,
            index: true
        },
       roomCapacity: {
            type: Number
        },
        roomImagePath:{
            type: String,
        },
        mapId:{
            type: Schema.Types.ObjectId,
            ref: 'Map',
            required: true
        },
        coords:{
            type: [Number], //[xcoord, ycoord]
            required: true
        },

        createdAt: { type: Date, default: Date.now }
    });

地图

var MapSchema = mongoose.Schema({
    ownerId:{
        type: Schema.Types.ObjectId,
        ref: 'User',
        required: true
    },
    mapName: {
        type: String
    },
    mapImagePath:{
        type: String,
        required: true
    },

    createdAt: { type: Date, default: Date.now },

    devices: [{type: Schema.Types.ObjectId, ref: 'Device'}]
});

如您所见,我的地图有一个引用设备的数组,我的设备有一个引用地图的 mapId 字段。现在,我正在尝试调用 .populate() 以便我可以检索地图及其设备数组。我在做:

module.exports.getMapByIdAndPopulate = function(mapId, callback){
    Map.findOne({_id: mapId}).populate('devices').exec(callback);
};

此 returns 地图,但设备数组本不应为空。另外,我在我的数据库中创建设备是这样的:

 var device = new Device({ //create the device
        deviceName: req.body.deviceName,
        roomCapacity: req.body.roomCapacity,
        roomImagePath: roomImagePath,
        mapId: req.body.mapId,
        coords: [req.body.xcoord, req.body.ycoord]
    });

如何检索包含一系列设备的地图?另外,我不知道我的理解是否正确,但我从未将任何设备插入地图数组。

我已经创建了一个简单的脚本 运行 通过你的模式和查询:

var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var ObjectId = mongoose.Types.ObjectId;

mongoose.connect("mongodb://localhost/mytestdb");

var db = mongoose.connection;

db.on('error', console.error.bind(console, 'connection error:'));
db.once('open', function () {

    // Schemas copied from your code, condensed to reduce clutter
    var DeviceSchema = mongoose.Schema({
        deviceName: {type: String, required: true, index: true},
        roomCapacity: {type: Number},
        roomImagePath: {type: String},
        mapId: {type: Schema.Types.ObjectId, ref: 'Map', required: true},
        coords: {type: [Number], required: true},
        createdAt: {type: Date, default: Date.now}
    });

    // Omitted ownerId for simplicity sake
    var MapSchema = mongoose.Schema({
        mapName: {type: String},
        mapImagePath: {type: String, required: true},
        createdAt: {type: Date, default: Date.now},
        devices: [{type: Schema.Types.ObjectId, ref: 'Device'}]
    });

    var Device = mongoose.model('Device', DeviceSchema);
    var Map = mongoose.model('Map', MapSchema);

    // Add Map
    new Map({
        mapName: "Map 1",
        mapImagePath: "mapImagePath 1",
        createdAt: new Date(),
        devices: []
    }).save().then(function (map1) {

        // Add a Device
        return new Device({
            deviceName: "Device 1",
            roomCapacity: 123,
            roomImagePath: "roomImagePath",
            mapId: map1._id,
            coords: [345, 456]
        }).save().then(function (device1) {

            // Add the new Device back to the map
            map1.devices.push(device1); // Push the whole device, not just the _id, mongoose would handle this for us

            return map1.save();
        })


    }).then(function (saveResult) {
        console.log(saveResult);

        // mapId is now an ObjectId
        var mapId = saveResult._id;

        return Map.findOne({_id: mapId}).populate('devices').exec().then(function (foundMap) {

            console.log(foundMap);

        });

    }).catch(function (err) {
        console.error(err);
    });


});

控制台输出:

第一个 console.log 包含新保存的地图以及设备的 Object ID:

{ __v: 1,
  mapName: 'Map 1',
  mapImagePath: 'mapImagePath 1',
  _id: 5771e0e5739d78441cff5424,
  devices: [ 5771e0e5739d78441cff5425 ],
  createdAt: Tue Jun 28 2016 02:28:53 GMT+0000 }

第二个 console.log 是填充的 .findOne():

{ _id: 5771e0e5739d78441cff5424,
  mapName: 'Map 1',
  mapImagePath: 'mapImagePath 1',
  __v: 1,
  devices:
   [ { _id: 5771e0e5739d78441cff5425,
       deviceName: 'Device 1',
       roomCapacity: 123,
       roomImagePath: 'roomImagePath',
       mapId: 5771e0e5739d78441cff5424,
       __v: 0,
       createdAt: Tue Jun 28 2016 02:28:53 GMT+0000,
       coords: [Object] } ],
  createdAt: Tue Jun 28 2016 02:28:53 GMT+0000 }

数据库记录显示数据符合预期 - 设备存储了地图的 ObjectId,地图将设备的 ObjectID 存储在数组中的 "devices" 路径中。

这表明记录已正确填充,暗示您的架构和查询应该是正确的。

因此我们应该找出为什么人口对你不起作用。

我建议你先检查你的数据库里面的数据,验证mapscollection的"devices"数组包含ObjectId而不是字符串。还要确保 collection 已正确命名和映射(如果不使用默认名称)

下面是我的数据库的测试数据:

maps collection:

{ 
    "_id" : ObjectId("5771e0e5739d78441cff5424"), 
    "mapName" : "Map 1", 
    "mapImagePath" : "mapImagePath 1", 
    "devices" : [
        ObjectId("5771e0e5739d78441cff5425")
    ], 
    "createdAt" : ISODate("2016-06-28T02:28:53.091+0000"), 
    "__v" : NumberInt(1)
}

devices collection:

{ 
    "_id" : ObjectId("5771e0e5739d78441cff5425"), 
    "deviceName" : "Device 1", 
    "roomCapacity" : NumberInt(123), 
    "roomImagePath" : "roomImagePath", 
    "mapId" : ObjectId("5771e0e5739d78441cff5424"), 
    "createdAt" : ISODate("2016-06-28T02:28:53.181+0000"), 
    "coords" : [
        NumberInt(345), 
        NumberInt(456)
    ], 
    "__v" : NumberInt(0)
}

首先,当您使用此语法时,只是为了确保您按照预期的方式进行操作

var device = new Device({});

您只是在创建一个对象,因此您还没有将它保存到 mongo。为此,只需

var device = new Device({});
device.save(function(err){
    //if err, wrong query
    //else, everything went ok
});

记住 .save 是异步的。

另一方面,正如您所说,您没有将任何设备的 id 引用插入到地图集合下的设备数组中。我认为您误解了填充方法。 populate 方法将查找指定数组中包含的所有 id(在本例中),并将使用模型中引用的集合填充每个元素。因此,如果该数组中没有任何内容,则 mongo 没有任何内容可填充。您必须手动将设备插入地图对象。