MongoDB: aggregate error when use Match and Group - mongodb

I've model
var LogSchema = mongoose.Schema({
userId: String,
pageId: String,
tagId: String
}, {
timestamps: true
});
In code,
Log.aggregate([
{
$match: {
createdAt: {
$gte: new Date(strFrom),
$lte: new Date(strTo),
}
},
//$group: { _id: "$userId" },
}
], function (err, logs) {
if (err) {
res.status(500).send({ message: "error retrieving logs." });
} else {
res.send(logs);
}
});
When I execute code use $match, that's ok. Then, I add $group, I receive error
Error: Arguments must be aggregate pipeline operators
So, I remove $match, only use $grooup, code run ok. So, when I use both $match and $group, receive errors.
Please, give me ideas
Thank so much

You have a missing brace.
Log.aggregate([{
$match: {
createdAt: {
$gte: new Date(strFrom),
$lte: new Date(strTo),
}
},
{ <-- missing brace around your group
$group: { _id: "$userId" },
}],
function (err, logs) {
if (err) {
res.status(500).send({ message: "error retrieving logs." });
} else {
res.send(logs);
}
});

Related

Push an object into a nested array in MongoDB

I've got a head-scratcher here that I'd like to share with you all.
So here's the model:
_id: ObjectId()
name: String,
columns: [
{
name: String,
_id: ObjectId()
tasks: [
{
title: String,
description: String,
status: String,
_id: ObjectId()
subtasks: [
{
title: String,
isCompleted: Boolean,
},
],
},
],
},
],
});
and the query:
exports.createSubtask = (req, res) => {
if (!req.body) {
res.status(400).send({ message: "Task name can not be empty!" });
return;
}
const board = req.params.board;
const column = req.params.column;
const task = req.params.task;
Board.findOneAndUpdate(
{
_id: board,
"columns._id": column,
"columns.tasks._id": task,
},
{
$push: {
"columns.$.tasks.$.subtasks": req.body,
},
}
)
.then((data) => {
if (!data) {
res.status(404).send({
message: `Cannot update Task with id=${task}. Maybe task was not found!`,
});
} else res.send({ message: "Task was updated successfully." });
})
.catch((err) => {
res.status(500).send({
message: "Error updating Task with id=" + task,
});
});
};
I'm trying to push an object into the subtasks array with $push, but Postman is throwing an error.
Any ideas as to what I'm doing wrong? Appreciate the help.
Golden Ratio
However, I was able to successfully push an object into the tasks array with the following query:
exports.createTask = (req, res) => {
if (!req.body) {
res.status(400).send({ message: "Task name can not be empty!" });
return;
}
const board = req.params.board;
const column = req.params.column;
Board.findOneAndUpdate(
{
_id: board,
"columns._id": column,
},
{
$push: {
"columns.$.tasks": req.body,
},
}
)
.then((data) => {
if (!data) {
res.status(404).send({
message: `Cannot update Column with id=${column}. Maybe column was not found!`,
});
} else res.send({ message: "Column was updated successfully." });
})
.catch((err) => {
res.status(500).send({
message: "Error updating Column with id=" + column,
});
});
};
It is not possible to use multiple positional $ for the nested array as mention in docs:
The positional $ operator cannot be used for queries which traverse more than one array, such as queries that traverse arrays nested within other arrays, because the replacement for the $ placeholder is a single value
You should work with the positional filtered operator $[<identifier>].
Board.findOneAndUpdate(
{
_id: board,
"columns._id": column,
"columns.tasks._id": task,
},
{
$push: {
"columns.$.tasks.$[task].subtasks": req.body,
},
},
{
arrayFilters: [
{ "task._id": task }
]
}
)
.then(...);
Note: Ensure that the passed in task is ObjectId type.
Credit to Yong Shun Yong for the help. Through trial and error, I solved the problem with the following code
Board.findOneAndUpdate(
{
_id: board,
"columns._id": column,
},
{
$push: {
"columns.$.tasks.$[].subtasks": req.body,
},
},
{
arrayFilters: [{ "task._id": task }],
}
)

mongodb aggregation where document field is less than another field

Using mongoose, I'm trying to make a query that searches for tasks where timeSpent is greater than timeBilled.
Task schema:
const myTaskSchema = new Schema({
date: { type: Date, default: Date.now },
timeSpent: { type: Number },
timeBilled: { type: Number }
})
The query I've tried:
myTaskSchema.aggregate([
{
$match: {
timeSpent: { $gt: '$timeBilled' }
}
}
])
.then(data => {
console.log(data)
})
But I'm getting zero results (I know there should be results)
NOTE: Not every task has a timeSpent or timeBilled.field if that matters.
here is my dirty solution. It'd be nice if I didnt have to add a field but this gets me where I want to be.
myTaskSchema.aggregate([
{
$addFields: {
needToBill: { $gt: ['$timeSpent', '$timeBilled'] }
}
},
{
$match: {
needToBill: true
}
},
{
$project: {
timeSpent: 1,
timeBilled: 1
}
}
])

MongoDB Aggregate Query Returning Empty Array

I'm new to Mongo and am trying to run an aggregate command on my model in my Node.js application (using Express). I'm trying to run the query for finding all users registered within the last month.
When I run User.find(), it returns all 2 users in the DB, so I know the users are definitely there.
However, when I run this, data is just an empty array. Is there a solution I'm missing here?
router.get("/stats", verifyTokenAndAdmin, async (req, res) => {
const date = new Date();
const lastYear = new Date(date.setFullYear(date.getFullYear() - 1));
try {
const data = await User.aggregate([
{ $match: { createdAt: { $gte: lastYear } } },
{
$project: {
month: { $month: "$createdAt" },
},
},
{
$group: {
_id: "$month",
total: { $sum: 1 },
},
},
]);
res.status(200).json(data)
} catch (err) {
res.status(500).json(err);
}
});
Also, the response is a 200 so no errors there.

MongoDB + Mongoose Aggregate w/ Asnyc

I've got the following route in my express file, which takes parameters passed in from a middleware function and queries my backend MongoDB database. But for some reason, it only ever returns an empty array.
I'd like to convert the Mongoose model that allows me to use aggregate functions into async/await to conform with the rest of my code. It's online here.
module.exports = {
search: asyncWrapper(async(req, res, next) => { // Retrieve and return documents from the database.
const {
filterTarget,
filter,
source,
minDate,
maxDate,
skip,
limit,
sortBy,
sortOrder
} = req.search;
try {
const mongoData = await Model.aggregate([
{
$match: {
date: {
$gt: minDate, // Filter out by time frame...
$lt: maxDate
}
}
},
{
$match: {
[filterTarget]: filter // Match search query....
}
},
{
$set: {
[filterTarget]: { $toLower: `$${filterTarget}` } // Necessary to ensure that sort works properly...
}
},
{
$sort: {
[sortBy]: sortOrder // Sort by date...
}
},
{
$group: {
_id: null,
data: { $push: "$$ROOT" }, // Push each document into the data array.
count: { $sum: 1 }
}
},
{
$project: {
_id: 0,
count: 1,
data: {
$slice: ["$data", skip, limit]
},
}
}
])
return res.status(200).json({ data: mongoData.data || [], count: mongoData.count || 0 });
} catch (err) {
next(err);
}
})
};
For some reason, the route is only returning an empty array every time. I've double and triple checked my variables, they are not the problem.
How can I use the Mongoose.aggregate() function in an async await route?

MongoError when trying to aggregate with collection relationship

I am new to mongodb, using Mean stack from meanjs.org.
I have a model with a user collection relationship:
var MealSchema = new Schema({
mealDate: {
type: Date,
default: Date.now
},
user: {
type: Schema.ObjectId,
ref: 'User'
}
});
mongoose.model('Meal', MealSchema);
I had find an implementation that works:
var user = req.user;
Meal.find({
mealDate: {
$gte: minus8days.toDate(),
$lt: tomorrow.toDate()
},
user : user
}).exec(function (err, meals) {
if (err) {
return res.status(400).send({
message: errorHandler.getErrorMessage(err)
});
} else {
res.json(meals);
}
});
But I need some grouping and aggregation so I am trying to change this code to use aggregate instead of a find, I've tried many combinations and I keep getting an error, basically the following implementation throws an error MongoError: Maximum call stack size exceeded
Meal.aggregate([
{
$match: {
$and: [
{
mealDate: {
$gte: minus8days.toDate(),
$lt: tomorrow.toDate()
}
}, { user: user }
]
}
}]...
Why user:user works with find and not with aggregate?
How can the aggregate approach be fixed?
Try this :
Meal.aggregate([
{
$match: {
mealDate: { $gte: minus8days.toDate(), $lt: tomorrow.toDate()},
user: user._id
}
}
], function(err, docs){
if(err) console.log("Error : " + JSON.stringify(err));
if(docs) console.log("Got the docs successfully : " + JSON.stringify(docs));
});