MongoDb aggregate format output - mongodb

I have the following mongoose aggregate query:
Model.aggregate([
{
$group: {
_id: "$user",
total: {
$sum: {
$toDecimal: "$amount"
}
}
}
}
])
which produces the following output:
[{
"_id": "ABC",
"total": {
"$numberDecimal": "XYZ"
}
}, ...]
Is there any way I could format it to produce slightly more readable results such as
[{
"_id": "ABC",
"total": "XYZ"
}, ...]

Related

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

MongoDB merge $facet results with corresponding items

Hello I have a collection like this
"_id" :"601bd0f4be72d839303adcd3",
"title":"Payment-1",
"initialBalance": {"$numberDecimal": "75"},
"paymentHistory":[
{"_id": "601bd1542df40f2ca8a769df","payment": {"$numberDecimal": "10"}},
{"_id": "601bd1542df40f2ca8a769de","payment": {"$numberDecimal": "20"}},
]
I want to calculate active balance (initialBalance - total of paymentHistory) for each payment.
I calculated total payments from paymentHistory for each document in collection.
this.aggregate([
{$match:{...}},
{
$facet:{
info:[
{$project:{_id:1,title:1,initialBalance:1}}
],
subPayments:[
{$unwind:"$paymentHistory"},
{$group:{_id:"$_id",total:{$sum:"$paymentHistory.payment"}}},
]
}
}
])
I get this result for above query.
"info": [
{
"_id": "601bd0f4be72d839303adcd3",
"title": "Payment-1",
"initialBalance": {"$numberDecimal": "580"},
},
...
],
"subPayments": [
{
"_id": "601bd0f4be72d839303adcd3",
"total": {"$numberDecimal": "80.75"}
},
...
]
I added following lines to aggregation.
{$facet:{...}},
{$project: {payments:{$setUnion:['$info','$subPayments']}}},
{$unwind: '$payments'},
{$replaceRoot: { newRoot: "$payments" }},
Now, I get this result
{
"_id": "601bd0f4be72d839303adcd3",
"total": {"$numberDecimal": "80.75"}
},
{
"_id": "601bd0f4be72d839303adcd3",
"title": "Payment-1",
"initialBalance": {"$numberDecimal": "580"},
},
{...}
I think if I group them via _id, then I calculate activeBalance in $project aggregation.
...
{
$group:{
_id:"$_id",
title:{$first:"$title"},
initialBalance:{$first:"$initialBalance"},
totalPayment:{$first: "$total"},
}
},
{
$set:{activeBalance:{$subtract:[{$ifNull:["$initialBalance",0]}, {$ifNull:["$totalPayment",0]}]}}
}
The problem is after $group aggregation fields return null.
{
"_id": "601bd0f4be72d839303adcd3",
"title": null,
"initialBalance": null,
"totalPayment": {
"$numberDecimal": "80.75"
},
"activeBalance": {
"$numberDecimal": "-80.75"
}
}
How can I solve this problem?
This is what #prasad_ suggested:
db.accounts.aggregate([
{
$addFields: {
activeBalance: {
$subtract: [
"$initialBalance",
{
$reduce: {
input: "$paymentHistory",
initialValue: 0,
in: { $sum: ["$$value", "$$this.payment"] }
}
}
]
}
}
}
]);

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

Get last and minimal values from grouped documents

My document model looks like:
{
"model": "ABC123",
"date": "2018-12-24T23:00:00.000+0000",
"price": "2000" ,
}
I would like to retrive collection to get array of documents:
[
{ "_id" : "ABC123", "newestDate" : ISODate("2018-12-26T23:00:00Z"), "newestPrice" : 2801.33, "lowestPriceAtAll": 1300 },
{ "_id" : "ABC124", "newestDate" : ISODate("2018-12-26T23:00:00Z"), "newestPrice" : 2801.33, "lowestPriceAtAll": 990}
]
where _id is model field, newestPrice is price of newest document (grouped by model) and lowestPriceAtAll is lowest price in all documents with the same model.
I grilled two queries.
First is to find lowest price documents:
offers.aggregate([
{ $sort: { "model": 1, "price": 1 }},
{
$group: {
_id: "$model",
lowestPrice: { "$first": "$price" },
lowestPriceDate: { "$first": "$date"},
}
}
])
the second is to find newest documents:
offers.aggregate([
{ $sort: { "model": 1, "date": -1 }},
{
$group: {
_id: "$model",
newestDate: { "$first": "$date" },
newestPrice: { "$first": "$price"},
}
}
])
Is it possible to merge these two queries into one? (the most important thing is that documents have to be grouped by model field).
you can use $facet
db.offers.aggregate([
{$facet :{
lowest: [
{ $sort: { "model": 1, "price": 1 }},
{
$group: {
_id: "$model",
lowestPrice: { "$first": "$price" },
lowestPriceDate: { "$first": "$date"},
}
}
],
newest: [
{ $sort: { "model": 1, "date": -1 }},
{
$group: {
_id: "$model",
newestDate: { "$first": "$date" },
newestPrice: { "$first": "$price"},
}
}
]
}}
])

Project the size of a set type array in mongodb

I am not able to display the size of an array in projection and groupby. please find my below query.
[
{
"$unwind":"$a"
},
{
$group:{
_id:"$a.acid",
"sessions":{
$sum:1
},
"users":{
$addToSet:"$cid"
}
}
},
{
$project:{
"sessions":1,
"users":1
}
}
]
By the above query I can display all the users, But I want to display the size of the users set(array) only. Could anyone help me on this
You can use $size in mongo like following query:
db.collection.aggregate[{
"$unwind": "$a"
}, {
$group: {
_id: "$a.acid",
"sessions": {
$sum: 1
},
"users": {
$addToSet: "$cid"
}
}
}, {
$project: {
"sessions": 1,
"users": 1,
"size": {
$size: "$users"
}
}
}
])
EDIT AFTER OP's comment that it gives invalid operator error-
You can do something like again unwind users array and count number of users. But this involves again unwinding array so not recommended, The query will be something like following (Not tested):
db.collection.aggregate([{
"$unwind": "$a"
}, {
$group: {
_id: "$a.acid",
"sessions": {
$sum: 1
},
"users": {
$addToSet: "$cid"
}
}
}, {
$unwind: "$users"
}, {
$group: {
_id: "$_id",
"count": {
$sum: 1
},
"sessions": {$push:"$sessions"},// u can use "sessions":{$first:"$sessions"} also.
"users": {
$push: "$users"
}
}
}])