merge two objects of different collections and get specific data in Mongodb - 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' }
}
}
])

Related

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

How to sum the balance and income using 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

mongodb:complex nested aggregation

I have this collection:
[
{
"_id": {
"$oid": "60b22e1dbd46fa18a8308318"
},
"title": "basketball",
"price": 12,
"category": "Furniture",
"description": "",
"images": [
"http://res.cloudinary.com/hadarush100/image/upload/v1622289949/nfg948x3zro6gbiuknrz.jpg"
],
"categoryId": 1,
"userId": "60ad16493062eb11141d4927",
"createdAt": 1622289948232,
"chats": [
{
"id": 1,
"createdAt": 1622289948232,
"messages": [
{
"id": "1",
"createdAt": 1622289948232,
"senderId": "60ad16493062eb11141d4927",
"text": "Hello, Im the seller of this product."
}
]
},
{
"id": "2",
"createdAt": 1622289948232,
"messages": [
{
"id": 1,
"createdAt": 1622289948232,
"senderId": "60ad16493062eb11141d4927",
"text": "Hello, Im the seller of this product."
}
]
}
]
}
]
and i want to find specific document (by _id), then dive into specific chat in this document (by id), than use $lookup for replacing the "senderId" property in each message with a "sender" property that contains the full sender details (as a user), that exist in another collection (users). the result needs to look like this:
[
{
"_id": {
"$oid": "60b22e1dbd46fa18a8308318"
},
"title": "basketball",
"price": 12,
"category": "Furniture",
"description": "",
"images": [
"http://res.cloudinary.com/hadarush100/image/upload/v1622289949/nfg948x3zro6gbiuknrz.jpg"
],
"categoryId": 1,
"userId": "60ad16493062eb11141d4927",
"createdAt": 1622289948232,
"chats": [
{
"id": 1,
"createdAt": 1622289948232,
"messages": [
{
"id": "1",
"createdAt": 1622289948232,
"sender": {
"_id": {
"$oid": "60ad16493062eb11141d4927"
},
"username": "hadar",
"email": "hadarushha#gmail.com",
"profileImgUrl": "https://randomuser.me/api/portraits/men/79.jpg",
"createdAt": 1621956168518
},
"text": "Hello, Im the seller of this product."
}
]
},
{
"id": "2",
"createdAt": 1622289948232,
"messages": [
{
"id": 1,
"createdAt": 1622289948232,
"sender": {
"_id": {
"$oid": "60ad16493062eb11141d4927"
},
"username": "hadar",
"email": "hadarushha#gmail.com",
"profileImgUrl": "https://randomuser.me/api/portraits/men/79.jpg",
"createdAt": 1621956168518
},
"text": "Hello, Im the seller of this product."
}
]
}
]
}
]
You can use this aggregation:
$match to filter only selected document (_id)
$unwind multiple time to transform arrays into objects
$lookup to query external collection (users)
$group in reverse order
I assumed that your collections are more or less like this (next time, post both collections and also an example on a working playground)
db={
"products": [
{
"_id": {
"$oid": "60b22e1dbd46fa18a8308318"
},
"title": "basketball",
"price": 12,
"category": "Furniture",
"description": "",
"images": [
"http://res.cloudinary.com/hadarush100/image/upload/v1622289949/nfg948x3zro6gbiuknrz.jpg"
],
"categoryId": 1,
"userId": "60ad16493062eb11141d4927",
"createdAt": 1622289948232,
"chats": [
{
"id": 1,
"createdAt": 1622289948232,
"messages": [
{
"id": "1",
"createdAt": 1622289948232,
"senderId": "60ad16493062eb11141d4927",
"text": "Hello, Im the seller of this product."
}
]
},
{
"id": "2",
"createdAt": 1622289948232,
"messages": [
{
"id": 1,
"createdAt": 1622289948232,
"senderId": "60ad16493062eb11141d4927",
"text": "Hello, Im the seller of this product."
}
]
}
]
},
{
"_id": {
"$oid": "60b22e1dbd46fa18a8308319"
},
"title": "volleyball",
"price": 8,
"category": "Furniture",
"description": "",
"images": [
"http://res.cloudinary.com/hadarush100/image/upload/v1622289949/nfg948x3zro6gbiuknrz.jpg"
],
"categoryId": 1,
"userId": "60ad16493062eb11141d4927",
"createdAt": 1622289948232,
"chats": [
{
"id": 1,
"createdAt": 1622289948232,
"messages": [
{
"id": "1",
"createdAt": 1622289948232,
"senderId": "60ad16493062eb11141d4927",
"text": "Hello, Im the seller of this product."
}
]
},
{
"id": "2",
"createdAt": 1622289948232,
"messages": [
{
"id": 1,
"createdAt": 1622289948232,
"senderId": "60ad16493062eb11141d4928",
"text": "Hello, Im the seller of this product."
}
]
}
]
}
],
"users": [
{
"_id": {
"$oid": "60ad16493062eb11141d4927"
},
"username": "hadar",
"email": "hadarushha#gmail.com",
"profileImgUrl": "https://randomuser.me/api/portraits/men/79.jpg",
"createdAt": 1621956168518
},
{
"_id": {
"$oid": "60ad16493062eb11141d4928"
},
"username": "test",
"email": "test#gmail.com",
"profileImgUrl": "https://randomuser.me/api/portraits/men/49.jpg",
"createdAt": 1621956168528
},
]
}
And here is the working aggregation:
db.products.aggregate([
{
"$match": {
"_id": {
"$oid": "60b22e1dbd46fa18a8308319"
}
}
},
{
"$unwind": "$chats"
},
{
"$unwind": "$chats.messages"
},
{
"$addFields": {
"chats.messages.senderIdObjId": {
"$convert": {
"input": "$chats.messages.senderId",
"to": "objectId",
}
}
}
},
{
"$lookup": {
"from": "users",
"localField": "chats.messages.senderIdObjId",
"foreignField": "_id",
"as": "chats.messages.sender"
}
},
{
"$unwind": "$chats.messages.sender"
},
{
"$group": {
"_id": "$chats.id",
"messages": {
"$push": "$chats.messages"
},
"allFields": {
"$first": "$$ROOT"
}
}
},
{
"$addFields": {
"allFields.chats.messages": "$messages"
}
},
{
"$replaceWith": "$allFields"
},
{
"$group": {
"_id": "$_id",
"chats": {
"$push": "$chats"
},
"allFields": {
"$first": "$$ROOT"
}
}
},
{
"$addFields": {
"allFields.chats": "$chats"
}
},
{
"$replaceWith": "$allFields"
},
])
Working Playground here

Mongdb aggregate do second group inside first group stage output

I don't know if it's either possible but I would like to obtain a specific output from an aggregate pipeline.
Exemples objects:
{
"_id": "6001d736e6dc1c55e893158d",
"manager": "6000da590ed6253807158216",
"label": "Test",
"identifier": "Test",
"interval": 11,
"unit": "X",
"created_at": "2021-01-15T17:56:06.749Z",
"updated_at": "2021-01-21T12:21:35.670Z",
"__v": 0
},
{
"_id": "6030236f976756b0b2d74556",
"manager": "6022f3285752fec73393bda2",
"label": "Temperature salon",
"identifier": "DS18B20_TEMP",
"interval": 60,
"unit": "°C",
"created_at": "2021-02-19T20:45:35.847Z",
"updated_at": "2021-02-19T20:45:35.847Z",
"__v": 0
}
I'm trying to obtain a group by date AND by unit(field in object), i succeed to do it separatly but i can't find a solution to do both in the same pipeline.
Expected output if i do the first group by month:
{
"_id": "2021-01-00T00:00:00.000Z",
"X": objectsArray[],
"°C": objetcsArray[]
},
{
"_id": "2021-02-00T00:00:00.000Z",
"X": objectsArray[],
"°C": objetcsArray[]
}
What i have for the moment with this group:
{
'_id': {
'$add': [
{ '$subtract': [
{ '$subtract': [ '$created_at', new Date(0) ] },
{ '$mod': [
{ '$subtract': [ '$created_at', new Date(0) ] },
this.millisecondsIn(interval),
]},
]},
new Date(0),
]
},
sensors: {
$addToSet: '$$ROOT',
},
}
{
"_id": "2021-01-21T00:00:00.000Z",
"sensors": [
{
"_id": "601ab8f623224a5387c6252d",
"manager": "6000da590ed6253807158216",
"label": "Test",
"identifier": "Test2",
"interval": 60,
"unit": "°C",
"created_at": "2021-02-03T14:53:42.538Z",
"updated_at": "2021-02-03T14:53:42.538Z",
"__v": 0
},
{
"_id": "6029ad3dda9bafb99cf0b4d5",
"manager": "6022f3285752fec73393bda2",
"label": "Test sensor 1",
"identifier": "RANDOMID",
"interval": 60,
"unit": "°C",
"created_at": "2021-02-14T23:07:41.255Z",
"updated_at": "2021-02-14T23:07:41.255Z",
"__v": 0
}
]
},
{
"_id": "2020-12-24T00:00:00.000Z",
"sensors": [
{
"_id": "6001917f41c38212a477a2ce",
"manager": "6000da590ed6253807158216",
"label": "Test label",
"identifier": "TEst id",
"interval": 10,
"unit": "%",
"created_at": "2021-01-15T12:58:39.514Z",
"updated_at": "2021-01-16T19:08:40.239Z",
"__v": 0
},
{
"_id": "6001d736e6dc1c55e893158d",
"manager": "6000da590ed6253807158216",
"label": "Test",
"identifier": "Test",
"interval": 11,
"unit": "X",
"created_at": "2021-01-15T17:56:06.749Z",
"updated_at": "2021-01-21T12:21:35.670Z",
"__v": 0
}
]
},
{
"_id": "2021-02-18T00:00:00.000Z",
"sensors": [
{
"_id": "6030238d976756b0b2d74557",
"manager": "6022f3285752fec73393bda2",
"label": "Taux d'humidité salon",
"identifier": "DHT_22_HUM",
"interval": 60,
"unit": "%",
"created_at": "2021-02-19T20:46:05.042Z",
"updated_at": "2021-02-19T20:46:05.042Z",
"__v": 0
},
{
"_id": "60302357976756b0b2d74555",
"manager": "6022f3285752fec73393bda2",
"label": "Temperature salon",
"identifier": "DTH_22_TEMP",
"interval": 60,
"unit": "°C",
"created_at": "2021-02-19T20:45:11.071Z",
"updated_at": "2021-02-19T20:45:11.071Z",
"__v": 0
},
{
"_id": "6030236f976756b0b2d74556",
"manager": "6022f3285752fec73393bda2",
"label": "Temperature salon",
"identifier": "DS18B20_TEMP",
"interval": 60,
"unit": "°C",
"created_at": "2021-02-19T20:45:35.847Z",
"updated_at": "2021-02-19T20:45:35.847Z",
"__v": 0
}
]
}
Does anyone know if the wanted output is possible and if it is, how ?
Thanks
The general steps would be:
$group by unit and date, pushing all of the documents into a sensors array
$project to create a new field with {k: <unit value>, v: <sensors array>}
$group by date, pushing the new field into an array
$project with $arrayToObject to convert array
$addFields to include the date in the new object
$replaceRoot to promote the new object
Thank you so much Joe, after few hours and many tries I end up with this:
this.sensorModel
.aggregate()
.group({
'_id': {
date: { '$year': '$created_at' },
'unit': '$unit',
},
test: { '$addToSet': '$$ROOT' },
})
.project({
newField: {
k: '$_id.unit',
v: '$test',
}
})
.group({
'_id': '$_id.date',
data: { '$addToSet': '$newField', },
})
.project({
'sensors': { $arrayToObject: '$data', },
})
.replaceRoot({
$arrayToObject: [[{k: {$toString: '$_id'}, v: '$sensors'}]]
})
.exec();
The result is almost perfect:
[
{
"2021": {
"°C": [
{
"_id": "6029ad3dda9bafb99cf0b4d5",
"manager": "6022f3285752fec73393bda2",
"label": "Test sensor 1",
"identifier": "RANDOMID",
"interval": 60,
"unit": "°C",
"created_at": "2021-02-14T23:07:41.255Z",
"updated_at": "2021-02-14T23:07:41.255Z",
"__v": 0
},
],
"X": [
{
"_id": "6001d736e6dc1c55e893158d",
"manager": "6000da590ed6253807158216",
"label": "Test",
"identifier": "Test",
"interval": 11,
"unit": "X",
"created_at": "2021-01-15T17:56:06.749Z",
"updated_at": "2021-01-21T12:21:35.670Z",
"__v": 0
}
],
"%": [
{
"_id": "6030238d976756b0b2d74557",
"manager": "6022f3285752fec73393bda2",
"label": "Taux d'humidité salon",
"identifier": "DHT_22_HUM",
"interval": 60,
"unit": "%",
"created_at": "2021-02-19T20:46:05.042Z",
"updated_at": "2021-02-19T20:46:05.042Z",
"__v": 0
},
]
}
}
]

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