Mongodb: how to sum at breaks in 2 fields? - mongodb

I have a document sample as follows:
{
_id: ObjectId("5dfa4f7b9254a519a5c7f166"),
Date: "12/11/19",
Description: "Amazon",
Amount: 32.01,
DebitCredit: "debit",
Category: "Shopping",
Month: 12,
Day: 11,
Year: 2019,
Account_Name: "Gold Delta SkyMiles"
}...
This is my query:
db.checks.aggregate([
{ $match: { Category: "Shopping" } },
{ $project: { Year: 1, Month: 1, Category: 1, Amount: { $sum: "$Amount" } } },
{
$group: {
_id: {
Year: "$Year",
Month: "$Month",
Category: "$Category",
Amount: { $sum: "$Amount" }
}
}
},
{ $sort: { Year: 1, Month: 1 } }
]);
I'm looking for a total by each year / month combination. How can I achieve this?

You're almost there, you just need to tweak your $group stage a little:
From the docs:
Groups input documents by the specified _id expression and for each distinct grouping
So all we have to do is take out the amount field outside of the _id field.
{
$group: {
_id: {
Year: "$Year",
Month: "$Month",
},
Amount: { $sum: "$Amount" }
}
}
I removed category as it was redundant but feel free to add it back to your query.
EDIT:
For the $sort stage the fields Year & Month do not exists after the $group which is the reason its failing.
After the $group stage your documents are of the form:
{
_id: {Year: number, Month: number},
Amount: number
}
So just change your $sort into:
{ $sort: { '_id.Year': 1, '_id.Month': 1 } }

Related

How to calculate average records per month?

My records like this [{ createdAt }, {createdAt}, {createdAt} ]
I need average records per month.
january => 3 records
february => 2 records etc..
You can try to $group by month and year when counting and by month when averaging:
db.collection.aggregate([
{
$group: {
_id: {
month: {
$month: "$createdAt"
},
year: {
$year: "$createdAt"
},
},
count: {
$sum: 1
}
}
},
{
$group: {
_id: {
month: "$_id.month"
},
average: {
$avg: "$count"
}
}
},
{
$project: {
_id: 0,
month: "$_id.month",
average: 1
}
}
])
Link to playground
Not fully clear what you mean by "average records per month" but I think it would be this:
db.collection.aggregate([
{
$group: {
_id: {
$dateTrunc: {
date: "$createdAt",
unit: "month"
}
},
count: { $count: {} }
}
},
{
$group: {
_id: null,
data: { $push: { k: { $toString: { $month: "$_id" } }, v: "$count" } }
}
},
{ $replaceWith: { $arrayToObject: "$data" } }
])
Getting the month name is not so easy, either you use a external library or build your own with $switch

How can I find out how many products were sold in each month?

I have a collection with products with structure like this:
{ _id: 01,
user_id: 10,
line_items: [
{
_id: 2,
quantity: 2,
},
{
_id: 3,
quantity: 1,
}
],
purchase_date: 2021-02-05T21:00:00.000+00:00
}
How can I find out how many products were sold in each month?
To find out how many line items were sold each month, you need to run an aggregation where the pipeline consists of a $group stage. The group by key will be the month value returned by the $month operator on the purchase_date field. The count will consist of the $sum operator on another $sum of the array of quantities returned by the expression "$line_items.quantity" which essentially is interpreted as in the above document
{ $sum: [2, 1] } => 3
So your overall pipeline follows:
db.collection.aggregate([
{ $group: {
_id: {
"$month": "$purchase_date"
},
total: {
$sum: {
$sum: "$line_items.quantity"
}
}
} }
])
Mongo Playground
Option 1 The easiest and faster is this:
db.collection.aggregate([
{
$group: {
_id: {
"$substr": [
"$purchase_date",
0,
7
]
},
count: {
$sum: 1
}
}
}
])
Explained:
Group by the first 7 characters that include year and month: "2021-12" and count the products.
playground1
Option 2: Convert string to date/month:
db.collection.aggregate([
{
$group: {
_id: {
$month: {
$dateFromString: {
dateString: "$purchase_date"
}
}
},
count: {
$sum: 1
}
}
}
])
Explained:
Convert the string to month and group
playground2

How do I get the earliest record by date for a unique constraint?

I have a collection with two fields that are important to this question: a category and a date (I'll just use "yesterday" and "today" for simplicity).
// Sample Data
{ category: 1, date: ISODate("yesterday") }
{ category: 1, date: ISODate("today") }
{ category: 2, date: ISODate("yesterday") }
{ category: 2, date: ISODate("today") }
{ category: 3, date: ISODate("yesterday") }
Now, I'm modifying a query that would first query on the category then sort by date descending and limit to 1 result (giving me the earliest record by date for a single category.
db.collection("things").find({ category: 1 }).sort({ date: -1 }).limit(1)
// Result
{ category: 1, date: ISODate("today") }
That worked great. However, I want the earliest record by for EVERY category. This means I can't use the limit operator (because I don't know how many categories there are at any given point in time) and I'm not guaranteed to have the date a record for every day for each category.
// Expected Result
{ category: 1, date: ISODate("today") }
{ category: 2, date: ISODate("today") }
{ category: 3, date: ISODate("yesterday") }
I've played with distinct, to no success.
How can I get the record with the earliest date for each category in a single query?
You can $group by category and use $max to get earliest date:
db.things.aggregate([
{
$group: {
_id: "$category",
date: { $max: "$date" }
}
},
{
$project: {
_id: 0,
category: "$_id",
date: 1
}
}
])
You can try below aggregation
You can first $sort with date then $group and take the $first document with the date field.
db.collection.aggregate([
{ "$sort": { "date": 1 } },
{ "$group": {
"_id": "$category",
"date": { "$first": "$date" }
}},
{ "$project": { "_id": 0, "category": "$_id", "date": 1 }}
])

Mongodb need count for specific member id per day

Collection name : activity
What I need is activity count
of "memberId" = 123
where activity "type" = 'xxx'
per day
between "11/01/2015" and "11/15/2015" // from date and to date range
Expected Output:
[
{date:"2015-02-22",count:10},
{date:"2015-02-22",count:5},
]
I have no idea how to perform aggregate between dates and for a specific member id
where I am at is far far away from the solution :
db.activity.aggregate(
{ $project: {
date: {
years: {$year: '$dateInserted'},
months: {$month: '$dateInserted'},
days: {$dayOfMonth: '$dateInserted'},
},
memberId: '$memberId'
}},
{ $group: {
_id: { memberId: '$memberId', date: '$date' },
number: { $sum: 1}
}})
db.activity.aggregate([
{$match : { memberId : "xxx",item:"xyz",dateInserted: {$gte: ISODate("2013-01-01T00:00:00.0Z"), $lt: ISODate("2016-02-01T00:00:00.0Z")}}},
{$project: {day: {day: {$dayOfMonth: '$dateInserted'}, month: {$month: '$dateInserted'}, year: {$year: '$dateInserted'}}}},
{$group: { _id: { day: '$day' }, count: { $sum: 1} }},{ $sort:{_id:1}}
]);
Try that
You may need add $match stage before $project:
$match:
{dateInserted:{{$gte:new Date("2015-01-11T00:00:00 -02:00"),{$lte:new Date("2015-01-15T23:59:59 -02:00")}}},
{memberId:123},
{type:"xxx"}

Grouping by two fields in MongoDB?

I am attempting to get a count of records created on each day for the last 15 days. I came up with the query below:
db.users.aggregate(
{ $group: {
_id: {$dayOfYear: '$created'},
created: {$sum:1},
date: {$first: '$created'}
}
},
{$sort: {_id: 1}},
{$limit: 15}
);
Which almost works, but it will also count days from exactly a year ago. So if 2 records were created on 2/20/2014 and 3 were created on 2/20/2013 then a count of 5 would be returned (when all I want is 2). I was hoping to also group by the year like below:
db.users.aggregate(
{ $group: {
_id: {{$dayOfYear: '$created'},{$year: '$created'}},
created: {$sum:1},
date: {$first: '$created'}
}
},
{$sort: {_id: 1}},
{$limit: 15}
);
But this is a syntax error. How should I be attempting to count records created on a specific day?
db.users.aggregate(
{ $group: {
_id: {
day: { $dayOfYear: '$created' },
year: { $year: '$created' }
},
count: { $sum: 1 },
date: { $first: '$created' }
}
},
{ $project: { _id: 0, date: 1, count: 1 } }, // Only for more clear result.
{ $sort: { date: -1 } }, // If you want to view last 15 days - sort by descending.
{ $limit: 15 }
);