MongoDB count value of attribute and group by month and year - mongodb

I have documents in my mongodb and i try to count every same description i have and classify to month they created.
I want to return an array that include array of objects that includes month number with sub array of description value with the count.
I want to be able to choose which description value to count and choose by what year he will show me the data
for example this is my documents:
[
{
"username": "ron",
"skills": [
{
"rank": "high",
list: [
{
"subject": "Football"
},
{
"subject": "Swim"
}
]
},
{
"rank": "low",
list: [
{
"subject": "Baseball"
},
]
}
],
"duration": 0,
"date": ISODate("2021-07-24T12:05:27.127Z"),
"createdAt": ISODate("2021-07-24T12:05:49.985Z"),
"updatedAt": ISODate("2021-07-24T12:05:49.985Z"),
"__v": 0
},
{
"username": "john",
"skills": [
{
"rank": "low",
list: [
{
"subject": "Football"
},
]
}
],
"duration": 0,
"date": ISODate("2021-07-25T12:05:53.000Z"),
"createdAt": ISODate("2021-07-24T12:05:59.249Z"),
"updatedAt": ISODate("2021-07-24T12:05:59.249Z"),
"__v": 0
},
{
"username": "david",
"skills": [
{
"rank": "high",
list: [
{
"subject": "Football"
},
{
"subject": "Baseball"
}
]
},
{
"rank": "low",
list: [
{
"subject": "Swim"
},
]
}
],
"duration": 0,
"date": ISODate("2021-08-26T12:06:13.000Z"),
"createdAt": ISODate("2021-07-24T12:06:21.328Z"),
"updatedAt": ISODate("2021-07-24T12:06:21.328Z"),
"__v": 0
},
{
"username": "david",
"skills": [
{
"request": "high",
list: [
{
"subject": "Swim"
},
]
},
{
"request": "low",
list: [
{
"subject": "Football"
},
{
"subject": "Baseball"
},
]
}
],
"duration": 0,
"date": ISODate("2021-01-21T13:07:50.000Z"),
"createdAt": ISODate("2021-07-24T12:08:05.552Z"),
"updatedAt": ISODate("2021-07-24T12:14:51.285Z"),
"__v": 0
},
{
"username": "david",
"skills": [
{
"rank": "high",
list: [
{
"subject": "Football"
},
]
},
],
"duration": 0,
"date": ISODate("2022-01-21T13:07:50.000Z"),
"createdAt": ISODate("2022-07-24T12:08:05.552Z"),
"updatedAt": ISODate("2022-07-24T12:14:51.285Z"),
"__v": 0
}
]
The result I expect to get if i want to get match only the description that equal to "Football" or "Baseball":
{
[ {"month_number":7,"result":[{"description":'Football',"count":2},{"description":"Baseball","count":1}]},
{"month_number":8,"result":[{"description":'Football',"count":1}]}]
}
I'm new to mongodb ... so far I have been able to count how many there are of each value and display only the values I want but I do not know how to classify it into months depending on the year I choose.
I tried this:
db.exercises.aggregate([{$match:{ $and:[{description:{$in: ["Baseball","Football"]}}]}} ,{$group:{_id:"$description",count:{$sum:1}}}])
and this
db.exercises.aggregate(
[
{
$project:
{
_id: 0,
year: { $year: "$date" },
month: { $month: "$date" },
description:"$description"
}
},
{
$match:{$and:[{description:{$in: ["Baseball","Football","Swim"]},year:2021}]}
},
{$group:{_id:"$description" , count:{$sum:1}}}
]
)

You need add 1 more grouping i.e. by month.
Working playground
db.collection.aggregate([
{
$project: {
_id: 0,
year: {
$year: "$date"
},
month: {
$month: "$date"
},
description: "$description"
}
},
{
$match: {
$and: [
{
description: {
$in: [
"Baseball",
"Football",
"Swim"
]
},
year: 2021
}
]
}
},
{
$group: {
_id: {
description: "$description",
month: "$month"
},
count: {
$sum: 1
}
}
},
{
$group: {
_id: {
month_number: "$_id.month"
},
results: {
$push: {
description: "$_id.description",
count: "$count"
}
}
}
},
{
$project: {
_id: 0,
month_number: "$_id.month_number",
results: 1
}
}
])

Related

MongoDB Select By Group along with that Count Unique match exclude array and object fields Get data sort by latest objects

I have a collection where from the backend user can input multiple same name bikes but with different registration number but in front-End I want them to be grouped by matching the same name but as user updates separately display image changes but I want only one display image as it is 1 vehicle
provided there is a node created I will implement it we can sort it by the latest and take the price and image of it
Activa -2 Count
KTM -1 Count
but there is a catch.
Activa 2 bikes but I want only count 2 and the price as it is the same in an array I want only 1 and the same applies to displayimage here display image file path is different but I want the latest one only Sharing data below
Data:
[
{
"price": [
{
"Description": "Hourly",
"Price": "1"
},
{
"Description": "Daily",
"Price": "11"
},
{
"Description": "Monthly",
"Price": "111"
}
],
"_id": "62e69ee3edfe4d0f3cb4994a",
"bikename": "KTM",
"bikenumber": "KA05HM2034",
"bikebrand": {
"id": 1,
"label": "Honda"
},
"freekm": 234,
"displayimage": {
"file": "bike-2020-honda-city-exterior-8-1659281111883.jpg",
"file_path": "https://www.example.com/images/upload/bike-2020-honda-city-exterior-8-1659281111883.jpg",
"idx": 1
}
},
{
"price": [
{
"Description": "Hourly",
"Price": "1"
},
{
"Description": "Daily",
"Price": "11"
},
{
"Description": "Monthly",
"Price": "111"
}
],
"_id": "62dba8418ef8f51f454ed757",
"bikename": "Activa",
"bikenumber": "KA05HM2033",
"bikebrand": {
"id": 1,
"label": "Honda"
},
"freekm": 234,
"displayimage": {
"file": "bike-v_activa-i-deluxe-1658562557459.jpg",
"file_path": "https://www.example.com/images/upload/bike-v_activa-i-deluxe-1658562557459.jpg",
"idx": 0
}
},
{
"price": [
{
"Description": "Hourly",
"Price": "1"
},
{
"Description": "Daily",
"Price": "11"
},
{
"Description": "Monthly",
"Price": "111"
}
],
"_id": "62d7ff7e70b9ab38c6ab0cb1",
"bikename": "Activa",
"bikenumber": "KA05HM2223",
"bikebrand": {
"id": 1,
"label": "Honda"
},
"freekm": 234,
"afterfreekmprice": 22,
"descreption": "Activa",
"displayimage": {
"file": "bike-v_activa-i-deluxe-1658322798414.jpg",
"file_path": "https://www.example.com/images/upload/bike-v_activa-i-deluxe-1658322798414.jpg",
"idx": 0
}
}
]
Expected:
[
{
"_id":{
"price": [
{
"Description": "Hourly",
"Price": "1"
},
{
"Description": "Daily",
"Price": "11"
},
{
"Description": "Monthly",
"Price": "111"
}
],
"_id": "62dba8418ef8f51f454ed757",
"bikename": "Activa",
"bikebrand": {
"id": 1,
"label": "Honda"
},
"freekm": 234,
"displayimage": {
"file": "bike-v_activa-i-deluxe-1658562557459.jpg",
"file_path": "https://www.example.com/images/upload/bike-v_activa-i-deluxe-1658562557459.jpg",
"idx": 0
}
},
"count": 2
},
{
"_id":{
"price": [
{
"Description": "Hourly",
"Price": "1"
},
{
"Description": "Daily",
"Price": "11"
},
{
"Description": "Monthly",
"Price": "111"
}
],
"_id": "62e69ee3edfe4d0f3cb4994a",
"bikename": "KTM",
"bikebrand": {
"id": 1,
"label": "Honda"
},
"freekm": 234,
"displayimage": {
"file": "bike-2020-honda-city-exterior-8-1659281111883.jpg",
"file_path": "https://www.example.com/images/upload/bike-2020-honda-city-exterior-8-1659281111883.jpg",
"idx": 1
}
}
"count": 1
}
]
You can use the aggregation pipeline,
$sort by _id in descending order
$group by bikename and get the first root document that is latest one in root and count total documents in count
$project to show required documents
db.collection.aggregate([
{ $sort: { _id: -1 } },
{
$group: {
_id: "$bikename",
root: { $first: "$$ROOT" },
count: { $sum: 1 }
}
},
{
$project: {
_id: "$root",
count: 1
}
}
])
Playground
You can use $group for this:
db.collection.aggregate([
{$group: {
_id: "$bikename",
count: {$sum: 1},
data: {$first: "$$ROOT"}
}
},
{$set: {"data.count": "$count"}},
{$replaceRoot: {newRoot: "$data"}}
])
See how it works on the playground example

Calculate running total across for different groups by day

I'm trying to aggreate a collection of transactions into a running total of owners by day.
The initial collection looks like this:
[
{ "to": "A", "from": "0", "ts": 1 },
{ "to": "A", "from": "0", "ts": 1 },
{ "to": "B", "from": "0", "ts": 1 },
{ "to": "B", "from": "0", "ts": 2 },
{ "to": "C", "from": "0", "ts": 3 },
{ "to": "A", "from": "B", "ts": 4 }
]
What I would like to get is something like this:
[
{
"ts": 1,
"holdings": [
{ "owner": "0", "holdings": -3 },
{ "owner": "A", "holdings": 2 },
{ "owner": "B", "holdings": 1 }
]
},
{
"ts": 2,
"holdings": [
{ "owner": "0", "holdings": -4 },
{ "owner": "A", "holdings": 2 },
{ "owner": "B", "holdings": 2 }
]
},
{
"ts": 4,
"holdings": [
{ "owner": "0", "holdings": -5 },
{ "owner": "A", "holdings": 3 },
{ "owner": "B", "holdings": 1 },
{ "owner": "C", "holdings": 1 }
]
}
]
I've already understood how to generate this for a single ts that I'm setting, but I don't know how to do it across all ts.
The aggregation pipeline for a single ts looks like this:
db.collection.aggregate([
// start with: { "to": "A", "from": "0", "ts": 1 }
{
// create a doc with an array with subset of fields:
// { "_id": ObjectId("5a934e000102030405000000"),
// "data": [ { "change": 1, "owner": "A", "ts": "1" },
// { "change": -1, "owner": "0", "ts": "1" } ] }
$project: {
data: [
{
owner: '$to',
ts: '$ts',
change: 1,
},
{
owner: '$from',
ts: '$ts',
change: -1,
},
],
},
},
{
// unwind the array into 2 docs:
// { "_id": ObjectId("5a934e000102030405000000"), "data": { "change": 1, "owner": "A", "ts": "1" } },
// { "_id": ObjectId("5a934e000102030405000000"), "data": { "change": -1, "owner": "0", "ts": "1" } },
$unwind: '$data',
},
{
// use data as root:
// { "data": { "change": 1, "owner": "A", "ts": "1" } },
// { "data": { "change": -1, "owner": "0", "ts": "1" } }
$replaceRoot: {
newRoot: '$data',
},
},
{
// select day to calc totals
$match: {
ts: {
$lt: 6,
},
},
},
{
// sum totals, grouped by owner
$group: {
_id: '$owner',
//_id: null,
holdings: {
$sum: '$change',
},
},
},
])
This gives the correct result for a particular day (selected in the match stage). I don't understand how I can now generalize that to all days.
One way to do it is using $setWindowFields, which has a built-in accumulation:
db.collection.aggregate([
{
$project: {
ts: "$ts",
data: [{owner: "$to", change: 1}, {owner: "$from", change: -1}]
}
},
{$unwind: "$data"},
{
$group: {
_id: {ts: "$ts", owner: "$data.owner"},
holdings: {$sum: "$data.change"}
}
},
{
$setWindowFields: {
partitionBy: "$_id.owner",
sortBy: {"_id.ts": 1},
output: {
cumulativeHoldings: {
$sum: "$holdings",
window: {documents: ["unbounded", "current"]}
}
}
}
},
{
$group: {
_id: "$_id.ts",
holdings: {$push: {owner: "$_id.owner", holdings: "$cumulativeHoldings"}}
}
}
])
Playground

Adding a nested value as a field - MongDB aggregation

So I have a parent document with users, as well as an array that has users too. I want to add the DisplayName from the nested users array to the aggregation output. Any ideas?
Output I'm looking to achieve:
[
{
"user": {
"_id": "11",
"Name": "Dave",
"DocID": "1",
"DocDisplyName": "ABC"
},
{
"user": {
"_id": "33",
"Name": "Henry",
"DocID": "1",
"DocDisplyName": "ABC",
"BranchDisplayName:"BranchA"
}
}
]
And so on.. So an array of all users and for users that belong to a branch, add the branch display Name to the output.
// Doc 1
{
"_id": "1",
"DisplayName": "ABC",
"Users": [
{ "_id": "11", "Name": "Dave" },
{ "_id": "22", "Name": "Steve" }
],
"Branches": [
{
"_id": "111",
"DisplayName": "BranchA",
"Users": [
{ "_id": "33", "Name": "Henry" },
{ "_id": "44", "Name": "Josh" },
],
},
{
"_id": "222",
"DisplayName": "BranchB",
"Users": [
{ "_id": "55", "Name": "Mark" },
{ "_id": "66", "Name": "Anton" },
],
}
]
}
``Doc 2
{
"_id": "2",
"DisplayName": "DEF",
"Users": [
{ "_id": "77", "Name": "Josh" },
{ "_id": "88", "Name": "Steve" }
],
"Branches": [
{
"_id": "333",
"DisplayName": "BranchA",
"Users": [
{ "_id": "99", "Name": "Henry" },
{ "_id": "10", "Name": "Josh" },
],
},
{
"_id": "444",
"DisplayName": "BranchB",
"Users": [
{ "_id": "112", "Name": "Susan" },
{ "_id": "112", "Name": "Mary" },
],
}
]
}
Collection.aggregate([
{
$addFields: {
branchUsers: {
$reduce: {
input: "$Branches.Users",
initialValue: [],
in: {
$concatArrays: ["$$this", "$$value"],
},
},
},
},
},
{
$addFields: {
user: {
$concatArrays: ["$branchUsers", "$Users"],
},
},
},
{
$addFields: {
"user.DocID": "$_id","user.DocDisaplyName": "$DisplayName"
},
},
{
$unwind: "$user",
},
{
$project: {
_id: 0,
user: 1,
},
}
])
Thanks in advance!
OK I found a solution.
{
$addFields: {
"branchUsers.BranchDisplayName": {
$let: {
vars: {
first: {
$arrayElemAt: [ "$Branches", 0 ]
}
},
in: "$$first.DisplayName"
}
}
}
},
This creates the field only for the users that belong to the branch

Need to return matched data from mongo db JSON

I have Json which have values like state_city details this contains information like which city belongs to which state -
Need to query it for particular state name which will gives me all cities that belongs to that state.
db.collection.find({
"count": 10,
"state.name": "MP"
})
[
{
"collection": "collection1",
"count": 10,
"state": [
{
"name": "MH",
"city": "Mumbai"
},
{
"name": "MH",
"city": "Pune"
},
{
"name": "UP",
"city": "Kanpur"
},
{
"name": "CG",
"city": "Raipur"
}
]
},
{
"collection": "collection2",
"count": 20,
"state": [
{
"name": "MP",
"city": "Indore"
},
{
"name": "MH",
"city": "Bhopal"
},
{
"name": "UP",
"city": "Kanpur"
},
{
"name": "CG",
"city": "Raipur"
}
]
}
]
You have to use aggregate query to get only matching elements in array :
db.collection.aggregate([{
$unwind: "$content.state"
},
{
$match: {
"content.state.name": "MH",
"count": 10
}
},
{
$group: {
_id: "$content.state.city",
}
},
{
$addFields: {
key: 1
}
},
{
$group: {
_id: "$key",
cities: {
$push: "$_id"
}
}
},
{
$project: {
_id: 0,
cities: 1
}
}
])
This query will return :
{
"cities": [
"Pune",
"Mumbai"
]
}
The following query would be the solution.
db.collection.find({ "count": 10, "state":{"name": "MP"}})
For more complex queries, $elemMatch is also available.

MongoDB aggregate results into nested array

I'm quite new to MongoDB and I am currently facing a situation. Below are 2 sample records from the whole database that I have :
{
"_id": 1,
"Record": 1,
"Link": [ "https://wikileaks.org/plusd/cables/1979PANAMA06344_e.html" ],
"Location": [ "USA", "PAN", "USA", "USA", "PAN" ],
"Organization": [ "GN", "SOUTHCOM", "UCMJ", "PRC" ],
"Date": [ "2016" ],
"People": [ "P.Walter" ]
}
{
"_id": 2,
"Record": 2,
"Link": [ "https://wikileaks.org/gifiles/docs/11/111533_-latam-centam-brief-110822-.html" ],
"Location": [ "NIC", "GTM", "JAM", "GTM", "PAN" ],
"Organization": [ "CENTAM", "Calibre Mining Corporation", "STRATFOR", "Alder Resources" ],
"Date": [ "2013" ],
"People": [ "Daniel Ortega", "Hugo Chavez", "Paulo Gregoire" ]
}
Basically, I'm trying to get an output like this :
{
"Country": "US",
"Years": [
{
"Year": "2016",
"Links": [ "https://wikileaks.org/gifiles/docs/11/111533_-latam-centam-brief-110822-.html",
"https://wikileaks.org/plusd/cables/1979PANAMA06344_e.html",
"https://wikileaks.org/gifiles/docs/90/9058_wax-12312008-csv-.html" ]
},
{
"Year": "2013",
"Links": [ ""https://wikileaks.org/gifiles/docs/11/111533_-latam-centam-brief-110822-.html",
"https://wikileaks.org/plusd/cables/1979PANAMA06344_e.html",
"https://wikileaks.org/gifiles/docs/90/9058_wax-12312008-csv-.html" ]
}
]
"Link_Count": 6
}
{
"Country": "UK",
"Years": [
{
"Year": "2009",
"Links": [ "https://wikileaks.org/gifiles/docs/11/111533_-latam-centam-brief-110822-.html",
"https://wikileaks.org/plusd/cables/1979PANAMA06344_e.html",
"https://wikileaks.org/gifiles/docs/90/9058_wax-12312008-csv-.html" ]
},
{
"Year": "2011",
"Links": [ ""https://wikileaks.org/gifiles/docs/11/111533_-latam-centam-brief-110822-.html",
"https://wikileaks.org/plusd/cables/1979PANAMA06344_e.html"]
}
]
"Link_Count": 5
}
I've tried to aggregate it, but I couldn't achieve what I want like I've given in the output. Here's my query :
db.test.aggregate([
{
"$unwind": "$Location"
},
{
"$group" : {
"_id": {
"Country": "$Location",
"Year": "$Date",
"Links": "$Link"
},
Loc: {
$addToSet: "$Location"
}
}
},
{
"$unwind": "$Loc"
},
{
"$group": {
"_id": "$Loc",
"Years": { "$push": {
"Year": "$_id.Year",
"Links": "$_id.Links"
}
}
}
}
]).toArray()
I used $unwind and $addToSet on $Location because there are duplicates found within $Location. I'm open to any suggestions or solution so please do tell! Thanks in advance!
You can use :
db.test.aggregate([{
"$unwind": "$Location"
}, {
"$unwind": "$Date"
}, {
"$unwind": "$Link"
}, {
"$group": {
"_id": {
"Country": "$Location",
"Year": "$Date"
},
Links: {
$addToSet: "$Link"
}
}
}, {
"$group": {
"_id": "$_id.Country",
Years: {
$push: {
"Year": "$_id.Year",
"Links": "$Links"
}
},
Link_Count: { $sum: { $size: "$Links" } }
}
}])
The idea is to $unwind all arrays to be able to $push link into a new array, and to count the grouped record with $size for the last $group stage.