Mongoose aggregate giving no response - node.js

I am trying to mongoose aggregate for grouping businesses. I have working MongoDB script. I am attaching the NodeJS with Mongoose script as well as the MongoDB code below:
History.aggregate()
.cursor({ batchSize: 1000 })
.group({
_id: "$businessName",
transactions: {
$push: "$$ROOT"
},
numberOfTransactions: {
$sum: 1
}
})
.exec(function (err, transactions) {
if (err) {
res.json({
"message": err.message
});
} else if (transactions) {
// winston.info(transactions);
res.send(
transactions
);
}
});
Working MongoDb code:
db.getCollection('transactionhistories')
.aggregate([
{$group:
{_id:"$businessName", transactions:{$push:"$$ROOT"},numberOfTransactions: { $sum: 1 }},
},
{$sort:{businessId:1}}
]).toArray()
I am not receiving any response when using the first code. Can someone help? Thanks in advance!

The .cursor({ batchSize: 1000 }) method you are using would make the aggregate call return a cursor so the callback won't even be executed. Source.
You should use the cursor to access the result of the aggregation:
const cursor = Todo.aggregate()
.cursor({ batchSize: 1000 })
.group({
_id: "$name",
transactions: {
$push: "$$ROOT"
},
numberOfTransactions: {
$sum: 1
}
})
.exec();
cursor.eachAsync(function (doc) {
console.log('doc: ', doc);
});
More details on how to use cursors here.

Related

Grab count of a specific field in database

So I have a schema, and in that schema there is an object called "caseCount" with a Number Value. I simply want to fetch every value for each object of "caseCount" and add it up. Could somebody point me in the right direction? If I need to explain some more, I'm happy to do so. Thank you!
Schema:
const Profile = Schema({
userID: String,
messageCount: Number,
caseCount: Number,
Skins: Array,
});
code:
const data = Profiles.collection.aggregate([{
$group: {
_id: '$id',
totalCaseCount: { $sum: '$caseCount' }
}
}]);
Demo - https://mongoplayground.net/p/7TnpQl3tJHt
Use $sum in aggregation query.
$group
db.collection.aggregate({
$group: {
_id: null,
toatalCaseCount: { $sum: "$caseCount" }
}
})
Profiles.collection.aggregate([{
$group: {
_id: null,
totalCaseCount: { $sum: '$caseCount' }
}
}], function(err, data) {
console.log(data); // here you'll get your data from the query
});

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.

Mongodb findone document in array by object id

Can I get only 1 photo by objectid? I only need to get 1 Image detail from 1 post by photo but what i get is all photo of post.
this is my db structure
and this is my code looks like:
Post.findOne({
$and: [
{ photo: { $elemMatch: { _id: id } } } ]
}).exec((err, post) => {
if (err) {
return res.status(400).json({ error: err });
}
req.post = post;
console.log(req.post);
next();
});
what i get in req.post is only [].
Thanks in advance
The $elemMatch projection operator provides a way to alter the returned documents, in here coupled with find utilizing second parameter which is projection will achieve that.
Post.find(
{},
{
_id: 0,
photo: { $elemMatch: { _id: id } }
}
);
This will provide leaned documents with the promise: .lean().exec():
Post.find(
{},
{
_id: 0,
photo: { $elemMatch: { _id: id } }
}
)
.lean()
.exec();
NOTE: $elemMatch behaviour is to return the first document where photo's id matches.
You can try with aggregate instead of findOne:
https://docs.mongodb.com/manual/reference/operator/aggregation-pipeline/
Post.aggregate([
{ $match: { 'photo._id': id } },
{ $unwind: "$photo" },
{ $match: { 'photo._id': id } },
]);
Maybe not the best, but single photo data is achievable.

How can I sort then limit an array of results in mongoose?

I have a user collection with tons of properties for each user. One property is an array ('guestlist_submitted') and I am trying to return the most recent 3 items in that array according to the date field.
Using Mongoose, I am able to target that specific property on the user object, but I can't seem to limit or sort them.
I've tried using slice and and a date calculation to return the most recent 3 entires but I am struggling to get it right and not sure whats wrong.
I've tried using Sort/Limit on query builder but I've learned that these dont apply to the array inside the property I am targeting despite me saying " select('guestlist_submitted') as part ofthe query builder.
When I return the result after just targeting the property on the user object, it comes as an array with an object inside and another array inside that object. I am having problems traversing down it to target that inner array to use the slice and date sort.
First try
Users.
find({ fbid: req.query.fbid}).
select('guestlist_submitted -_id').
sort({"guestlist_submitted.date": 'desc'}).
limit(3).
exec((err, result) => {
if (err) throw (err));
if (result) {
res.send(result);
}
Second try:
Users.
find({ fbid: req.query.fbid}).
select('guestlist_submitted -_id').
exec((err, result) => {
if (err) res.send(JSON.stringify(err));
if (result) {
const slicedArr = result.guestlist_submitted.slice(0,1);
const sortedResultArr = slicedArr.sort(function(a,b){return new Date(b.date) - new Date(a.date); });
const newResultArr = sortedResultArr.slice(0, 2);
res.send(newResultArr);
}
The best I can get is 'guestlist_submitted' and the entire array, not sorted and its inside an object inside an array.
I expected the output to be the 3 most recent results from the guestlist_submitted array inside a new array that I can display on the client.
Thank you in advance for your help
EDIT - FIGURED OUT A SOLUTION
I ended up figuring out how to get what I wanted using aggregate()
Users.aggregate([
{
$match: {fbid: req.query.fbid}
},
{
$unwind: "$guestlist_submitted"
},
{
$sort: {'guestlist_submitted.date': -1}
},
{
$limit: 2
},
{
$group: {_id: '$_id', guestlist_submitted: {$push: '$guestlist_submitted'}}
},
{
$project: { guestlist_submitted: 1, _id: 0 }
}
]).exec((err, result) => {
if (err) res.send(JSON.stringify(err));
if (result) {
console.log(result);
res.send(result);
}
});
I ended up figuring out how to get what I wanted using aggregate()
Users.aggregate([
{
$match: {fbid: req.query.fbid}
},
{
$unwind: "$guestlist_submitted"
},
{
$sort: {'guestlist_submitted.date': -1}
},
{
$limit: 2
},
{
$group: {_id: '$_id', guestlist_submitted: {$push: '$guestlist_submitted'}}
},
{
$project: { guestlist_submitted: 1, _id: 0 }
}
]).exec((err, result) => {
if (err) res.send(JSON.stringify(err));
if (result) {
console.log(result);
res.send(result);
}
});

Mongoose update with limit

I am looking to update X documents all at once. The short is I basically need to randomly select N documents and then update them as "selected". I'm trying to design an API that needs to randomly distribute questions. I can not find a way to do this in mongoose I have tried:
update ends up selecting everything
Question
.update({}, {
$inc: {
answerCount: 1,
lockedCount: 1
},
$push:{
devices: deviceID
}
}, {multi:true})
.limit(4)
--- I also tried
Question
.find()
.sort({
answerCount: 1,
lockedCount: 1
})
.limit(req.query.limit || 4)
.update({}, {
$inc: {
answerCount: 1,
lockedCount: 1
},
$push:{
devices: deviceID
}
}, { multi: true }, callback);
Both resulted in updating all docs. Is there a way to push this down to mongoose without having to use map ? The other thing I did not mention is .update() without multi resulted in 1 document being updated.
You could also pull an array of _ids that you'd like to update then run the update query using $in. This will require two calls to the mongo however the updates will still be atomic:
Question.find().select("_id").limit(4).exec(function(err, questions) {
var q = Question.update({_id: {$in: questions}}, {
$inc: {answerCount: 1, lockedCount:1},
$push: {devices: deviceid}
}, {multi:true});
q.exec(function(err) {
console.log("Done");
});
});
So I did an simple map implementation and will use it unless someone can find a more efficient way to do it.
Question
.find({
devices: { $ne: deviceID}
},
{ name: true, _id: true})
.sort({
answerCount: 1,
lockedCount: 1
})
.limit(req.query.limit || 4)
.exec(updateAllFound );
function updateAllFound(err, questions) {
if (err) {
return handleError(res, err);
}
var ids = questions.map(function(item){
return item._id;
});
return Question.update({ _id: { $in: ids} } ,
{
$inc: {
answerCount: 1,
lockedCount: 1
},
$push:{
devices: deviceID
}
}, { multi: true }, getByDeviceID);
function getByDeviceID(){
return res.json(200, questions);
}
}

Resources