How to use $match (multiple conditions) and $group in Mongodb - 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

Related

return specific fields from grouped docs

I'm using $group to return the docs based on their level.it is returning a fields array with their docs which are working fine.
I'm trying to return certain fields of the single doc.
current result :
"master": [
{
"_id": "63099a4c00b3c49e75e642e6",
"level": "master",
"uid": "l7AafULWLtP5mpcWKihdo",
"createdAt": "2022-08-27T04:15:08.905Z",
"updatedAt": "2022-08-27T04:15:08.905Z",
"slug": "text-2",
"__v": 0
}
],
Expected result :
"master": [
{
"uid": "l7AafULWLtP5mpcWKihdo",
"slug": "text-2",
}
],
$group:
{
$group: {
_id: "$level",
fields: { $push: "$$ROOT" }
}
},
{
$group: {
_id: null,
fields: {
$push: {
k: "$_id",
v: "$fields"
}
}
}
},
{
$replaceWith: { $arrayToObject: "$fields" }
},
{
$sort: { createdAt: -1 },
},
is this possible to achieve? I'm new to MongoDB any help is appreciated

Mongo DB aggregation query to get an attribute based on another array of objects

I am trying to
get all the ids for which the period.startDate > sysdate
get all the ids for which the period.endDate < sysdate
from the JSON.
[
{
"id": 1,
"period":
[
{
"startDate": "2020-05-05",
"endDate": "2020-05-06"
},
{
"startDate": "2020-06-05",
"endDate": "2020-06-06"
}
]
},
{
"id": 2,
"period":
[
{
"startDate": "2024-07-05",
"endDate": "2024-07-06"
},
{
"startDate": "2024-08-05",
"endDate": "2024-08-06"
}
]
}
]
I have tried to go far as below aggregation:
[{
$project: {
_id: 0,
sId: '$id',
period: 1
} }, {
$unwind: {
path: '$period',
includeArrayIndex: 'index'
} }, {
$group: {
_id: '$sId',
minDate: {
$min: '$periods.startDate'
}
} }, {
$project: {
storeId: '$_id',
_id: 0,
minDated: {
$dateFromString: {
dateString: '$minDate'
}
},
today: ISODate('2022-08-03T11:37:03.954Z')
} }]
One option is using $reduce and $group:
db.collection.aggregate([
{$project: {
_id: 0,
id: 1,
minDate: {
$dateFromString: {
dateString: {
$reduce: {
input: "$period",
initialValue: {$first: "$period.startDate"},
in: {$min: ["$$value", "$$this.startDate"]}
}
}
}
},
maxDate: {
$dateFromString: {
dateString: {
$reduce: {
input: "$period",
initialValue: {$first: "$period.endDate"},
in: {$max: ["$$value", "$$this.startDate"]}
}
}
}
}
}
},
{$group: {
_id: 0,
startDateLargerIds: {
$push: {
$cond: [{$gt: ["$minDate", ISODate("2022-08-03T11:37:03.954Z")]},
"$id", "$$REMOVE"]}
},
endDateSmallerIds: {
$push: {
$cond: [{$lt: ["$maxDate", ISODate("2022-08-03T11:37:03.954Z")]},
"$id", "$$REMOVE"]}
}
}
},
{$unset: "_id"}
])
See how it works on the playground example

Mongodb join multiple group of values

Hello I'm starting to learn mongoDB query
I have some understanding problems with aggregate
For example, I have these documents:
[
{
totalTaxInclusive: 15,
totalTaxExclusive: 12.5,
payments:[{
method: "CB",
amount: 10
},
{
method: "CASH",
amount: 5
}
],
},
{
totalTaxInclusive: 40,
totalTaxExclusive: 33.33,
payments:[{
method: "CB",
amount: 40
}
],
},
]
and so on.
How can I make a request, who I will have a:
$group: {
_id: "$payments.method",
amount: { $sum: "$payments.amount"},
}
and a
$group: {
_id: null,
totalCount: { $sum: 1 },
totalTaxInclusive: { $sum: "$totalTaxInclusive"},
totalTaxExclusive: { $sum:"$totalTaxExclusive" },
}
To have a result of something like:
{
totalCount: 2,
totalTaxInclusive: 55,
totalTaxExclusive: 45.83,
payments: [{
method: "CASH",
amount: 5,
},
{
method: "CB",
amount: 50,
}
]
}
Thanks a lot for your help.
$group by null and get the sum of required fields, construct the array of payments
$concatArrays to concat arrays
$reduce to concat nested array of payments to array
$unwind deconstruct payments array
$group by method and get the sum of amount and get required fields first value
$group by null and construct the array of payments and get count fields first value
db.collection.aggregate([
{
$group: {
_id: null,
totalTaxInclusive: { $sum: "$totalTaxInclusive" },
totalTaxExclusive: { $sum: "$totalTaxExclusive" },
totalCount: { $sum: 1 },
payments: { $push: "$payments" }
}
},
{
$addFields: {
payments: {
$reduce: {
input: "$payments",
initialValue: [],
in: { $concatArrays: ["$$this", "$$value"] }
}
}
}
},
{ $unwind: "$payments" },
{
$group: {
_id: "$payments.method",
amount: { $sum: "$payments.amount" },
totalTaxInclusive: { $first: "$totalTaxInclusive" },
totalTaxExclusive: { $first: "$totalTaxExclusive" },
totalCount: { $first: "$totalCount" }
}
},
{
$group: {
_id: null,
totalTaxInclusive: { $first: "$totalTaxInclusive" },
totalTaxExclusive: { $first: "$totalTaxExclusive" },
totalCount: { $first: "$totalCount" },
payments: {
$push: {
method: "$_id",
amount: "$amount"
}
}
}
}
])
Playground
First stage to group all documents and store their data in relevant arrays. Also count all documents in this stage.
Second stage to sum all items in totalTaxExclusive and totalTaxInclusive arrays
Third and forth stage to unwind payments because it is an array of arrays
Forth stage to group by payment_method and to get total sum by each payment_method
Fifth stage to output result in requested format
db.collection.aggregate([
{
"$group": {
"_id": null,
"totalTaxInclusive": {
"$addToSet": "$totalTaxInclusive"
},
"totalTaxExclusive": {
"$addToSet": "$totalTaxExclusive"
},
"payments": {
"$addToSet": "$payments"
},
"count": {
"$sum": 1
}
}
},
{
"$set": {
"totalTaxExclusive": {
"$sum": "$totalTaxExclusive"
},
"totalTaxInclusive": {
"$sum": "$totalTaxInclusive"
}
}
},
{
"$unwind": "$payments"
},
{
"$unwind": "$payments"
},
{
"$group": {
"_id": "$payments.method",
"amount": {
"$sum": "$payments.amount"
},
"totalTaxInclusive": {
"$first": "$totalTaxInclusive"
},
"totalTaxExclusive": {
"$first": "$totalTaxExclusive"
},
"count": {
"$first": "$count"
}
}
},
{
"$group": {
"_id": null,
"payments": {
"$addToSet": {
"method": "$_id",
"amount": "$amount"
}
},
"totalTaxInclusive": {
"$first": "$totalTaxInclusive"
},
"totalTaxExclusive": {
"$first": "$totalTaxExclusive"
},
"count": {
"$first": "$count"
}
}
}
])
Working example

MongoDB Query - Get frequency map of an array

[
{
"_id": ObjectId("id-1"),
"tests": [
{
"category": "cat1",
"status": "status1",
},
{
"category": "cat1",
"status": "status2",
},
{
"category": "cat2",
"status": "status2",
},
],
},
{
"_id": ObjectId("id-2"),
"tests": [
{
"category": "cat2",
"status": "status1",
},
{
"category": "cat1",
"status": "status1",
},
{
"category": "cat1",
"status": "status2",
},
],
}
]
I have the above collection, my intention is to generate the below result. Please note that the statuses and categories are dynamic.
[
{
"id" : id-1,
"status": {
"status1": count,
"status2": count
},
"category": {
"cat1": count of it,
"cat2": count of it
}
},
{
"id" : id-2,
"status": {
"status1": count of it,
"status2": count of it
},
"category": {
"cat1": count of it,
"cat2": count of it
}
}
]
What I've attempted to do till now, is
Unwinded tests field, then
{
"$group": {
"_id": {
"id": "$_id",
"testStatus": "$tests.status"
},
"val": {
"$sum": 1
}
}
},
{
"$group": {
"_id": {
"id": "$_id.id",
},
"resGroup": {
"$addToSet": {
k: "$_id.testStatus",
v: "$val"
}
}
}
},
{
"$project": {
"_id": "$_id.id",
"statusGroup": {
"$arrayToObject": "$resGroup"
}
}
}
I've done the same for the category field and used $facet to run multiple aggregations.
But, am unable to fetch the result in the required format.
Any help on this will be appreciated.
Thanks
MongoDB Version: 3.4
$map to iterate loop of tests array and convert the object to an array using $objectToArray
$unwind deconstruct tests array
$unwind again deconstruct tests array because it's a nested array
$group by _id, k, and v and get the total count
$group by _id and k and construct the array of the status field in items
$arrayToObject convert items key-value array to an object
$group by _id and construct the array of items
$arrayToObject convert items array to object
db.collection.aggregate([
{
$project: {
tests: {
$map: {
input: "$tests",
in: { $objectToArray: "$$this" }
}
}
}
},
{ $unwind: "$tests" },
{ $unwind: "$tests" },
{
$group: {
_id: {
_id: "$_id",
k: "$tests.k",
v: "$tests.v"
},
count: { $sum: 1 }
}
},
{
$group: {
_id: {
_id: "$_id._id",
k: "$_id.k"
},
items: {
$push: {
k: "$_id.v",
v: "$count"
}
}
}
},
{
$group: {
_id: "$_id._id",
items: {
$push: {
k: "$_id.k",
v: { $arrayToObject: "$items" }
}
}
}
},
{ $project: { items: { $arrayToObject: "$items" } } }
])
Playground

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"},
}
}
]
}}
])