【问题标题】:Why isn't my array populating using Mongoose为什么我的数组没有使用 Mongoose 填充
【发布时间】:2016-10-30 03:40:35
【问题描述】:

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

设备:

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

这会返回地图,但设备数组不应该是空的。另外,我在我的数据库中创建设备,如下所示:

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

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

【问题讨论】:

    标签: node.js mongodb mongoose mongoose-populate


    【解决方案1】:

    我创建了一个简单的脚本来运行您的架构和查询:

    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 包含新保存的带有设备对象 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 }
    

    数据库记录显示数据符合预期 - Device 存储了 Map 的 ObjectId,Map 将 Device 的 ObjectID 存储在“设备”路径的数组中。

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

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

    我建议您首先检查数据库中的数据,验证 maps 集合的“设备”数组是否包含 ObjectId 但不包含字符串。还要确保集合被正确命名和映射(如果不使用默认名称)

    这是我的数据库的测试数据:

    maps收藏:

    { 
        "_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收藏:

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

    【讨论】:

    • 哇,谢谢!!!问题是,我创建了一个地图,然后再创建设备。我怎样才能做到这一点?例如,您正在创建地图后立即创建设备。如何在其他时间将设备放入地图数组中?
    • 完成设备创建后,您可以1) Map.findById(mapId, "devices"),将新设备添加回devices数组和.save()更新后的地图;或2)直接执行Map.update({_id: mapId}, {$addToSet: {"devices": device._id}})
    【解决方案2】:

    首先,当你使用这个语法时,只是为了确保你在做它应该做的事情

    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 没有任何内容可以填充。您必须手动将设备插入到地图对象中。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-10-03
      • 2019-11-24
      • 2019-10-24
      • 2014-09-07
      • 2017-01-21
      • 1970-01-01
      • 2021-12-20
      • 2011-08-18
      相关资源
      最近更新 更多