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,
$matchcDateby$regexand match"2021"year$reduceto iterate loop ofcreditsarray, set initial value to 0$substrto get substring of thecDatefrom 0 index and 4 character that is year$condto check is substring is"2021"then$sumthe initial value withcAmountotherwise 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"
]
}
}
}
}
}
])
