var oneWeekAgo = new Date();
oneWeekAgo.setDate(oneWeekAgo.getDate() - 7);
User.aggregate([
{ $match: { isAdmin: false, isActive: true } },
{
$group: {
_id: null,
totalCount: {
$sum: 1
}
}
},
])
User.aggregate([
{ $match: { isAdmin: false, dateCreated: { $gte: oneWeekAgo }, isActive: true } },
{
$group: {
_id: null,
lastWeekTotal: {
$sum: 1
}
}
},
])
is there a way to combine 2 aggregation queries above?
I want to count all the entries in the collection and also entries that are created within a week.
Expected result:
[ { _id: null, totalCount: 100 , lastWeekTotal: 10 } ]
You can combine inside $group together like this,
The $cond operator, there are three arguments ($cond: [if check condition, then, else])
first part if condition checks your conditions using $and operator, if conditions is true then return 1 otherwise 0
User.aggregate([
{
$group: {
_id: null,
totalCount: {
$sum: {
$cond: [
{
$and: [
{ $eq: ["$isAdmin", false] },
{ $eq: ["$isActive", true] }
]
},
1,
0
]
}
},
lastWeekTotal: {
$sum: {
$cond: [
{
$and: [
{ $gte: ["$dateCreated", oneWeekAgo] },
{ $eq: ["$isAdmin", false] },
{ $eq: ["$isActive", true] }
]
},
1,
0
]
}
}
}
}
])
Playground
Related
I want to write a MongoDB query in NodeJS where it return the matching documents as well as the count of documents too. For ex consider the below code -
const result = await Student.aggregate(
[
{
$match: {
...filter
}
},
{
$project: {
_id: 1,
payment: 1,
type: 1,
BirthDate: 1
}
},
{
$sort: { StudentData: -1 }
},
{
$count: 'count'
},
{
$skip: skip
},
{
$limit: limit
}
]
);
Here I want to save two things in the result variable - the number of documents and individually all the documents.
let [{ totalItems, result }] = await Student.aggregate(
[
{
$match: {
...filter
}
},
{
$project: {
_id: 1,
payment: 1,
type: 1,
BirthDate: 1
}
},
{
$facet: {
result: [
{
$sort: { BirthDate: -1 },
},
{
$skip: skip
},
{
$limit: limit
}
],
totalItems: [{ $count: 'count' }]
}
},
{
$addFields: {
totalItems: {
$arrayElemAt: ["$totalItems.count", 0]
},
}
}
]
);
My query
let findEvents = await eventModel.aggregate([
{
$match: {
participants: {
$eq: event.userId
},
eventStatus: {
$eq: "COMPLETED"
},
},
},
{
$group: {
_id: null,
totalEvents: {
$sum: 1
},
completedEvents: {
$sum: {
$cond: {
if: {
$eq: [
"$attendants",
event.userId
]
},
then: 1,
else: 0
}
}
}
}
}
])
In this query the if condition is not working, not able to fetch userId from attendents array where userId exists. Could you explain why ?
On my backend I use mongoDB with nodejs and mongoose
I have many records in mongodb with this structure:
{
..fields
type: 'out',
user: 'id1', <--mongodb objectID,
orderPayment: [
{
_id: 'id1',
paid: true,
paymentSum: 40
},
{
_id: 'id2',
paid: true,
paymentSum: 60,
},
{
_id: 'id3',
paid: false,
paymentSum: 50,
}
]
},
{
..fields
type: 'in',
user: 'id1', <--mongodb objectID
orderPayment: [
{
_id: 'id1',
paid: true,
paymentSum: 10
},
{
_id: 'id2',
paid: true,
paymentSum: 10,
},
{
_id: 'id3',
paid: false,
paymentSum: 77,
}
]
}
I need to group this records by 'type' and get sum with conditions.
need to get sum of 'paid' records and sum of noPaid records.
for a better understanding, here is the result Ι need to get
Output is:
{
out { <-- type field
paid: 100, <-- sum of paid
noPaid: 50 <-- sum of noPaid
},
in: { <-- type field
paid: 20, <-- sum of paid
noPaid: 77 <-- sum of noPaid
}
}
Different solution would be this one. It may give better performance than solution of #YuTing:
db.collection.aggregate([
{
$project: {
type: 1,
paid: {
$filter: {
input: "$orderPayment",
cond: "$$this.paid"
}
},
noPaid: {
$filter: {
input: "$orderPayment",
cond: { $not: "$$this.paid" }
}
}
}
},
{
$set: {
paid: { $sum: "$paid.paymentSum" },
noPaid: { $sum: "$noPaid.paymentSum" }
}
},
{
$group: {
_id: "$type",
paid: { $sum: "$paid" },
noPaid: { $sum: "$noPaid" }
}
}
])
Mongo Playground
use $cond in $group
db.collection.aggregate([
{
"$unwind": "$orderPayment"
},
{
"$group": {
"_id": "$type",
"paid": {
"$sum": {
$cond: {
if: { $eq: [ "$orderPayment.paid", true ] },
then: "$orderPayment.paymentSum",
else: 0
}
}
},
"noPaid": {
"$sum": {
$cond: {
if: { $eq: [ "$orderPayment.paid", false ] },
then: "$orderPayment.paymentSum",
else: 0
}
}
}
}
}
])
mongoplayground
hello I have this function where I want to calculate the number of orders for each status in one array, the code is
let statusEnum = ["pending", "canceled", "completed"];
let userOrders = await Orders.aggregate([
{
$match: {
$or: [
{ senderId: new mongoose.Types.ObjectId(req.user._id) },
{ driverId: new mongoose.Types.ObjectId(req.user._id) },
{ reciverId: new mongoose.Types.ObjectId(req.user._id) },
],
},
},
{
$group: {
_id: null,
totalOrders: { $sum: 1 },
totalPendingOrders: "??", //I want to determine this for each order status
totalCompletedOrders: "??",
totalCanceledOrders: "??",
},
},
]);
so I could add add a $match and use {status : "pending"} but this will filter only the pending orders, I could also map the status enum and replace each element instead of the "pending" above and then push each iteration in another array , but that just seems so messy, is there any other way to calculate total for each order status with using only one aggregate?
thanks
You can use group as you used, but with condition
db.collection.aggregate([
{
$group: {
_id: null,
totalPendingOrders: {
$sum: { $cond: [ { $eq: [ "$status", "pending" ] }, 1, 0 ] }
},
totalCompletedOrders: {
$sum: { $cond: [ { $eq: [ "$status", "completed" ] }, 1, 0 ] }
},
totalCanceledOrders: {
$sum: { $cond: [ { $eq: [ "$status", "canceled" ] }, 1, 0 ] }
}
}
}
])
Working Mongo playground
I'm trying to use $cond to conditionally $push multiple integers onto a numbers array during an aggregate $group without any success. Here is my code:
Item.aggregate(
[
{
$group: {
_id: "$_id",
numbers: {
$push: {
$cond: {
if: { $gt: [ "$price.percent", 70 ] },
then: { $each: [10,25,50,70] },
else: null,
}
}
}
}
},
]
)
...
Is Mongo DB just not set up for this right now, or am I looking at this all wrong?
Please try it without $each as below
Item.aggregate(
[{
$group: {
_id: "$_id",
numbers: {
$push: {
$cond: {
if: { $gt: [ "$price.percent", 70 ] },
then: [10,25,50,70] ,
else: null,
}
}
}
}
}]);
Provided answers will work but they'll add null to the array whenever else block gets executed & at the end you need to filter out the null values from the actual array (numbers) which is an additional step to do!
You can use $$REMOVE to conditionally exclude fields in MongoDB's $project.
Item.aggregate(
[{
$group: {
_id: "$_id",
numbers: { $push: { $cond: [{ $gt: ["$price.percent", 70] }, [10, 25, 50, 70], '$$REMOVE'] } } // With $$REMOVE nothing happens on else
}
}]);
REF: $cond
Have you tried:
Item.aggregate(
[
{
$group: {
_id: "$_id",
numbers: {
$push: {
$each: {
$cond: {
if: { $gt: [ "$price.percent", 70 ] },
then: [10,25,50,70],
else: null
}
}
}
}
}
},
]
)