Mongoose return fields order inconsistent - node.js

I have a very simple mongoose query, that returns correctly but the field display ordering is not consistent. Notice how sometimes it lists _id and then username and sometimes it lists username and then _id. Any way to fix this?
exports.list = function(req, res){
return db.userModel.find({}, 'username', function (err, users) {
if(!err) {
return res.json(users);
} else {
console.log(err);
return res.send(400);
}
});
}
Output
[
{
"username": "admin",
"_id": "5145293a420135521c000001"
},
{
"username": "bob",
"_id": "5145293b420135521c000002"
},
{
"_id": "5145293b420135521c000003",
"username": "example"
}
]

Related

How to pick a subdocument with its id?

I'm trying to get a subdocument (nested in array) by its id, but I still get the whole document.
router.get("/book/:libraryid/:bookid", (req, res) => {
Library.findOne({ _id: req.params.libraryid, "book._id": req.params.bookid})
.then(result => {
console.log(result); //shows all subdocument
});
});
How can I just pick out the subdocument with its id?
Schema:
{
"_id": {
"$oid": "12345"
},
"libaryName": "A random libary",
"Books": [
{
"_id": {
"$oid": "1"
}
"bookTitle": "Example",
"TotalPages": "500"
},
{
"_id": {
"$oid": "2"
}
"bookTitle": "Delete Me",
"TotalPages": "400"
}
]
}
Use the following and it should return you the document with only filtered book based on bookId.
router.get("/book/:libraryid/:bookid", (req, res) => {
Library.findOne({ _id: req.params.libraryid}, {"books": {"$elemMatch": {_id: req.params.bookid}}})
.then(result => {
console.log(result); //shows all subdocument
});
});

Mongoose - query to get all subdocuments of a document

I'm quite new with mongoose, I have a list of companies and each company has a subarray of users. I just want to retrieve with mongoose all users of a specific company:
a single company is like
{
"_id": "57ffa47f5b70f90831212348",
"name": "mycompany",
"address": "...",
"phone": "...",
"users": [
{
"_id": "57ffa47f5b70f90831212347",
"username": "alpha",
"name": "myname",
"surname": "mysurname",
"password": "..."
}
]
}
I tried with
Company.findOne({
'name': req.user.name
})
.aggregate({$unwind: '$users'})
.exec(
function(err, users) {
if (err) res.status(500).send(err);
res.json(users);
});
but I had no luck... I'm not sure how to use aggregate correctly.
Just populate company.users
Company.findOne({
'name': req.user.name
})
.populate('users', 'username name surname')
.exec(
function(err, company) {
if (err) res.status(500).send(err);
res.json(company.users);
});

Mongodb node.js sum of key values of all the document in the collection

this is my cart collection which is having a price key. In my node.js code I want to view sum of price of both the documents. I tried using aggregate but didnt work
cart collection
[
{
"_id": "57244d0a05dcf1d7151ede7f",
"art_id": "57244c9505dcf1d7151ede7c",
"artist_id": "5721a528c9d28cd51f014038",
"user_id": "5721a528c9d28cd51f014038",
"price": "90"
},
{
"_id": "57244d1f05dcf1d7151ede80",
"art_id": "57244c6105dcf1d7151ede7b",
"artist_id": "5721a528c9d28cd51f014038",
"user_id": "5721a528c9d28cd51f014038",
"price": "150"
}
]
node.js code
function test(req, res, next) {
db.users.findOne({
_id: mongoskin.helper.toObjectID(req.session.user._id)
}, function(err, user) {
if (!user) {
return res.status(400).send({
status: '404 user not found'
});
}
db.cart.find({
user_id: req.session.user._id
}).toArray(function(err, result) {
if (err) return next(err);
res.send(result)
});
});
}
to add all price can use aggregate like:
db.cart.aggregate(
[
{
$group : {
_id : null,
totalPrice: { $sum: price }
}
}
]
).exec(function(error, result) {
if (err) return next(err);
res.send(result)
});

How to remove a specific field by given conidtion in mongodb?

I am not being able to remove the specific eventid from the UserSchema. I searched for this and got a method using unset but that is removing the whole user while the push function is not doing anything. Is there any other way to do this?
Current UserSchema
[
{
"_id": "56593f3657e27af8245735d7",
"username": "abc",
"name": "xyz",
"__v": 0,
"favouriteid": [
{
"eventid": "5659242639e43bfc2a0836b9",
"_id": "56593f6b57e27af8245735d8"
},
{
"eventid": "5659244039e43bfc2a0836ba",
"_id": "56593f8857e27af8245735d9"
}
]
}
]
What I need-
[
{
"_id": "56593f3657e27af8245735d7",
"username": "abc",
"name": "xyz",
"__v": 0,
"favouriteid": [
{
"eventid": "5659242639e43bfc2a0836b9",
"_id": "56593f6b57e27af8245735d8"
}
]
}
]
I will be passing eventid and userid
But when I am using this code, the user gets deleted itself rather than the required field.
api.post('/removefavourites', function (req, res) {
// _id: req.body.id;
User.findById({_id: req.body.userid},
{$unset:{favouriteid: {eventid: req.body.eventid}}},
{upsert: true},
function (err) {
if(err)
return err;
else
res.json({success: true});
});
});
You can use update method with pull operation as shown below:
api.post('/removefavourites', function (req, res) {
// _id: req.body.id;
User.update({_id: req.body.userid},
{$pull:{favouriteid: {eventid: req.body.eventid}}},
function (err) {
if(err)
return err;
else
res.json({success: true});
});
});
Hope this helps :)

Passing two query results into a response

I have a query that fetches the top 5 people for a leaderboard. In robomongo this query works fine.
When I do something like
var leaderboard = User.find({points: {$exists: true}}, {
"firstname": 1,
"lastname": 1,
"points": 1
}).sort({points : -1}).limit(5)
console.log('leaderboard');
I get a lot of meaningless json with [object] almost everywhere.
How would I execute this query for use with mongoose + express so I can pass through to the view as an array of
firstname, lastname, points
So I can loop it through in the view?
My complete code is
app.get('/dashboard', function(req, res){
if (req.user) {
// logged in
User.find({}, function(err, docs) {
// console.log(docs);
});
// Get total points after submit
var leaderboard = User.find({points: {$exists: true}}, {
"firstname": 1,
"lastname": 1,
"points": 1
}).sort({points : -1}).limit(5).toArray();
console.log(leaderboard);
User.find({
points: {
$exists: true
}
}, function(err, docs) {
if(err){
console.log(err);
//do error handling
}
//if no error, get the count and render it
var count = 0;
for (var i = 0; i < docs.length; i++) {
count += docs[i].points;
}
var totalpoints = count;
res.render('dashboard', {
title: 'Dashboard',
user: req.user,
totalpoints: totalpoints
});
});
} else {
// not logged in
return res.redirect('/login');
}
});
So you are really only returning a cursor here and not executing the query. You can of course always nest the queries, but you can be a bit nicer and use async.waterfall to avoid the indenting mess.
Also I would use .aggregate() rather than looping all the documents just to get a total. And mongoose automatically converts results to an array, so the .toArray() is not required here:
app.get('/dashboard', function(req, res){
if (req.user) {
// logged in
async.waterfall(
[
function(callback) {
User.find(
{ "points": { "$exists": true } },
{
"firstname": 1,
"lastname": 1,
"points": 1
}
).sort({points : -1}).limit(5).exec(callback);
},
function(leaderboard,callback) {
User.aggregate(
[
{ "$match": { "points": { "$exists": true } }},
{ "$group": {
"_id": null,
"totalpoints": { "$sum": "$points" }
}}
],
function(err,result) {
callback(err,result,leaderboard)
}
);
}
],
function(err,result,leaderboard) {
if (err) {
console.log(err);
//do error handling
} else {
res.render('dashboard', {
title: 'Dashboard',
user: req.user,
totalpoints: result[0].totalpoints,
leaderboard: leaderboard
});
}
}
);
} else {
// not logged in
return res.redirect('/login');
}
});
So you get your leaderboard result and just put it in the response, much as is done in the example here.
An alternate approach is using async.parallel, since you don't need the output of the first query within the second. In that case the results of both queries are sent to the callback at the end, much like above. Again you just use the results in your final response.
app.get('/dashboard', function(req, res){
if (req.user) {
// logged in
async.parallel(
{
"leaderboard": function(callback) {
User.find(
{ "points": { "$exists": true } },
{
"firstname": 1,
"lastname": 1,
"points": 1
}
).sort({points : -1}).limit(5).exec(callback);
},
"totalpoints":function(callback) {
User.aggregate(
[
{ "$match": { "points": { "$exists": true } }},
{ "$group": {
"_id": null,
"totalpoints": { "$sum": "$points" }
}}
],
function(err,result) {
callback(err,result[0].totalpoints)
}
);
}
},
function(err,results) {
if (err) {
console.log(err);
//do error handling
} else {
res.render('dashboard', {
title: 'Dashboard',
user: req.user,
totalpoints: results.totalpoints,
leaderboard: results.leaderboard
});
}
}
);
} else {
// not logged in
return res.redirect('/login');
}
});

Resources