Reputation: 31
I am new to NodeJS(Express). I am trying to build an API that will list the "Rooms" of a Hotel, and "Photos" for each room.
What I am trying to do is: - get a list of all rooms by a hotel - loop through this list of rooms and add photos - return JSON with rooms and photos
My problem is that I can not seem to attach the "photos" result to the "rooms result.
async.waterfall([
// Function 1
function(callback) {
// Find all Hotel Rooms
Room.find({ 'hotel': req.params.hotel_id})
.exec(function(err, rooms) {
callback(null, rooms); // Send rooms to function 2
});
},
// Function 2
function(rooms, callback) {
// Loop through each room to get photos for each room
rooms.forEach(function(room) {
// Get photos for room
RoomPhoto.find({ 'room': room._id})
.exec(function(err, photos) {
// I want to add these "photos" to the main "rooms" object, but attached to each individual "room"
room.photos = JSON.stringify(photos);
});
})
console.log(rooms)
callback(null, rooms);
}
], function(err, results) {
if(err) console.log('err');
res.json(results);
});
Any help would be greatly appreciated, as I have been hours trying to figure it out.
Thanks
Ray
I have updated my script to this, but the "photos" are still not going into the final "rooms" object. I have included the code and also the console output.
Code:
exports.index = function(req, res, next) {
Room.find({ 'hotel': req.params.hotel_id})
.exec(function(err, rooms) {
async.each(rooms, function(room, room_cb) {
console.log('Room Name: ' + room.name);
RoomPhoto.find({ 'room': room._id})
.exec(function(err, photos) {
console.log('Photos: ' + photos);
room.photos = photos;
// photos should now be in the main "rooms" object right??
// I can console.log the photos here to prove they exists
room_cb();
});
}, function(err) {
if(err) {
console.log('Error: ' + err)
}
else {
// But the photos are not in the final "rooms" object
console.log('FINAL ROOMS OBJECT, why are the photos not here??');
console.log(rooms);
res.json(rooms);
}
})
});
};
Here is the console output for the above script:
Room Name: Test Room 1
Room Name: Test Room 2
Photos:
{ _id: 5acd3094e0026f38ca4b44bc, src: 'https://picsum.photos/200/300/?image=252', room: 5acd3094e0026f38ca4b44bb, __v: 0 }
Photos:
{ _id: 5acd30c8cec87777eefcd32d, src: 'https://picsum.photos/200/300/?image=252', room: 5acd30c8cec87777eefcd32c, __v: 0 }
FINAL ROOMS OBJECT
[ { _id: 5acd3094e0026f38ca4b44bb, name: 'Test Room 1', hotel: 5accd4e1734d1d55c3195c84, __v: 0 }, { _id: 5acd30c8cec87777eefcd32c, name: 'Test Room 2', hotel: 5accd4e1734d1d55c3195c84, __v: 0 } ]
Upvotes: 3
Views: 336
Reputation: 1789
Since you are new to node.js
you may not be aware of the latest standards, and things you can use.. Here's how you can accomplish it without including async
module at all...
const requestHandler = async (req, res, next) => {
let rooms = await Room.find({ 'hotel': req.params.hotel_id})
.exec()
.catch(err => {
console.log(err);
throw err;
});
// Get photos for room
let photos = await Promise.all(rooms.map(
room => RoomPhoto.find({ 'room': room._id})
.catch(err => {
console.log(err);
throw err;
});
))
rooms = rooms.map((room, index) => {
room = room.toObject();
room.photos = JSON.stringify(photos[index]);
return room;
});
// Now your "rooms" array will have room objects, with photos populated.
res.send(rooms);
});
To export it you can do it like this:
module.exports.index = requestHandler;
Upvotes: 0
Reputation: 31
I found something that works... For some reason, the MongoDB "_id" was causing a problem.
exports.index = function(req, res, next) {
Room.find({ 'hotel': req.params.hotel_id})
.exec(function(err, rooms) {
rooms = JSON.parse(JSON.stringify(rooms).replace(/_id/g,"room_id"));
console.log(rooms);
console.log('---------------------------');
async.each(rooms, function(room, room_cb) {
RoomPhoto.find({ 'room': room.room_id})
.exec(function(err, photos) {
console.log('Room: ' + room.id);
console.log('Photos: ' + photos);
room.photos = photos;
//console.log('Photos: ' + rooms[key].photos);
room_cb();
});
}, function(err) {
if(err) {
console.log('Error: ' + err)
}
else {
console.log('FINAL ROOMS OBJECT');
rooms = JSON.parse(JSON.stringify(rooms).replace(/room_id/g,"_id"));
console.log(rooms);
res.json(rooms);
}
})
});
};
Upvotes: 0
Reputation: 29172
Since the function RoomPhoto
is also asynchronous, then you need to wait until all the photos are loaded before calling the callback function. Like that:
// Function 2
function(rooms, callback) {
// https://caolan.github.io/async/docs.html#each
// Loop through each room to get photos for each room
async.each(rooms, function(room, room_cb) {
// Get photos for room
RoomPhoto.find({ 'room': room._id})
.exec(function(err, photos) {
room.photos = JSON.stringify(photos)
room_cb()
});
}, function (err) {
console.log(rooms)
callback(null, rooms)
})
}
Upvotes: 4
Reputation: 354
Try placing your second callback(null, rooms);
line (the one near the end) under the room.photos = JSON.stringify(photos);
line.
Because of the asynchronous nature of JavaScript, it is probably running before your query has even finished getting the photos.
You might also want to add some checks in case there are no photos for a room.
Upvotes: -1