How to sum the balance and income using MongoDB? - mongodb

I have a document exchange_history like:
[
{
"_id": {"$oid": "611fd7059ec255088af94e4d"},
"amount": {"$numberDecimal": 1.2},
"exchange_time": {"$date": "2021-08-20T16:02:00.448Z"},
"user_id": "60ef435299b1760752cbafe1"
},
{
"_id": {"$oid": "611fed2a31ef85394c4f9b10"},
"amount": {"$numberDecimal": 1.02},
"exchange_time": {"$date": "2021-08-20T16:02:00.448Z"},
"user_id": "60ef435299b1760752cbafe1"
},
{
"_id": {"$oid": "6120b47d4cf35f7f70312a71"},
"amount": {"$numberDecimal": -1},
"exchange_time": {"$date": "2021-08-21T08:08:16.397Z"},
"user_id": "60ef435299b1760752cbafe1"
},
{
"_id": {"$oid": "6120b4834cf35f7f70312a72"},
"amount": {"$numberDecimal": -2},
"exchange_time": {"$date": "2021-08-21T08:08:16.397Z"},
"user_id": "60ef435299b1760752cbafe1"
},
{
"_id": {"$oid": "6120b4e94cf35f7f70312a73"},
"amount": {"$numberDecimal": 10},
"exchange_time": {"$date": "2021-08-21T08:08:16.397Z"},
"user_id": "611df4d9ece358586d212d91"
}
]
How to calculate the balance (sum amount) and income (sum amount where amount is greater than 0) by user_id at the same time using MongoDB?
My MongoDB version is 4.4. Pls help me!

Try this ...
Document :
[
{
"_id": {
"oid": "611fd7059ec255088af94e4d"
},
"amount": {
"numberDecimal": 1.2
},
"exchange_time": {
"date": "2021-08-20T16:02:00.448Z"
},
"user_id": "60ef435299b1760752cbafe1"
},
{
"_id": {
"oid": "611fed2a31ef85394c4f9b10"
},
"amount": {
"numberDecimal": 1.02
},
"exchange_time": {
"date": "2021-08-20T16:02:00.448Z"
},
"user_id": "60ef435299b1760752cbafe1"
},
{
"_id": {
"oid": "6120b47d4cf35f7f70312a71"
},
"amount": {
"numberDecimal": -1
},
"exchange_time": {
"date": "2021-08-21T08:08:16.397Z"
},
"user_id": "60ef435299b1760752cbafe1"
},
{
"_id": {
"oid": "6120b4834cf35f7f70312a72"
},
"amount": {
"numberDecimal": -2
},
"exchange_time": {
"date": "2021-08-21T08:08:16.397Z"
},
"user_id": "60ef435299b1760752cbafe1"
},
{
"_id": {
"oid": "6120b4e94cf35f7f70312a73"
},
"amount": {
"numberDecimal": 10
},
"exchange_time": {
"date": "2021-08-21T08:08:16.397Z"
},
"user_id": "611df4d9ece358586d212d91"
}
]
Code :
db.collection.aggregate([
{
$group: {
_id: "$user_id",
balance: {
$sum: "$amount.numberDecimal"
},
income: {
$sum: {
$cond: [
{
$gt: [
"$amount.numberDecimal",
0
]
},
"$amount.numberDecimal",
0
]
}
}
}
},
])
And Output:
[
{
"_id": "611df4d9ece358586d212d91",
"balance": 10,
"income": 10
},
{
"_id": "60ef435299b1760752cbafe1",
"balance": -0.78,
"income": 2.2199999999999998
}
]
Try mongoplayground

Related

Aggregate occurrences of events in nested array

Given the following input:
[
{
"statuses": [
{
"status": "allowed",
"count": 3,
"events_count": [
"2001",
"1001",
"1001"
]
}
],
"date": "2022-09-10 15:00",
"_id": "2022-09-10 15:00"
}
]
I need count the number of occurrences of stauses.events_count, so the output would be:
[
{
"statuses": [
{
"status": "allowed",
"count": 3,
"events_count": [
{"type": "2001", "count": 1},
{"type": "1001", "count": 2},
]
}
],
"date": "2022-09-10 15:00",
"_id": "2022-09-10 15:00"
}
]
What I've tried
This is what I got so far:
db.collection.aggregate([
{
"$unwind": "$statuses"
},
{
"$unwind": "$statuses.events_count"
},
{
"$group": {
"_id": {
"event_count": "$statuses.events_count",
"status": "$statuses.status",
"date": "$date",
"count": "$statuses.count"
},
"occurences": {
"$sum": 1
}
}
}
])
Which produces:
[
{
"_id": {
"count": 3,
"date": "2022-09-10 15:00",
"event_count": "2001",
"status": "allowed"
},
"occurences": 1
},
{
"_id": {
"count": 3,
"date": "2022-09-10 15:00",
"event_count": "1001",
"status": "allowed"
},
"occurences": 2
}
]
I'm having difficulties grouping everything back together. I tried grouping by date and pushing back to a 'statuses' array, but it produces two items in the array (with status==allowed), rather than 1 item with status==allowed
You did 2 $unwinds, so it should be 2 $groups in reverse order:
{
"$group": {
"_id": {
"status": "$_id.status",
"count": "$_id.count",
"date": "$_id.date"
},
"event_count": {
"$push": {
"type": "$_id.event_count",
"count": "$occurences"
}
}
}
},
{
"$group": {
"_id": "$_id.date",
"date": {"$last": "$_id.date"},
"statuses": {
"$push": {
"status": "$_id.status",
"count": "$_id.count",
"event_count": "$event_count"
}
}
}
}

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

How to add a property as an object in grouped array (mongodb aggregation)?

I'm new in mongoose and stuck with some problem. I have 2 instances: Patient and Appointment. They look like:
const AppointmentSchema = new mongoose.Schema({
patient: { type: Schema.Types.ObjectId, ref: 'Patient' },
dentNumber: Number,
diagnosis: String,
price: Number,
date: String,
time: String
}, {
timestamps: true
});
const PatientSchema = new mongoose.Schema({
fullname: String,
phone: String,
}, {
timestamps: true
});
PatientSchema.virtual('Appointments', {
ref: 'Appointment',
localField: '_id',
foreignField: 'patient',
justOne: false
})
Appointments data looks like this:
{
"success": true,
"data": [
{
"_id": "61cb1cad610963c75f7e41a2",
"patient": {
"_id": "61c045bcb0c7e68c96e6ba0e",
"fullname": "test 126",
"phone": "+7 123 54 55",
"createdAt": "2021-12-20T08:58:36.776Z",
"updatedAt": "2021-12-20T08:58:36.776Z",
"__v": 0
},
"dentNumber": 33,
"diagnosis": "String3",
"price": 500,
"date": "18-10-2021",
"time": "15:50",
"createdAt": "2021-12-28T14:18:21.537Z",
"updatedAt": "2021-12-28T14:24:29.576Z",
"__v": 0
},
{
"_id": "61cc26bd7d1e9476e52c4b35",
"patient": {
"_id": "61c045bcb0c7e68c96e6ba0e",
"fullname": "test 126",
"phone": "+7 123 54 55",
"createdAt": "2021-12-20T08:58:36.776Z",
"updatedAt": "2021-12-20T08:58:36.776Z",
"__v": 0
},
"dentNumber": 4,
"diagnosis": "String3",
"price": 1600,
"date": "17-10-2021",
"time": "16:50",
"createdAt": "2021-12-29T09:13:33.575Z",
"updatedAt": "2021-12-29T09:13:33.575Z",
"__v": 0
},
{
"_id": "61cc3663970d00f9129d0456",
"patient": {
"_id": "61c045bcb0c7e68c96e6ba0e",
"fullname": "test 126",
"phone": "+7 123 54 55",
"createdAt": "2021-12-20T08:58:36.776Z",
"updatedAt": "2021-12-20T08:58:36.776Z",
"__v": 0
},
"dentNumber": 5,
"diagnosis": "String4",
"price": 1700,
"date": "18-10-2021",
"time": "17:50",
"createdAt": "2021-12-29T10:20:19.257Z",
"updatedAt": "2021-12-29T10:20:19.257Z",
"__v": 0
},
]
}
I need to sort Appointments by date in this way:
await Appointment
.aggregate([
{ $group: {
_id: "$date",
data: { $push: {
id: "$_id",
patientId: "$patient",
//patient: { patient: await Patient.findById(mongoose.Types.ObjectId.fromString("$patient")) },
dentNumber: "$dentNumber",
diagnosis: "$diagnosis",
price: "$price",
date: "$date",
time: "$time"
} }
} },
{ $sort: { _id: 1 } },
])
After this I get an answer in proper form:
{
"success": true,
"data": [
{
"_id": "17-10-2021",
"data": [
{
"id": "61cb1cad610963c75f7e41a2",
"patientId": "61c045bcb0c7e68c96e6ba0e",
"dentNumber": 33,
"diagnosis": "String3",
"price": 500,
"date": "17-10-2021",
"time": "15:50"
},
{
"id": "61d1651b25ab055c5cc913ac",
"patientId": "61be3e6b51b968aa92d5e248",
"dentNumber": 5,
"diagnosis": "String4",
"price": 1750,
"date": "17-10-2021",
"time": "19:35"
}
]
},
{
"_id": "18-10-2021",
"data": [
{
"id": "61cc26bd7d1e9476e52c4b35",
"patientId": "61c045bcb0c7e68c96e6ba0e",
"dentNumber": 4,
"diagnosis": "String3",
"price": 1600,
"date": "18-10-2021",
"time": "16:50"
},
]
},
But instead of property "patientId" I need somehow get a full object Patient. To make the data look like this:
{
"success": true,
"data": [
{
"_id": "17-10-2021",
"data": [
{
"id": "61cb1cad610963c75f7e41a2",
"dentNumber": 33,
"diagnosis": "String3",
"price": 500,
"date": "17-10-2021",
"time": "15:50",
"patient": {
"_id": "61c045bcb0c7e68c96e6ba0e",
"fullname": "test 126",
"phone": "+7 123 54 55",
"createdAt": "2021-12-20T08:58:36.776Z",
"updatedAt": "2021-12-20T08:58:36.776Z",
"__v": 0
}
},
{
"id": "61d1651b25ab055c5cc913ac",
"dentNumber": 5,
"diagnosis": "String4",
"price": 1750,
"date": "17-10-2021",
"time": "19:35",
"patient": {
"_id": "61be3e6b51b968aa92d5e248",
"fullname": "test 421",
"phone": "+7 123-11-22",
"createdAt": "2021-12-18T20:02:51.177Z",
"updatedAt": "2021-12-28T14:41:45.948Z",
"__v": 0
}
}
]
},
{
"_id": "18-10-2021",
"data": [
{
"id": "61cc26bd7d1e9476e52c4b35",
"dentNumber": 4,
"diagnosis": "String3",
"price": 1600,
"date": "18-10-2021",
"time": "16:50",
"patient": {
"_id": "61c045bcb0c7e68c96e6ba0e",
"fullname": "test 126",
"phone": "+7 123 54 55",
"createdAt": "2021-12-20T08:58:36.776Z",
"updatedAt": "2021-12-20T08:58:36.776Z",
"__v": 0
}
}
]
},
]
}
You can not execute another query by passing the internal field in any query, You need to do a lookup operation before the group,
$lookup with patient collection, pass patient as localField and pass _id as foreignField
$group stage, use $first to get first element from lookup result in patient
await Appointment.aggregate([
{
$lookup: {
from: "patient",
localField: "patient", // change to your exact collection name
foreignField: "_id",
as: "patient"
}
},
{
$group: {
_id: "$date",
data: {
$push: {
id: "$_id",
patientId: { $first: "$patient._id" },
patient: { $first: "$patient" },
dentNumber: "$dentNumber",
diagnosis: "$diagnosis",
price: "$price",
date: "$date",
time: "$time"
}
}
}
},
{ $sort: { _id: 1 } }
])
Playground

merge two objects of different collections and get specific data in Mongodb

I have two collections, products and orders.
products collection look like this.
[
{
"_id": "5efb56741c32133bf43ea9aa",
"title": "Xyz",
"image": "172e4eb73415b3cc8540e651.jpg",
"quantity": "1 Ltr",
"price": 1500,
"status": true,
"creationDate": "2020-06-30T15:12:52.570Z",
"__v": 0
},
{
"_id": "5f0079bd27a734424cb3069a",
"title": "abc",
"image": "122e4eb73413b3cc854n42n1.jpg",
"quantity": "500 ml",
"price": 700,
"status": true,
"creationDate": "2020-06-30T15:12:52.570Z",
"__v": 0
}
]
orders collection look like this.
[
{
"_id": "5efca937def27b74fc9f6aa6",
"products": [
{
"Date": "2020-07-01T15:14:36.630Z",
"_id": "5efca937def27b74fc9f6aa8",
"productId": "5efb56741c32133bf43ea9aa",
"productQuantity": 2
},
{
"Date": "2020-07-01T15:14:36.630Z",
"_id": "5efca937def27b74fc9f6aa7",
"productId": "5f0079bd27a734424cb3069a",
"productQuantity": 1
}
],
"totalQuantity": 3,
"totalPrice": 3700,
"creationDate": "2020-07-01T15:18:15.756Z",
"__v": 0
},
{
"_id": "5efca897def27b74fc9f6aa2",
"products": [
{
"Date": "2020-07-01T15:14:36.630Z",
"_id": "5efca897def27b74fc9f6aa3",
"productId": "5f0079bd27a734424cb3069a",
"productQuantity": 1
}
],
"totalQuantity": 1,
"totalPrice": 700,
"creationDate": "2020-07-01T15:15:35.422Z",
"__v": 0
}
]
using this two collections how can I get result like this:
[
{
"_id": "5efca937def27b74fc9f6aa6",
"products": [
{
"Date": "2020-07-01T15:14:36.630Z",
"_id": "5efca937def27b74fc9f6aa8",
"productId": "5efb56741c32133bf43ea9aa",
"productQuantity": 2,
"title": "Xyz",
"image": "172e4eb73415b3cc8540e651.jpg",
"quantity": "1 Ltr",
"price": 1500
},
{
"Date": "2020-07-01T15:14:36.630Z",
"_id": "5efca937def27b74fc9f6aa7",
"productId": "5f0079bd27a734424cb3069a",
"productQuantity": 1,
"title": "abc",
"image": "122e4eb73413b3cc854n42n1.jpg",
"quantity": "500 ml",
"price": 700
}
],
"totalQuantity": 3,
"totalPrice": 3700,
"creationDate": "2020-07-01T15:18:15.756Z",
"__v": 0
},
{
"_id": "5efca897def27b74fc9f6aa2",
"products": [
{
"Date": "2020-07-01T15:14:36.630Z",
"_id": "5efca897def27b74fc9f6aa3",
"productId": "5f0079bd27a734424cb3069a",
"productQuantity": 1,
"title": "abc",
"image": "122e4eb73413b3cc854n42n1.jpg",
"quantity": "500 ml",
"price": 700
}
],
"totalQuantity": 1,
"totalPrice": 700,
"creationDate": "2020-07-01T15:15:35.422Z",
"__v": 0
}
]
I got the result using this query.
order.aggregate([
{ "$unwind": "$products" },
{
$lookup: {
from: 'products',
localField: "products.productId",
foreignField: "_id",
as: "product"
}
},
{
"$addFields": {
"products": { $mergeObjects: [{ $arrayElemAt: ["$product", 0] }, "$$ROOT.products"] }
}
},
{
"$group": {
"_id": "$_id",
"products": { "$push": "$products" },
"totalQuantity": { $first: '$totalQuantity' },
"totalPrice": { $first: '$totalPrice' },
"creationDate": { $first: '$creationDate' }
}
}
])

Group items inside of another group

I want to group the following collection by category and sum its total value, then create a subcategory attribute, on same structure, summing subcategories if more than one equal.
[
{
"_id": 1,
"date": ISODate("2019-10-10T00:00:00.000Z"),
"description": "INTERNET BILL",
"credit": "",
"debit": "-100.00",
"category": "home",
"subcategory": "internet",
"__v": 0
},
{
"_id": 2,
"date": ISODate("2019-10-10T00:00:00.000Z"),
"description": "WATER BILL",
"credit": "",
"debit": "-150.00",
"category": "home",
"subcategory": "water",
"__v": 0
},
{
"_id": 3,
"date": ISODate("2019-10-10T00:00:00.000Z"),
"description": "MC DONALDS",
"credit": "",
"debit": "-30.00",
"category": "food",
"subcategory": "restaurants",
"__v": 0
},
{
"_id": 4,
"date": ISODate("2019-10-10T00:00:00.000Z"),
"description": "BURGER KING",
"credit": "",
"debit": "-50.00",
"category": "food",
"subcategory": "restaurants",
"__v": 0
},
{
"_id": 5,
"date": ISODate("2019-10-10T00:00:00.000Z"),
"description": "WALMART",
"credit": "",
"debit": "-20.00",
"category": "food",
"subcategory": "groceries",
"__v": 0
},
]
Desireble output:
[
{
"_id": "home",
"total": "-250.00",
"subcategory" : [
{"id": "internet", "total": "-100"},
{"id": "water", "total": "-150"}
]
},
{
"_id": "food",
"total": "-100.00",
"subcategory" : [
{"id": "restaurants", "total": "-80"},
{"id": "groceries", "total": "-20"}
]
}
]
With the following query, I've almost achieved it, but I haven't find a way to sum values on subcategories.
db.getCollection('expenses').aggregate([
{$match:
{"date" : { "$gte" : new Date("11-10-2019"), "$lte": new Date("2019-10-11") }}
},
{$group: {
_id: "$category",
total: { $sum: { $toDouble: { $cond: { if: { $ne: [ "$debit", ""] }, then: "$debit", else: "$credit" } } } },
subcategories: { $addToSet: {id: "$subcategory" }},
}}
])
You can to $group twice (by subcategory first):
db.collection.aggregate([
{
$group: {
_id: { category: "$category", subcategory: "$subcategory" },
total: { $sum: { $toDouble: "$debit" } }
}
},
{
$group: {
_id: "$_id.category",
total: { $sum: "$total" },
subcategories: { $push: { id: "$_id.subcategory", total: "$total" } }
}
}
])
Mongo Playground