I have a Customer collection with the following document:
{
"_id": 1,
firstname: "John",
lastname: "Doe",
credits: [
{
cDate: "2020-01-16",
cAmount: 350
},
{
cDate: "2021-02-07",
cAmount: 180
},
{
cDate: "2021-06-25",
cAmount: 650
},
]
}
{
"_id": 2,
firstname: "Bob",
lastname: "Smith",
credits: [
{
cDate: "2020-03-19",
cAmount: 200
},
{
cDate: "2020-08-20",
cAmount: 90
},
{
cDate: "2021-11-11",
cAmount: 300
},
]
}
Now I would like to return the total spent for a specific year i.e. 2021.
The data should look something like this:
{"firstname": "John", "lastname": "Doe", "total": 830},
{"firstname": "Bob", "lastname": "Smith", "total": 300}
First I tried to match the records that contain cDates
within the expected year (2021) to reduce the number of records (the actual dataset has hundreds of customers) and then projected the wanted fields:
Customer.aggregate([
{
$match: {
credits: {
$elemMatch: {
cDate: {
$gte: ISODate("2021-01-01"),
$lte: ISODate("2021-12-31"),
},
},
},
},
},
{
$project: {
_id: 0,
firstname: 1,
lastname: 1,
total: {
$sum: "$credits.cAmount",
},
},
}
])
the result is:
{"firstname": "John", "lastname": "Doe", "total": 1180},
{"firstname": "Bob", "lastname": "Smith", "total": 590}
Almost there, now I'd like to skip the credit records that do not contain the expected year (2021), so that only the values with a cDate
equal to 2021 are calculated.
The $match
I kept the same and I tried to add a $cond
in the $project
bit.
Customer.aggregate([
{
$match: {
credits: {
$elemMatch: {
cDate: {
$gte: ISODate("2021-01-01"),
$lte: ISODate("2021-12-31"),
},
},
},
},
},
{
$project: {
_id: 0,
firstname: 1,
lastname: 1,
total: {
$cond: {
if: { credits: { cDate: { regex: "2021-" } } }, // if cDate contains 2021-
then: { $sum: "$credits.cAmount" }, // add the cAmount
else: { $sum: 0 } // else add 0
},
},
},
}
])
This results is still the same, all totals get calulated from all years.
{"firstname": "John", "lastname": "Doe", "total": 1180},
{"firstname": "Bob", "lastname": "Smith", "total": 590}
What am I missing?
Thanks for your help.
CodePudding user response:
Property cDate
has string value, you can not match by date type,
$match
cDate
by$regex
and match"2021"
year$reduce
to iterate loop ofcredits
array, set initial value to 0$substr
to get substring of thecDate
from 0 index and 4 character that is year$cond
to check is substring is"2021"
then$sum
the initial value withcAmount
otherwise return initial value
Customer.aggregate([
{
$match: {
"credits.cDate": {
$regex: "2021"
}
}
},
{
$project: {
_id: 0,
firstname: 1,
lastname: 1,
total: {
$reduce: {
input: "$credits",
initialValue: 0,
in: {
$cond: [
{
$eq: [
{ $substr: ["$$this.cDate", 0, 4] },
"2021"
]
},
{ $sum: ["$$value", "$$this.cAmount"] },
"$$value"
]
}
}
}
}
}
])