I'm using async each to iterate over objects and perform a query to populate their children. Each site has locations which have floors which have areas. My sites and locations populate just fine, however, that is where it stops. Sails outputs that it is looking for floors and areas though but they are never assigned. Any ideas?
gg: function (req, res) {
var userID = req.param('id');
User.findOne({ id: userID }).populate('sites').exec(function afterFind(err, foundUser) {
if (err) return res.json({ status: 'failure', message: 'Server error' });
if (foundUser === undefined) return res.json({ status: 'failure', message: 'User was not found' });
var resultToJson = foundUser.toJSON();
var sites = resultToJson.sites;
async.each(sites, function (site, siteCb) {
sails.log.debug('Finding locations for ' + site.name);
Locations.find({ site: site.id }).exec(function afterFind(err, foundLocations) {
if (err) {
siteCb(err);
} else {
site['locations'] = foundLocations;
async.each(site['locations'], function (location, locCb) {
sails.log.debug('Finding floors for ' + location.name);
Floor.find({ location: location.id }).exec(function afterFind(err, foundFloors) {
if (err) {
locCb(err);
} else {
location['floors'] = foundFloors;
async.each(location['floors'], function (floor, floorCb) {
sails.log.debug('Finding areas for ' + floor.name);
Area.find({ floor: floor.id }).exec(function afterFind(err, foundAreas) {
if (err) {
floorCb(err);
} else {
floor['areas'] = foundAreas;
floorCb();
}
});
}, function (floorError) {
if (floorError) {
locCb(floorError);
}
else {
locCb();
}
});
}
});
}, function (locError) {
if (locError) {
siteCb(locError);
} else {
siteCb();
}
});
}
});
}, function (siteError) {
if (siteError) {
sails.log.debug(siteError);
return res.json({ status: 'failure', message: 'Server error' });
} else {
return res.json({ status: 'success', message: 'Sites for user retrieved', sites: sites });
}
});
});
}
This code should be fixed with use of toJSON() on foundLocations. Anytime when you're overriding a defined attribute with populated one (or something else) it will not work when using a ORM returned object, use toJSON() or something and assign to plain JS object.
Ref: https://stackoverflow.com/a/43500017/1435132
Also, any reason to not use populate with Locations.find?
Related
I have this Schema here
Consider the likedTours which is an Array of Objects (Tours) (ignore position 0).
I want to pull any Objects where the _id of a Tour matches the critiria.
Adding a new Tour upon liking a tour is okay, but on unlike I don't know how to pull that item out.
Here is my function in the Controller in the Node.JS backend
const unlikeTour = async (req, res) => {
try {
TourDB.Tour.findOneAndUpdate(
{ _id: req.params.tourid },
{
$pull: { likedUsers: req.userID },
$inc: { likes: -1 },
}
).exec(async (err, docs) => {
if (!err) {
try {
await UserDB.User.findOneAndUpdate(
{ _id: req.userID },
{ $pull: { 'likedTours._id': docs._id } } //Here I need help
).exec()
return res.status(200).send({ successMessage: 'Tour successfully unliked' })
} catch (err) {
return res.status(500).send({ errorMessage: 'User not found' })
}
} else {
return res.status(500).send({ errorMessage: 'Tour not found' })
}
})
} catch (err) {
return res.status(500).send({ errorMessage: err })
}
}
This method looks for a tour and update it by pulling out the userID and decrement the likes count by -1.
And then I try to find in the UserDB that tour in the likedTours and tried to pull but it doesn't not work like that.
Thanks in advance
you can update as
await UserDB.User.findOneAndUpdate(
{ _id: req.userID },
{ $pull: { likedTours: { _id: docs._id } } } //Here I need help
).exec();
reference: https://docs.mongodb.com/manual/reference/operator/update/pull/
I have created a function that is looking for any reservation document with matching _id, if exists is deleted, and next the Book(findById) function is performed which is looking for any books using function(findOneAndUpdate), if exists is added value to quantity +1.But i don't know why but the function does not want to be done.
router.post('/cancel-reservation', (req, res) => {
var reservation = req.body.reservation;
Reservation.findByIdAndRemove(reservation._id, function (err) {
if (err) throw err;
else {
Book.findById(reservation.idBook, (err, book) => {
if (err) throw err;
if (book == null) {
//////////// //*WORKING *////////////////
let bookObj = {
name: reservation.nameBook,
description: reservation.description,
publishingHouse: reservation.publishingHouse,
quantity: 1,
year: reservation.year,
sites: reservation.sites
};
var book = new Book(bookObj);
book.save()
.then(function (book) {
res.json(book)
})
.catch((err) => {
res.json('not saved')
})
//////////// //*WORKING *////////////////
} else if (book) {
// findOneAndUpdate
//////////// //*HERE IS PROBLEM *////////////////
book.quantity = book.quantity+1;
console.log(book._id)
Book.findOneAndUpdate({ _id: book._id },
{ $set: { quantity : book.quantity } }, { upsert: true }),
((err, complete) => {
if(err) console.log('errrrrr');
else {
console.log(complete)
console.log('complete')
res.json(complete)
}
})
}
});
}
});
});
this problem may be related to the fact that the findOneAndUpdate function is nested in findById?
I think that you have an extra parentheses on the findOneAndUpdate
Book.findOneAndUpdate({ _id: book._id },
{ $set: { quantity : book.quantity } }, { upsert: true },
(err, complete) => {
if(err) console.log('errrrrr');
else {
console.log(complete)
console.log('complete')
res.json(complete)
}
})
I'm trying to make 2 remote methods work in the same loopback model but only one is working in the api explorer. If I comment/delete the code of one of them the other one works perfectly. Here is how I'm trying to achive it:
module.exports = function(Usrs) {
var db = server.dataSources.wifiMongo;
db.connect(function(err, db) {
Usrs.authMethod = function(cb) {
db.collection('users', function(err, collection) {
if (err)
return console.log('Error al encontrar la collección, err = ', err);
collection.aggregate([{
"$group": {
_id: "$strategy",
count: {
$sum: 1
}
}
}, {
$project: {
tmp: {
label: '$_id',
value: '$count'
}
}
}, {
$group: {
_id: 'methods',
data: {
$addToSet: '$tmp'
}
}
}], function(err, methods) {
if (err) {
console.log('err = ' + err);
} else {
methods[0].data.forEach(function(method) {
method.color = randomColor({
luminosity: 'bright'
});
});
cb(null, methods[0].data);
}
});
});
};
});
Usrs.remoteMethod('authMethod', {
http: {
path: '/analytics/authMethod',
verb: 'get'
},
returns: {
arg: 'authMethod',
type: 'Object'
}
});
//-------------------------------------------------------------------------//
db.connect(function(err, db) {
if (err) return console.log('error al conectar wifiMongo, err = ', err);
Usrs.devices = function(cb) {
db.collection('clients', function(err, collection) {
if (err)
return console.log('Error al encontrar la collección, err = ', err);
collection.aggregate([{
"$group": {
_id: "$os",
count: {
$sum: 1
}
}
}], function(err, client) {
if (err) {
console.log(err);
} else {
var output = client.reduce(function(a, b) {
var match = b._id ? b._id.match(/(Mac|Windows|Android|iOS)/) : null;
match = match ? match[0] : 'Others';
a[match] = (a[match] || 0) + b.count;
return a;
}, {});
output = Object.keys(output).map(function(k) {
return {
label: k,
value: output[k],
color: randomColor({
luminosity: 'bright'
})
};
});
cb(null, output);
}
});
});
};
});
Usrs.remoteMethod('devices', {
http: {
path: '/analytics/devices',
verb: 'get'
},
returns: {
arg: 'devices',
type: 'Object'
}
});
};
If try it in the api explorer the first one runs without error.
The other one gives me internal server error.
An logs the following:
Unhandled error for request GET /usrs/analytics/devices: TypeError: Cannot read property 'collection' of undefined
at Function.Usrs.devices (/home/ubuntu/loopback/common/models/usrs.js:72:6)
at SharedMethod.invoke (/home/ubuntu/loopback/node_modules/strong-remoting/lib/shared-method.js:262:25)
at HttpContext.invoke (/home/ubuntu/loopback/node_modules/strong-remoting/lib/http-context.js:295:12)
at phaseInvoke (/home/ubuntu/loopback/node_modules/strong-remoting/lib/remote-objects.js:647:9)
at runHandler (/home/ubuntu/loopback/node_modules/strong-remoting/node_modules/loopback-phase/lib/phase.js:135:5)
at iterate (/home/ubuntu/loopback/node_modules/strong-remoting/node_modules/loopback-phase/node_modules/async/lib/async.js:146:13)
at Object.async.eachSeries (/home/ubuntu/loopback/node_modules/strong-remoting/node_modules/loopback-phase/node_modules/async/lib/async.js:162:9)
at runHandlers (/home/ubuntu/loopback/node_modules/strong-remoting/node_modules/loopback-phase/lib/phase.js:144:13)
at iterate (/home/ubuntu/loopback/node_modules/strong-remoting/node_modules/loopback-phase/node_modules/async/lib/async.js:146:13)
at /home/ubuntu/loopback/node_modules/strong-remoting/node_modules/loopback-phase/node_modules/async/lib/async.js:157:25
at /home/ubuntu/loopback/node_modules/strong-remoting/node_modules/loopback-phase/node_modules/async/lib/async.js:154:25
at execStack (/home/ubuntu/loopback/node_modules/strong-remoting/lib/remote-objects.js:492:7)
at RemoteObjects.execHooks (/home/ubuntu/loopback/node_modules/strong-remoting/lib/remote-objects.js:496:10)
at phaseBeforeInvoke (/home/ubuntu/loopback/node_modules/strong-remoting/lib/remote-objects.js:643:10)
at runHandler (/home/ubuntu/loopback/node_modules/strong-remoting/node_modules/loopback-phase/lib/phase.js:135:5)
at iterate (/home/ubuntu/loopback/node_modules/strong-remoting/node_modules/loopback-phase/node_modules/async/lib/async.js:146:13)
I've even tried placing them in different models, but the same error appears.
Always handle errors.
You might be getting error while connecting to wifiMongo, Once check that.
db.connect(function(err, db) {
if (err){ console.log('error al conectar wifiMongo, err = ', err);
return cb(err);
}
Usrs.devices = function(cb) {
Currently I have the following callback system:
var saveTask = function(err, result) {
if (err) return callback(err, result);
var newid = mongoose.Types.ObjectId();
var task = new Task({
_id: newid,
taskname: req.body.name,
teamid: req.body.team,
content: req.body.content,
creator: req.user.userId
});
task.save(function (err) {
if (!err) {
log.info("New task created with id: %s", task._id);
return callback(null, task);
} else {
if(err.name === 'ValidationError') {
return callback('400', 'Validation error');
} else {
return callback('500', 'Server error');
}
log.error('Internal error(%d): %s', res.statusCode, err.message);
}
});
};
if (req.body.team) {
valTeam.isMember(req.body.team, req.user._id, function (err, done) {
if (err) {
saveTask('403', 'Not the owner or member of this team');
} else {
saveTask(null, true);
}
});
} else {
saveTask(null, true);
}
valTeam.isMember
exports.isMember = function(teamid, userid, callback) {
Team.find({'_id':teamid, $or:[{'creator': userid }, {'userlist': { $in : [userid]}}]}, function(err, result) {
if (err) return err;
console.log(result);
if (!result.length)
return callback('404', false);
else
return callback(null, true);
});
}
In short, if team is sent by POST, I'm checking if the user is member of that ID in valTeam.isMember. Am I using the correct syntax and best method to call back my saveTask function to save the task if the user is part of the team?
This code currently works, but I feel like there should be an easier way to do it? How could I use a promise to achieve the same thing?
Thanks in advance.
It's curious the fact that you create objects instead Schemas. However "every head is a different world", this is my way:
task.save(function(error, data){
if (error) {
trow error;
} else {
//Make whatever you want here with data
});
exports.list = function(req, res) {
var location_parent_id = req.params.location_parent_id;
var query = {
company_id: req.company_id
};
if(location_parent_id) {
query.location_parent_id = location_parent_id;
Location.findOne({someQuery}, function(err, location) {
response.location = location;
});
} else {
query.location_parent_id = {
'$exists': false
}
}
Location.find(query, function(err, locations) {
if(err) {
response = {
status: 'error',
error: err
}
} else if(!locations) {
response = {
status: 'error',
error: 'Location not found'
}
} else {
response = {
status: 'ok',
locations: locations
}
return res.json(response);
}
});
}
That's my code. If there is a location_parent_id, then I want to return that location as well. Rather than getting into async and callback hell, I figure promises are a good way to execute what I want. Just not sure of exactly how.
You don't need to use q.defer at all. You can use the Node-callback interface methods to get promises right away. To chain the methods, use .then().
exports.list = function(req, res) {
var result = Q.ninvoke(Location, "find", {
company_id: req.company_id,
location_parent_id: req.params.location_parent_id || {'$exists': false}
}).then(function(locations) {
if (!locations)
throw new Error('Location not found');
return {
status: 'ok',
locations: locations
};
});
if (req.params.location_parent_id) {
// insert the step to wait for the findOne (in parallel), and merge into res
result = Q.all([result, Q.ninvoke(Location, "findOne", {someQuery})])
.spread(function(res, location) {
res.location = location;
return res;
});
}
result.catch(function(err) {
return {
status: 'error',
error: err.message
};
}).done(function(response) {
res.json(response);
});
}