Reputation: 743
I have two schemas: Device and Map.
Device:
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 }
});
Map
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'}]
});
As you can see, my Map has an array that references devices and my Device has a mapId field which references the map. Now, I am trying to call .populate() so I can retrieve the map along with the array of it's devices. I am doing:
module.exports.getMapByIdAndPopulate = function(mapId, callback){
Map.findOne({_id: mapId}).populate('devices').exec(callback);
};
This returns the map but the device array is empty when it shouldn't be. Also, I create devices in my DB like this:
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]
});
How can I retrieve a map with an array of it's devices? Also, I don't know if I a understanding populate correctly but I never insert any devices into the Map array.
Upvotes: 0
Views: 203
Reputation: 609
First of all, and just to be sure you are doing it as it supposed to be, when you use this syntax
var device = new Device({});
You are only creating an object, so you have not saved it into mongo yet. To do so, just
var device = new Device({});
device.save(function(err){
//if err, wrong query
//else, everything went ok
});
Remember that .save is asynchronous.
In the other hand as you said, you did not insert any device's id reference into the devices array under map collection. I think you are misunderstanding the populate method. The populate method will look forward all the ids contained on the specified array (in this case) and will populate each element using the referenced collection in your model. So, if you do not have anything in that array, there is nothing for mongo to populate. You have to insert the devices into the map objects manually.
Upvotes: 0
Reputation: 1465
I've created a simple script to run through your schemas and query:
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);
});
});
The console output:
First console.log contains the newly saved Map with the Device's Object ID:
{ __v: 1,
mapName: 'Map 1',
mapImagePath: 'mapImagePath 1',
_id: 5771e0e5739d78441cff5424,
devices: [ 5771e0e5739d78441cff5425 ],
createdAt: Tue Jun 28 2016 02:28:53 GMT+0000 }
Second console.log is the populated .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 }
Database records show that the data is as expected - the Device stored the Map's ObjectId, the Map stored the Device's ObjectID in an array at the "devices" path.
This shows that the record is correctly populated, implying that your schema and query should be correct.
Therefore we should find out why population doesn't work on your side.
I suggest you to first check the data inside your database, verify that the "devices" array of the maps
collection contains ObjectId but not strings. Also make sure that the collections are correctly named and mapped (if not using default name)
Here are the test data of my database:
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)
}
Upvotes: 2