Nested output by group in mongoDB - mongodb

I have a collection like this.
let Movies= [
{year: '2000', language: 'English', genre: 'Romance' , name: 'A beautiful day'},
{year: '2000', language: 'English', genre: 'Action' , name: 'A Dangerous day'},
{year: '2000', language: 'French', genre: 'Romance' , name: 'someromancename'},
{year: '2000', language: 'French', genre: 'Action' , name: 'someactionname'},]
I need to get a output in group format which may look something like this:
{
"2000" : {
"English": {
"Romance": [{
"name":"A beautiful day"
//other fields
}]
"Action": [{
"name":"A dangerous day"
}]
},
"French": {
"Romance": [{
"name":"Some Romance Name"
}]
"Action": [{
"name":"Some Action Name"
}]
},
}
}
I have tried using aggregation but not able to get the exact query as I am new to MongoDB. This is what I tried to do but the expected result is not achieved.
db.getCollection('Movies').aggregate([
{$group: {_id: {"year" : "$year",
"language": "$language" ,
"genre": "$genre" ,
},
"movies": {"$push": "$$ROOT"}
}
},
{$group: {_id: "$_id.year",
"movies": {"$push": {
"language": "$_id.language",
"genre": "$_id.genre" ,
"movies": "$movies"
}}
}
},
])

You can try,
$group by year, language and genre, and make array of movies name in movies
$group by year and language and make array of with genre in k and v format
$group by year and make array of language after converting above genre array to object using $arrayToObject
convert language array to object using $arrayToObject, convert year with languages object to object using $arrayToObject and replace that object to root using $replaceRoot
db.getCollection('Movies').aggregate([
{
$group: {
_id: {
"year": "$year",
"language": "$language",
"genre": "$genre"
},
"movies": { "$push": { name: "$name" } }
}
},
{
$group: {
_id: {
year: "$_id.year",
language: "$_id.language"
},
"movies": {
"$push": {
k: "$_id.genre",
v: "$movies"
}
}
}
},
{
$group: {
_id: "$_id.year",
movies: {
$push: {
k: "$_id.language",
v: { $arrayToObject: "$movies" }
}
}
}
},
{
$replaceRoot: {
newRoot: {
$arrayToObject: [[
{
k: "$_id",
v: { $arrayToObject: "$movies" }
}
]]
}
}
}
])
Playground

Related

Mongodb aggregate group by array elements

I have a mongodb document that contains customer id, status (active, deactivate) and date.
[
{
id:1,
date:ISODate('2022-12-01'),
status:'activate'
},
{
id:2,
date:ISODate('2022-12-01'),
status:'activate'
},
{
id:1,
date:ISODate('2022-12-02'),
status:'deactivate'
},
{
id:2,
date:ISODate('2022-12-21'),
status:'deactivate'
}
]
I need to get daywise customer status count.
I came up with below aggregation.
db.collection.aggregate([
{
$addFields: {
"day": {
"$dateToString": {
"format": "%Y-%m-%d",
"date": "$date"
}
}
}
},
{
$group: {
_id: "$day",
type: {
$push: "$status"
}
}
}
])
this way I can get status in a array. like below.
[
{
_id:"2022-12-01",
type:[
0:"activate",
1:"activate"
]
},
{
_id:"2022-12-02",
type:[
0:"deactivate"
]
},
{
_id:"2022-12-21",
type:[
0:"deactivate"
]
}
]
now it's working as intended. but I need the output like below.
[
{
_id:"2022-12-01",
type:{
"activate":2,
}
},
{
_id:"2022-12-02",
type:{
"deactivate":1
}
},
{
_id:"2022-12-21",
type:{
"deactivate":1
}
}
]
this table has around 100,000 documents and doing this programmatically will take about 10 seconds. that's why I'm searching a way to do this as a aggregation
One option is to group twice and then use $arrayToObject:
db.collection.aggregate([
{$group: {
_id: {day: "$date", status: "$status"},
count: {$sum: 1}
}},
{$group: {
_id: {$dateToString: {format: "%Y-%m-%d", date: "$_id.day"}},
data: {$push: {k: "$_id.status", v: "$count"}}
}},
{$project: {type: {$arrayToObject: "$data"}}}
])
See how it works on the playground example

How to use $match (multiple conditions) and $group in Mongodb

have list of records with the following fields - postBalance, agentId, createdAt, type. I want to filter by “type” and date. After this is done I want to get the $last postBalance for each agent based on the filter and sum up the postBalance. I have been struggling with this using this query
db.transaction.aggregate(
[{ $match: {
$and: [ {
createdAt: { $gte: ISODate('2022-09-15'), $lt:
('2022-09-16') } },
{ type: "CASH_OUT"}]}},
{
$group:
{
_id: {createdAt: {$last: "$createdAt"}},
totalAmount: { $sum: "$postBalance" },
}
}
]
)
An empty array is returned with this query and there are data in the collection.
Below are samples of the documents
{
"_id": {
"$oid": "6334cefd0048787d5535ff16"
},
"type": "CASH_OUT",
"postBalance": {
"$numberDecimal": "23287.625"
},
"createdAt": {
"$date": {
"$numberLong": "1664405245000"
}
},
}
{
"_id": {
"$oid": "6334d438c1ab8a577677cbf3"
},
"userID": {
"$oid": "62f27bc29f51747015fdb941"
},
"aggregatorID": "0000116",
"transactionFee": {
"$numberDecimal": "0.0"
},
"type": "AIRTIME_VTU",
"postBalance": {
"$numberDecimal": "2114.675"
},
"walletHistoryID": 613266,
"walletID": 1720,
"walletActionAt": {
"$date": {
"$numberLong": "1664406584000"
}
},
{
"type": "FUNDS_TRANSFER",
"postBalance": {
"$numberDecimal": "36566.39"
},
"createdAt": {
"$date": {
"$numberLong": "1664407090000"
}
}
}
This is the output I am expecting
{
"date" : 2022-10-09,
"CASHOUT ": 897663,088,
"FUNDS_TRANSFER": 8900877,
"AIRTIME_VTU": 8890000
}
How can my query be aggregated to get this? Thanks
It look like you want something like:
db.collection.aggregate([
{$match: {
createdAt: {
$gte: ISODate("2022-09-15T00:00:00.000Z"),
$lt: ISODate("2022-09-30T00:00:00.000Z")
}
}
},
{$group: {
_id: "$type",
createdAt: {$first: "$createdAt"},
totalAmount: {$sum: "$postBalance"}
}
},
{$group: {
_id: 0,
createdAt: {$first: "$createdAt"},
data: {$push: {k: "$_id", v: "$totalAmount"}}
}
},
{$project: {
data: {$arrayToObject: "$data"},
createdAt: 1,
_id: 0
}
},
{$set: {"data.date": "$createdAt"}},
{$replaceRoot: {newRoot: "$data"}}
])
See how it works on the playground example

What's the best way to using nosql in this situation?

I want to find the most up-to-date data by user in mongodb.
Assuming the data exists as follows:
{
_id: "1",
user_id: "userA",
date: "2022-10-20 11:00:00.000000000"
},
{
_id: "2",
user_id: "userA",
date: "2022-10-25 18:00:00.000000000"
},
{
_id: "3",
user_id: "userB",
date: "2022-10-25 18:00:00.000000000"
},
{
_id: "4",
user_id: "userC",
date: "2022-10-25 18:00:00.000000000"
}
So I want find result like this:
{
_id: "2",
user_id: "userA",
date: "2022-10-25 18:00:00.000000000"
},
{
_id: "3",
user_id: "userB",
date: "2022-10-25 18:00:00.000000000"
},
{
_id: "4",
user_id: "userC",
date: "2022-10-25 18:00:00.000000000"
}
What's the best way to this?
If you're using Mongo version 5.2+ then you can use $group with the new $bottom operator, like so:
db.collection.aggregate([
{
$group: {
_id: "$user_id",
root: {
"$bottom": {
"sortBy": {
"date": 1
},
"output": "$$ROOT"
}
}
}
},
{
$replaceRoot: {
newRoot: "$root"
}
}
])
MongoPlayground
Otherwise there is no real good way of doing this, you'll have to sort the entire collection by date (assuming all dates are saved in a proper format) then group by user id and get the "latest" instance.
db.collection.aggregate([
{
$sort: {
date: -1
}
},
{
$group: {
_id: "$user_id",
root: {
$first: "$$ROOT"
}
}
},
{
$replaceRoot: {
newRoot: "$root"
}
}
])
Mongo Playground

Can we push object value into $project using mongodb

db.setting.aggregate([
{
$match: {
status: true,
deleted_at: 0,
_id: {
$in: [
ObjectId("5c4ee7eea4affa32face874b"),
ObjectId("5ebf891245aa27c290672325")
]
}
}
},
{
$lookup: {
from: "site",
localField: "_id",
foreignField: "admin_id",
as: "data"
}
},
{
$project: {
name: 1,
status: 1,
price: 1,
currency: 1,
numberOfRecord: {
$size: "$data"
}
}
},
{
$sort: {
numberOfRecord: 1
}
}
])
how to push the currency into price object using project please guide thanks a lot, also eager to know what is difference between $addtoSet and $push, what is good option to opt it from project or fix it from $addField
https://mongoplayground.net/p/RiWnnRtksb4
Output should be like this:
[
{
"_id": ObjectId("5ebf891245aa27c290672325"),
"currency": "USD",
"name": "Menz",
"numberOfRecord": 0,
"price": {
"numberDecimal": "20",
"currency": "USD",
},
"status": true
},
{
"_id": ObjectId("5c4ee7eea4affa32face874b"),
"currency": "USD",
"name": "Dave",
"numberOfRecord": 2,
"price": {
"numberDecimal": "10",
"currency": "USD"
},
"status": true
}
]
You can insert a field into an object with project directly, like this (field price):
$project: {
name: 1,
status: 1,
price: {
numberDecimal: "$price.numberDecimal",
currency: "$currency"
},
numberOfRecord: {
$size: "$data"
}
}
By doing it with project, there is no need to use $addField.
For the difference between $addToSet and $push, read this great answer.
You can just set the object structure while projecting, so in this case there's no need for either $push or $addToSet.
{
$project: {
name: "1",
status: 1,
price: {
currency: "$currency",
numberDecimal: "$price.numberDecimal"
},
currency: 1,
numberOfRecord: {
$size: "$data",
}
}
}
Now the difference between $push and $addToSet is pretty trivial and derived from the name, $push saves all items while $addToSet will just create a set of them, for example:
input:
[
//doc1
{
item: 1
},
//doc2
{
item: 2
},
//doc3
{
item: 1
}
]
Now this:
{
$group: {
_id: null,
items: {$push: "$item"}
}
}
Will result in:
{_id: null, items: [1, 2, 1]}
While:
{
$group: {
_id: null,
items: {$addToSet: "$item"}
}
}
Will result in:
{_id: null, items: [1, 2]}

Mongoose query $in with latest document on specific field

I'm trying to query documents in MongoDB using find(), but it's not working to get documents I want. Say we have a documents
Pet = [
{project: "foo", date: 11111, data: "Lion"},
{project: "bar", date: 1111, data: "Tiger"},
{project: "foo", date: 2222, data: "Cat"},
{project: "bee", date: 3333, data: "Rat"},
{project: "pet", date: 4444, data: "Cow"},
{project: "yeti", date: 2233, data: "Dog"}, ...];
Then, I just want to query only 1 document of each project in the array of ["foo", "pet"]. From this sample data, i expect to get
[{project: "foo", date: 11111, data: "Lion"},
{project: "pet", date: 4444, data: "Cow"}]
I try
Pet.find({project: {$in: ["foo","pet"]}, {},{ sort: { date: -1 },limit: 1});
I get only 1 document as I set limit equal to 1. How can I do to get the expected query?
If I understood correctly your question this could help.
Assumption: You have a collection called projects with the fields from your example.
db.projects.aggregate(
[
{ $match: { project: { $in: [ "foo","pet" ] } } },
{ $sort: { project: 1, date: 1 } },
{
$group:
{
_id: "$project",
firstProject: { $first: "$date" }
}
}
]
)
Note: You could play a little more with the $group section of your aggregate to show other records within the group
you have to use aggregation
db.collection.aggregate([
{
"$match": {
"project": {
"$in": [
"pet",
"foo"
]
}
}
},
{
"$group": {
"_id": "$project",
"date": {
"$first": "$date"
},
"data": {
"$first": "$data"
}
}
},
{
"$project": {
"date": 1,
"data": 1
}
},
{
"$sort": {
"date": -1
}
}
])
here is working example : https://mongoplayground.net/p/rIDuXTeHg4i