I want to combine separate collections to 1 json, but I have some problems in nested $lookup.
This is my example collections.
Collection Package_subscriptions:
[
{
"_id": "1",
"package_name": "Small",
"package_desc": "AAA",
"package_price": 10
},
{
"_id": "2",
"package_name": "Medium",
"package_desc": "BBB",
"package_price": 20
}
Collection Package_modules:
{
"_id" : 1,
"subscription_id" : 1,
"billing_model_id" : 1,
"module_id" : 1,
"created_at" : ISODate("2019-06-17T07:59:43.199Z"),
}
{
"_id" : 2,
"subscription_id" : 1,
"billing_model_id" : 3,
"module_id" : 2,
"created_at" : ISODate("2019-06-17T08:00:37.464Z"),
}
{
"_id" : 3,
"subscription_id" : 2,
"billing_model_id" : 2,
"module_id" : 1,
"created_at" : ISODate("2019-06-17T08:00:56.610Z"),
}
{
"_id" : 4,
"subscription_id" : 2,
"billing_model_id" : 4,
"module_id" : 2,
"created_at" : ISODate("2019-06-17T08:01:29.667Z"),
}
Collection Modules:
{
"_id" : 1,
"module_name" : "Call",
"status" : "Active",
}
{
"_id" : 2,
"module_name" : "SMS",
"status" : "Active",
}
Collection Billing_models
{
"_id" : 1,
"unit_count" : "Menit",
"counter" : 2000,
},
{
"_id" : 2,
"unit_count" : "Menit",
"counter" : 3000,
}
{
"_id" : 3,
"unit_count" : "SMS",
"counter" : 3000,
},
{
"_id" : 4,
"unit_count" : "SMS",
"counter" : 5000,
}
This is my code to try the issue, but there's not as expected.
Package_subscription
.aggregate([
{
$lookup: {
from: "o_package_modules",
localField: "_id",
foreignField: "subscription_id",
as: "package_modules"
}
},
{
$unwind: {
path: "$package_modules",
preserveNullAndEmptyArrays: true
}
},
{
$lookup: {
from: "o_modules",
localField: "package_modules.module_id",
foreignField: "_id",
as: 'module'
}
},
{
$lookup: {
from: "o_billing_models",
localField: "package_modules.billing_model_id",
foreignField: "_id",
as: 'billing_module'
}
}
])
.exec()
.then((pricing) => {
res.json(pricing)
})
.catch((err) => {
res.send(err)
})
I expect the output is:
[
{
"_id": "1",
"package_name": "Small",
"package_desc": "AAA",
"package_price": 10,
"package_modules": [
{
"_id": 1,
"subscription_id": 1,
"billing_model": {
"_id": 1,
"unit_count": "Menit",
"counter": 2000
},
"module": {
"_id": 1,
"module_name": "Call",
"status": "Active"
},
"created_at": "2019-06-17T07:59:43.199Z",
},
{
"_id": 2,
"subscription_id": 1,
"billing_model": {
"_id": 3,
"unit_count": "SMS",
"counter": 3000
},
"module": {
"_id": 2,
"module_name": "SMS",
"status": "Active"
},
"created_at": "2019-06-17T08:00:37.464Z",
}
]
},
{
"_id": 2,
"package_name": "Medium",
"package_desc": "BBB",
"package_price": 20,
"package_modules": [
{
"_id": 3,
"subscription_id": 2,
"billing_model": {
"_id": 2,
"unit_count": "Menit",
"counter": 3000
},
"module": {
"_id": "1",
"module_name": "Call",
"status": "Active"
},
"created_at": "2019-06-17T08:01:29.667Z",
},
{
"_id": 4,
"subscription_id": 2,
"billing_model": {
"_id": 4,
"unit_count": "SMS",
"counter": 5000
},
"module": {
"_id": 2,
"module_name": "SMS",
"status": "Active"
},
"created_at": "2019-06-17T08:01:50.285Z",
}
]
}
]
But the output from my code is:
[
{
"_id": "1",
"package_name": "Small",
"package_desc": "AAA",
"package_price": 10,
"package_modules": {
"_id": 1,
"subscription_id": 1,
"billing_model_id": 1
"module_id": 1
"created_at": "2019-06-17T07:59:43.199Z",
},
},
"billing_model": [
{
"_id": 1,
"unit_count": "Menit",
"counter": 2000
},
],
"module": [
{
"_id": 1,
"module_name": "Call",
"status": "Active"
}
]
},
{
"_id": "1",
"package_name": "Small",
"package_desc": "AAA",
"package_price": 10,
"package_modules": {
"_id": 2,
"subscription_id": 2,
"billing_model_id": 3
"module_id": 2
"created_at": "2019-06-17T07:59:43.199Z",
},
},
"billing_model": [
{
"_id": 3,
"unit_count": "SMS",
"counter": 3000
},
],
"module": [
{
"_id": 2,
"module_name": "SMS",
"status": "Active"
}
]
}
..........
// And medium where is loop again
]
I think there are multiple issue in your code.
In your $lookup stage, you need to setup the as field as the package_modules.billing_module and package_modules.module instead of billing_module and module, and so on, so that it becomes a field of package_module object instead of a separate object itself.
you need to unwind after each $lookup stage, as $lookup returns an array instead of an object.
You need to $group at the end of the aggregation pipeline to push all the package_modules of one subscription into one array.
After resolving all the above issues,
Your aggregation pipe should look like this:
Package_subscription
.aggregate([
{
$lookup: {
from: "o_package_modules",
localField: "_id",
foreignField: "subscription_id",
as: "package_modules"
}
},
{
$unwind: {
path: "$package_modules",
preserveNullAndEmptyArrays: true
}
},
{
$lookup: {
from: "o_modules",
localField: "package_modules.module_id",
foreignField: "_id",
as: 'package_modules.module'
}
},
{
$unwind: {
path: "$package_modules.module",
preserveNullAndEmptyArrays: true
}
},
{
$lookup: {
from: "o_billing_models",
localField: "package_modules.billing_model_id",
foreignField: "_id",
as: 'package_modules.billing_module'
}
},
{
$unwind: {
path: "$package_modules.billing_module",
preserveNullAndEmptyArrays: true
}
},
{
$group: {
_id: "$_id",
package_modules : {$push : "$package_modules"},
package_name: {$first . : "$package_name"},
package_desc: {$first . : "$package_desc"},
package_price: {$first . : "$package_price"}
}
}
])
.exec()
.then((pricing) => {
res.json(pricing)
})
.catch((err) => {
res.send(err)
})
The $push in the last $group stage will combine all package_modules into one array. And i think that is what you want at the end.
I hope this works for you.
thank you its working for me
var order_details_data = await OrderDetails.aggregate([{
'$match': {
`order_id`: ObjectId(val._id)
}
}, {
$sort: {
date: -1
}
}, {
$addFields: {
o_price: {
$toDouble: `$o_price`
}
}
}, {
$lookup: {
from: `products`,
localField: `product_id`,
foreignField: `_id`,
as: `product_id`,
},
}, {
$unwind: {
path: `$product_id`,
preserveNullAndEmptyArrays: true
}
}, {
$lookup: {
from: `productvarientvalues`,
localField: `product_id.material_id`,
foreignField: `_id`,
as: `product_id.material_id`,
},
}, {
$unwind: {
path: `$product_id.material_id`,
preserveNullAndEmptyArrays: true
}
}, {
$group: {
_id: `$order_id`,
date: {
$first: `$date`
},
totalAmount: {
$sum: `$totalAmount`
},
orderDetails: {
$push: `$$ROOT`
},
delivery_address: {
$first: delivery_address
},
ship_address_id: {
$first: ship_address_id
},
// material_id: { $push: `$product_id.material_id` }
product_id: {
$push: `$product_id`
},
}
}, {
$project: {
// test: `$test`,
totalAmount: {
$multiply: [`$qty`, `$o_price`]
},
o_price: `$o_price`,
product_id: `$product_id`,
order_id: `$order_id`,
user_id: `$user_id`,
product_id: {
$arrayElemAt: [`$product_id`, 0]
},
data: {
$arrayElemAt: [`$data`, 0]
},
qty: `$qty`,
date: {
$dateToString: {
format: `%M `,
date: `$date`
}
},
discount: `$discount`,
examTotal: {
$sum: [`$qty`, `$price`]
},
createdAt: {
$dateToString: {
format: `%Y-%m-%d %H:%M`,
date: `$createdAt`
}
},
}
}
I have data in worksheets collection like below:
/* 1 */
{
"_id" : ObjectId("5c21d780f82aa31334ab6506"),
"isBilling" : true,
"hours" : 6,
"userId" : ObjectId("5c1f38a1d7537d1444738493"),
}
/* 2 */
{
"_id" : ObjectId("5c21d780f82aa31334ab6507"),
"isBilling" : true,
"hours" : 4,
"userId" : ObjectId("5c1f38a1d7537d1444738493"),
}
/* 3 */
{
"_id" : ObjectId("5c21e10fae07cc1204a5b647"),
"isBilling" : false,
"hours" : 8,
"userId" : ObjectId("5c1f388fd7537d1444738492"),
}
I have to create a aggregate query to sum the hours, where isBilling equals to true, and where isBilling equals to false.I want the below output:
{
"billingHours":10,
"fixContract":8
}
I have to get data with the particular userId. I tried the below:
Worksheet.aggregate([
{
$match: conditions
},
{
$lookup:{
"from": "worksheets",
"let": {},
"pipeline": [
{ "$match": { "$expr": { "$eq": [ "$isBilling",false] } } },
{
"$group": { "_id": null, "totalHours": { "$sum": "$hours" } }
},
],
"as": "billingHours"
}
},
{
"$project":{"billingHours":1}
}
])
I am getting the below result:
[
{
"_id": "5c21d780f82aa31334ab6506",
"billingHours": [
{
"_id": null,
"totalHours": 16
}
]
},
{
"_id": "5c21d780f82aa31334ab6507",
"billingHours": [
{
"_id": null,
"totalHours": 16
}
]
}
]
I don't know why it is giving me 16 hours instead of 10 and giving me two objects instead of 1.
You don't need to use $lookup here. Simple $group with $cond will do the job.
db.collection.aggregate([
{ "$group": {
"_id": null,
"billingHours": {
"$sum": {
"$cond": [{ "$eq": ["$isBilling", true] }, "$hours", 0]
}
},
"fixContract": {
"$sum": {
"$cond": [{ "$eq": ["$isBilling", true] }, 0, "$hours"]
}
}
}}
])
I'm using mongoDB 3.6 on node.js 8.11.1 and working with MongoDB Node.js Driver.
I have two collections, 'group' and 'user':
group:
[
{
"_id":1,
"groupName":"group1",
"users":[
{
"userId":1,
"isAdmin":"false"
},
{
"userId":2,
"isAdmin":"true"
}
]
},
{
"_id":2,
"groupName":"group2",
"users":[
{
"userId":2,
"isAdmin":"false"
},
{
"userId":3,
"isAdmin":"true"
}
]
}
]
user:
[
{
"_id":1,
"username":"user1",
"firstname":"a",
"lastname":"aa",
"mobileNo":"+1111111"
},
{
"_id":2,
"username":"user2",
"firstname":"b",
"lastname":"bb",
"mobileNo":"+2222222"
},
{
"_id":3,
"username":"user3",
"firstname":"c",
"lastname":"cc",
"mobileNo":"+3333333"
}
]
I need an aggregate to return something like this:
[
{
"_id":1,
"groupName":"group1",
"members":[
{
"isAdmin":"false",
"username":"user1",
"firstname":"a",
"lastname":"aa"
},
{
"isAdmin":"true",
"username":"user2",
"firstname":"b",
"lastname":"bb"
}
]
},
{
"_id":2,
"groupName":"group2",
"members":[
{
"isAdmin":"false",
"username":"user2",
"firstname":"b",
"lastname":"bb"
},
{
"isAdmin":"true",
"username":"user3",
"firstname":"c",
"lastname":"cc"
}
]
}
]
At "members" in result, "isAdmin" return from "users" at group collection and "username", "firstname" and "lastname" came from user collection
Many thanks,
Milad.
You can try below aggregation from mongodb 3.6 and above
db.group.aggregate([
{ "$unwind": "$users" },
{ "$lookup": {
"from": Users.collection.name,
"let": { "userId": "$users.userId", "isAdmin": "$users.isAdmin" },
"pipeline": [
{ "$match": { "$expr": { "$eq": [ "$_id", "$$userId" ] } } },
{ "$project": { "isAdmin": "$$isAdmin", "username": 1, "firstName": 1, "lastName": 1 }}
],
"as": "members"
}},
{ "$unwind": "$members" },
{ "$group": {
"_id": "$_id",
"members": { "$push": "$members" },
"groupName": { "$first": "$groupName" }
}}
])
I am new to MongoDb and would appreciate some help with this query. I wrote the following aggregation pipeline
db.collection1.aggregate([
{ "$match": { "type" : "L" }},
{ "$facet": {
"ON": [
{ "$match" : {"lampStatus":'ON'}},
{ "$count": "ON" }
],
"OFF": [
{ "$match" : {"lampStatus": 'OFF'}},
{ "$count": "OFF" }
]
} },
{ "$project": {
"ON": { "$ifNull": [{ "$arrayElemAt": ["$ON.ON", 0] }, 0 ] },
"OFF": { "$ifNull": [{ "$arrayElemAt": ["$OFF.OFF", 0] }, 0 ] },
} }
])
and got output of this type
{
"ON" : 0.0,
"OFF": 30
}
but how to get this type of output with json format
/* 1 */
{
"_id": "ON",
"count" : 0.0
}
/* 2 */
{
"_id": "OFF",
"count" : 30.0
}
any one please suggest me?
Actual Output
{
"ON" : 0.0,
"OFF" : 30
}
Expected output:
{
"_id" : "ON",
"COUNT" : 0.0
}
/* 2 */
{
"_id" : "OFF",
"COUNT" : 30.0
}
If you need some more aggregation trick
db.collection1.aggregate([
{ "$match": { "type" : "L" }},
{ "$facet": {
"ON": [{ "$match" : {"lampStatus": "ON" }}, { "$count": "ON" }],
"OFF": [{ "$match" : {"lampStatus": "OFF" }}, { "$count": "OFF" }]
}},
{ "$project": {
"ON": { "$ifNull": [{ "$arrayElemAt": ["$ON.ON", 0] }, 0 ] },
"OFF": { "$ifNull": [{ "$arrayElemAt": ["$OFF.OFF", 0] }, 0 ] },
}},
{ "$project": {
"data": {
"$map": { "input": { "$objectToArray": "$$ROOT" }, "in": { "_id": "$$this.k", "count": "$$this.v" }}
}
}},
{ "$unwind": "$data" },
{ "$replaceRoot": { "newRoot": "$data" }}
])
Upodated
db.collection.aggregate([
{ "$match": { "type" : "L" }},
{ "$facet": {
"ON": [
{ "$match" : {"lampStatus":'ON'}},
{ "$count": "ON" }
],
"OFF": [
{ "$match" : {"lampStatus": 'OFF'}},
{ "$count": "OFF" }
]
} },
{"$project": {
myArray: [
{_id:"ON", count: { "$ifNull": [{ "$arrayElemAt": ["$ON.ON", 0] }, 0 ]} },
{_id:"OFF", count: { "$ifNull": [{ "$arrayElemAt": ["$OFF.OFF", 0] }, 0 ] }}
]
}},
{"$unwind":"$myArray"},
{
"$replaceRoot": { newRoot: "$myArray" }
}
])
I have two collections like this :
1st collection name is Promotions:
{
"_id": "A019283847466",
"code": "AAA",
"aliases" : [ "AAA1","AAA2","AAA3"]
}
2nd Collection name is PromotionUsages:
{
"customerId": "_1234567890"
"code": "AAA1"
}
{
"customerId": "_0987654321"
"code": "AAA1"
}
Expected output is :
{
"code": "AAA"
"aliasCode": "AAA1"
"countUsages": 2
}
I used mongo $group and $aggregate but I am not getting required output
any help please
Thank You!!!
You can try below aggregation
db.collection.aggregate([
{ "$unwind": "$aliases" },
{ "$addFields": { "aliasesCode": "$aliases" }},
{ "$lookup": {
"from": PromotionUsages.collection.name,
"let": { "aliases": "$aliases" },
"pipeline": [
{ "$match": { "$expr": { "$eq": [ "$code", "$$aliases" ] } } },
{ "$count": "countUsages" }
],
"as": "aliases"
}},
{ "$unwind": "$aliases" },
{ "$project": { "code": 1, "aliasCode": 1, "countUsages": "$aliases.countUsages" }}
])
Output
{
"code": "AAA"
"aliasCode": "AAA1"
"countUsages": 2
}
Made some changes in solution suggested by Anthony Winzlet
db.promotions.aggregate([
{ "$unwind": "$aliases" },
{ "$addFields": { "aliasesCode": "$aliases" }},
{ "$lookup": {
"from": "promotionusages",
"let": { "aliases": "$aliases" },
"pipeline": [
{ "$match": { "$expr": { "$eq": [ "$code", "$$aliases" ] } } },
{ "$count": "countUsages" }
],
"as": "aliases"
}},
{ "$unwind": "$aliases" },
{ "$project": { "_id" : 0, "code": 1, "aliasesCode": 1, "countUsages": "$aliases.countUsages" }}
])