Sort in mongoose-aggregate-paginate-v2 - node.js

I am using mongoose-aggregate-paginate-v2
and sort does not work
var aggregate = searchmodel.aggregate()
aggregate.match({ word: { $regex: `.*${word}.*` } })
.group({
_id: '$user_id', doc: { $first: '$$ROOT' }
})
var options = {
page: page, limit: 50, sort: { createdAt: -1 }
}
searchmodel.aggregatePaginate(aggregate, options).then(function (results) {
return res.json(results)
}).catch(function (err) {
console.log(err);
})
I tried lot of things for sort but it does not work at all

You should write,
var aggregate = searchmodel.aggregate()
aggregate.match({ word: { $regex: `.*${word}.*` } })
.group({
_id: '$user_id', doc: { $first: '$$ROOT' }
})
var options = {
page: page, limit: 50, sort: { createdAt: 'desc' }
}
searchmodel.aggregatePaginate(aggregate, options).then(function (results) {
return res.json(results)
}).catch(function (err) {
console.log(err);
})

Actually I found the solution to the problem.
You need to add _id to the sort object.
Example:
const options = {
page: page,
limit: limit,
sort: { _id: 1, createdAt: -1 },
};
and it works.

Related

Mongoose document.updateOne() not working as expected

code
Hi, I am trying to update my document using document.updateOne() in mongoose but only the balance field, downlines,
const username = req.params.username;
try {
const partner = await Partner.findOne({ username });
await partner.updateOne({ isApproved: true });
const ref = await Partner.findOne({ username: partner.referer });
if (ref) {
await ref.updateOne(
{
$inc: { points: 10 },
referer: partner.referer,
$inc: { earnings: 10 },
$inc: { balance: 10 },
$push: { downlines: partner.username },
},
{ new: true }
);
const grandRef = await Partner.findOne({ username: ref.referer });
if (!grandRef) {
res
.status(200)
.json(
'Partner approval was successfull with a referrer but no grand referrer'
);
} else {
await grandRef.updateOne(
{
$inc: { points: 3 },
grandReferer: ref.referer,
$inc: { earnings: 3 },
$inc: { balance: 3 },
$push: { grandDownlines: partner.username },
},
{ new: true }
);
res
.status(200)
.json(
'Partner approval was successfull with both referrer and grand referrer'
);
}
} else {
res
.status(200)
.json(
'Partner approval was successfull without a referrer or a grand referrer'
);
}
} catch (err) {
res.status(500).json('Something went wrong');
console.log(err);
}
and grand_downlines updates but the earnings and points do not. Please check my snippet in the attached image and help.
Okay so I just realized because I am using "$inc" more than one at the same time and if I change the positions, the last "$inc" updates so I used expressions.
{
$push: { downlines: partner.username },
points: ref.points + 10,
balance: ref.balance + 10,
earnings: ref.earnings + 10,
},
{ new: true }

mongodb using $not getting undefined, in NodeJs

I am trying to make a find command in Mongo using $not as i see in this link,
await Match.find(
{
type:"Friendly", status:"Pending", "date.fullDate":{$gt: date},
$not:{$or: [{"team1.players":{$elemMatch: {_id:playerId}}}, {"team2.players":{$elemMatch: {_id:playerId}}}]}
}).sort({'date.fullDate':1}).exec(function(err, doc) {
console.log(doc)
return res.send({data: doc});
});
However, i am getting undefined.
I am thinking the problem is with the $not because when i remove it and make the command like this it works.
await Match.find(
{
type:"Friendly", status:"Pending", "date.fullDate":{$gt: date},
$or: [{"team1.players":{$elemMatch: {_id:playerId}}}, {"team2.players":{$elemMatch: {_id:playerId}}}]
}).sort({'date.fullDate':1}).exec(function(err, doc) {
console.log(doc)
return res.send({data: doc});
});
Note: that team2 could be null in the documents.
Any ideas why i am getting this undefined.
My document look like this:
match:{
team1:{
players:[
_id:value,
otherfields...
]
},
team2:{
players:[
_id:value,
otherfields...
]
}
}
await Match.find({
type: "Friendly", status: "Pending", "date.fullDate": { $gt: date },
$or: [
{
$not: {
"team1.players": { $elemMatch: { _id: playerId } }
}
},
{
$not: {
"team2.players": { $elemMatch: { _id: playerId } }
}
}
]
}).sort({ 'date.fullDate': 1 }).exec(function (err, doc) {
console.log(doc)
return res.send({ data: doc });
});
I had to use $nor instead of $not:{$or:[]}
Similar question here.

listing a subcollection array's objects by sorting property of mongoose

I want to sort the items in array of my collection's sub-collection.I can list them but not in the way that i want.I want them to sort by 'no' properties of item array objects
My Collection Schema
const itemSchema = mongoose.Schema({
item: [{ name: String, no: Number }],
});
module.exports = mongoose.model("Item", itemSchema);
My End-Point
getItems(req, res) {
try {
Item.find({ _id: req.item._id }, (err, founds) => {
if (err) {
res
.status(httpStatus.INTERNAL_SERVER_ERROR)
.json({ message: "error occured!", err });
}
res.send(founds[0].item);
});
} catch (error) {
res
.status(httpStatus.INTERNAL_SERVER_ERROR)
.json({ message: "display item error", error });
}
},
You can do this by using aggregation.
First we are matching the documents, then unwinding, sorting and lastly grouping.
Item.aggregate([
{
$match: {
_id: mongoose.Types.ObjectId("5df7c5121debf22a388da1c7")
}
},
{
$unwind: "$item"
},
{
$sort: {
"item.no": -1 //1 (for ascending) or -1 (for descending)
}
},
{
$group: {
_id: "$_id",
item: {
$push: "$item"
}
}
}
])
.then(founds => {
if (founds.length > 0) {
res.send(founds[0]);
} else {
res.status(400).send("No doc found");
}
})
.catch(err => {
res
.status(httpStatus.INTERNAL_SERVER_ERROR)
.json({ message: "error occured!", err });
});

Is there any way to rename the path while we select the complex object from mongodb using mongoose in nodejs?

I want to rename the path of the fields which are coming from the response.
My Query:
const allLeads = await Lead.find().select({
"basic.mobileNumber": 1
});
res.send({ allLeads });
Response I'm Getting
{
"allLeads": [
{
"_id": "5d9f0e2118d1a445bae077aa",
"basic": {
"mobileNumber": "1223654789"
}
},
{
"_id": "5d9f16a8cba7744902acb422",
"basic": {
"mobileNumber": "1123654789"
}
}
]
}
how I want the response
{
_id: 5d9f0e2118d1a445bae077aa,
mobileNumber: "1223654789"
},
{
_id: 5d9f16a8cba7744902acb422,
mobileNumber: "1123654789"
}
So is there any way yo archive this using mongoose?
I did it like this. Is there any other and better way to do this?
let simpleLeadInfo = [];
await SwatiLead.find()
.select({
_id: 1,
"basic.mobileNumber": 1,
})
.exec((err, data) => {
if (!err) {
for (lead in data) {
const obj = {
id: data[lead]._id,
mobileNumber: data[lead].basic.mobileNumber,
};
simpleLeadInfo = [...simpleLeadInfo, obj];
}
return res.send({ error: false, status: "OK", simpleLeadInfo });
}
});

Concurrent request can not handle properly with mongoDB and nodeJs

I'm working on my project, which is based on socket.io room. I used socket with nodejs and manage room data in mongoDB.
Here is my code, Only two players can join a room and then after I turn IsGameOn flag false to true.
This code is working fine when I send request to server one by one.
Problem occurs when many request comes at a time. And problem is more than 2 players join the room (Room players' data store in aPlayers Array).
I upload an Image of the database also. So, you can see what happen actually in the database.
const joinRoom = async (sData, callback) => {
if(sData.iPlayerId && sData.eRoomCount)
{
try {
let body = _.pick(sData, ['eRoomCount', 'iPlayerId']);
console.log(body);
await roomsModel.aggregate([
{
$match: {
eRoomCount: body.eRoomCount,
IsGameOn: { $eq: false }
}
},
{ $unwind: "$aPlayers" },
{
$group: {
_id: "$_id",
eRoomCount: { $first: "$eRoomCount" },
aPlayers: { $push: "$aPlayers" },
size: { $sum: 1 }
}
},
{
$match: {
size: { '$lt': body.eRoomCount }
}
},
{ $sort: { size: -1 } }
]).exec((error, data) => {
if (data.length < 1) {
let params = {
eRoomCount: body.eRoomCount,
aPlayers: [{
iPlayerId: body.iPlayerId
}]
}
let newRoom = new roomsModel(params);
console.log(JSON.stringify(newRoom));
newRoom.save().then((room) => {
console.log("succ", room);
callback(null,room);
}).catch((e) => {
callback(e,null);
});
} else {
roomsModel.findOne({ _id: data[0]._id }, (error, room) => {
if (error) {
callback(error,null);
}
if (!room) {
console.log("No room found");
callback("No room found",null);
}
room.aPlayers.push({ iPlayerId: body.iPlayerId });
if (room.aPlayers.length === room.eRoomCount) {
room.IsGameOn = true;
}
room.save().then((room) => {
callback(null,room);
}).catch((e) => {
callback(e,null);
});
})
}
});
} catch (e) {
console.log(`Error :: ${e}`);
let err = `Error :: ${e}`;
callback(e,null);
}
}
}
This is happens when request comes one by one.
This is happens when many request comes at a time.
The right way would be use mongoose's findOneAndUpdate instead of findOne. The operation findOneAndUpdate is atomic. If you do the right query, you can make your code thread safe.
// This makes sure, that only rooms with one or no player gets selected.
query = {
// Like before
_id: data[0]._id,
// This is a bit inelegant and can be improved (but would work fast)
$or: {
{ aPlayers: { $size: 0 } },
{ aPlayers: { $size: 1 } }
}
}
// $addToSet only adds a value to a set if it not present.
// This prevents a user playing vs. him/herself
update = { $addToSet: { aPlayers: { iPlayerId: body.iPlayerId } } }
// This returns the updated document, not the old one
options = { new: true }
// Execute the query
// You can pass in a callback function
db.rooms.findOneAndUpdate(query, update, options, callback)
for this problem you can use semaphore module, in your case Node Server accessing the concurrent request in a single thread environment but OS does the multitasking job. semaphore module will help you to overcome from this hell.
npm i semaphore            (install module)
const sem = require('semaphore')(1);    (require and specify concurrent request limit heres                      its 1 )
sem.take(function());            (wrap your function in it)
sem.leave();               (call it when your processing is done then the next request                   will access the function)
const joinRoom = (sData, callback) => {
sem.take(function () {
if (sData.iPlayerId && sData.eRoomCount) {
try {
let body = _.pick(sData, ['eRoomCount', 'iPlayerId']);
roomsModel.aggregate([
{
$match: {
eRoomCount: body.eRoomCount,
IsGameOn: { $eq: false }
}
},
{ $unwind: "$aPlayers" },
{
$group: {
_id: "$_id",
eRoomCount: { $first: "$eRoomCount" },
aPlayers: { $push: "$aPlayers" },
size: { $sum: 1 }
}
},
{
$match: {
size: { '$lt': body.eRoomCount }
}
},
{ $sort: { size: -1 } }
]).exec((error, data) => {
if (data.length < 1) {
let params = {
eRoomCount: body.eRoomCount,
aPlayers: [{
iPlayerId: body.iPlayerId
}]
}
let newRoom = new roomsModel(params);
console.log(JSON.stringify(newRoom));
newRoom.save().then((room) => {
console.log("succ", room);
sem.leave();
callback(null, room);
}).catch((e) => {
sem.leave();
callback(e, null);
});
} else {
roomsModel.findOne({ _id: data[0]._id }, (error, room) => {
if (error) {
sem.leave();
callback(error, null);
}
if (!room) {
console.log("No room found");
sem.leave();
callback("No room found", null);
}
room.aPlayers.push({ iPlayerId: body.iPlayerId });
if (room.aPlayers.length === room.eRoomCount) {
room.IsGameOn = true;
}
room.save().then((room) => {
sem.leave();
callback(null, room);
}).catch((e) => {
sem.leave();
callback(e, null);
});
});
}
});
} catch (e) {
console.log(`Error :: ${e}`);
let err = `Error :: ${e}`;
callback(e, null);
}
}
});
}

Resources