Mongodb aggregate lookup three collections - mongodb

Learning MongoDB for the past two days and I am trying to aggregate three collections but unable to achieve it
Below are the three collection maintaining in the database
t_credentials
{
"_id" : "619ca68b624c41e408348406",
"title" : "Company ID"
}
t_groups
{
"_id" : "61a253da88ca12a37218898d",
"group_name" : "Gold"
}
t_user_credentials
{
"_id" : "619ca88a624c41e408348424",
"credential_id" : "619ca68b624c41e408348406",
"group_id" : "61a253da88ca12a37218898d",
"identifiers" : {
"first_name" : "Lee",
"middle_name" : "Min",
"last_name" : "Ho"
},
"created_at" : "2021-12-01T17:20:49.000Z"
}
Here I am trying to achieve the output in the below format:
Expected Output
[{
"_id" : "619ca88a624c41e408348424",
"first_name" : ,
"middle_name" : ,
"last_name" : ,
"credential" : {
"_id:" : "619ca68b624c41e408348406",
"title" : "Company ID"
},
"group" : {
"_id" : "61a253da88ca12a37218898d",
"group_name" : "Gold"
},
"created_at" : "2021-12-01T17:20:49.000Z"
}]
But, I am getting the fields only from t_user_credentials but not able to get like in the above format
Query
db.t_user_credentials.aggregate([
{
$lookup: {
from: "t_credentials",
localField: "_id",
foreignField: "credential_id",
as: "credentials"
}
},
{
$unwind: {
path:'$credentials',
preserveNullAndEmptyArrays: true
}
},
{
$lookup: {
from: "t_groups",
localField: "_id",
foreignField: "group_id",
as: "groups"
}
},
{
$unwind: {
path: '$groups',
preserveNullAndEmptyArrays: true
}
},
{
$project: {
last_name: "$identifiers.last_name",
first_name: "$identifiers.first_name",
middle_name: "$identifiers.middle_name",
"credentials.title": 1,
created_at: 1,
group_id: 1
}
}
])
Can any one help me to solve this issue, it will be very helpful for me.

This query uses $replaceWith to merge the identifiers sub-document into the $$ROOT document. We also use $unset to remove fields we are no longer interested in. Before all of that we make sure to unwind our credential and group fields.
You can check out a live demo of this query here
Consider the following:
Database
db={
"t_credentials": [
{
"_id": "619ca68b624c41e408348406",
"title": "Company ID"
}
],
"t_groups": [
{
"_id": "61a253da88ca12a37218898d",
"group_name": "Gold"
}
],
"t_user_credentials": [
{
"_id": "619ca88a624c41e408348424",
"credential_id": "619ca68b624c41e408348406",
"group_id": "61a253da88ca12a37218898d",
"identifiers": {
"first_name": "Lee",
"middle_name": "Min",
"last_name": "Ho"
},
"created_at": "2021-12-01T17:20:49.000Z"
}
]
}
Query
db.t_user_credentials.aggregate([
{
"$lookup": {
"from": "t_credentials",
"localField": "credential_id",
"foreignField": "_id",
"as": "credential"
}
},
{
"$lookup": {
"from": "t_groups",
"localField": "group_id",
"foreignField": "_id",
"as": "group"
}
},
{
$unwind: "$group",
},
{
$unwind: "$credential"
},
{
$replaceWith: {
$mergeObjects: [
"$$ROOT",
"$identifiers"
]
}
},
{
$unset: [
"group_id",
"credential_id",
"identifiers"
]
}
])
Result
[
{
"_id": "619ca88a624c41e408348424",
"created_at": "2021-12-01T17:20:49.000Z",
"credential": {
"_id": "619ca68b624c41e408348406",
"title": "Company ID"
},
"first_name": "Lee",
"group": {
"_id": "61a253da88ca12a37218898d",
"group_name": "Gold"
},
"last_name": "Ho",
"middle_name": "Min"
}
]

Related

How can I group by a string instead of ObjectId in MongoDB aggregate?

I have two collections and a many-to-one relationship between them:
Product:
"_id" : ObjectId("61cc81c9585946c3b44f24411"),
"name" : "some random name",
"price" : 100,
"description" : "description",
"category_id" : ObjectId("61cc8100585946c3b44f2317d")
Category:
{
"_id" : ObjectId("61cc8100585946c3b44f2317d"),
"description" : "Category description",
"name" : "Electronics"
}
I would like to output the maximum product price for each category:
db.product.aggregate([
{ "$group": {
"_id": "$category_id",
"max": { "$max": "$price"}
}}
])
This works just fine as it prints me the following:
{ "_id" : ObjectId("61cc80fb585946c3b44f697c"), "max" : 62}
{ "_id" : ObjectId("61cc8100585946c3b44f697d"), "max" : 100}
But is there a way to get the "name" from the Category instead of its object id?
I know in SQL you would group by category_name but it does not seem to work here.
As suggested by #prasad, you should make use of $lookup stage after your $group stage.
db.product.aggregate([
{
"$group": {
"_id": "$category_id",
"max": {
"$max": "$price"
}
}
},
{
"$lookup": {
"from": "category",
"localField": "_id",
"foreignField": "_id",
"as": "categoryName",
}
},
{
"$set": {
"categoryName": {
"$arrayElemAt": [
"$categoryName.name",
0
]
}
}
}
])
Mongo Playground Sample

$lookup with nested data in mongodb

How do I combine 2 array objects using mongoDB NoSQL? Because I have tried to find some of the same problems here that I got, but I have not found the answers and problems that match what I got.
If someone here wants to help me, here are the problems I want to solve.
Example: I tried using noSQL in mongoDB like this:
db.tables.aggregate([
{ $lookup: { from: 'reservations', localField: '_id', foreignField: 'tableId', as: 'reservation' }},
{ $unwind: { path: '$reservation', 'preserveNullAndEmptyArrays': true }},
{ $lookup: { from: 'orders', localField: 'reservation._id', foreignField: 'reservationId', as: 'orders' }},
{ $lookup: { from: 'products', localField: 'orders.productId', foreignField: '_id', as: 'products' }},
{
$project: {
'_id': 1,
'initial': 1,
'description': 1,
'reservation._id': 1,
'reservation.guest': 1,
'orders._id': 1,
'orders.status': 1,
'orders.quantity': 1,
'orders.productId': 1,
'products._id': 1,
'products.name': 1
}
},
]);
After running noSQL mongoDB above, I got the results below:
{
"_id" : ObjectId("5b63e519514cf01c2864749a"),
"description" : "Kursi VIP 01",
"reservation" : {
"_id" : ObjectId("5b63f104514cf01c286474b6"),
"guest" : "Jhon Doe"
},
"orders" : [
{
"_id" : ObjectId("5b63f239514cf01c286474bb"),
"productId" : ObjectId("5b63e72d514cf01c286474a3"),
"status" : "3",
"quantity" : "2"
},
{
"_id" : ObjectId("5b63f252514cf01c286474bc"),
"productId" : ObjectId("5b63e7de514cf01c286474a6"),
"status" : "2",
"quantity" : "3"
},
{
"_id" : ObjectId("5b63f267514cf01c286474bd"),
"productId" : ObjectId("5b63e937514cf01c286474ac"),
"status" : "0",
"quantity" : "2"
}
],
"products" : [
{
"_id" : ObjectId("5b63e72d514cf01c286474a3"),
"name" : "AQUA 600ML"
},
{
"_id" : ObjectId("5b63e7de514cf01c286474a6"),
"name" : "Nasi Goreng Kecap Asin"
},
{
"_id" : ObjectId("5b63e937514cf01c286474ac"),
"name" : "Daging Ayam Goreng"
}
]
}
Now, my Question is. How to merge/combine 2 Object Array ("orders and products"), So I can get results like this:
{
"_id" : ObjectId("5b63e519514cf01c2864749a"),
"description" : "Kursi VIP 01",
"reservation" : {
"_id" : ObjectId("5b63f104514cf01c286474b6"),
"guest" : "Jhon Doe"
},
"orders" : [
{
"_id" : ObjectId("5b63f239514cf01c286474bb"),
"productId" : ObjectId("5b63e72d514cf01c286474a3"),
"name" : "AQUA 600ML",
"status" : "3",
"quantity" : "2"
},
{
"_id" : ObjectId("5b63f252514cf01c286474bc"),
"productId" : ObjectId("5b63e7de514cf01c286474a6"),
"name" : "Nasi Goreng Kecap Asin",
"status" : "2",
"quantity" : "3"
},
{
"_id" : ObjectId("5b63f267514cf01c286474bd"),
"productId" : ObjectId("5b63e937514cf01c286474ac"),
"name" : "Daging Ayam Goreng"
"status" : "0",
"quantity" : "2"
}
]
}
I hope, someone can help me.
Thanks in advance.
You can try below aggregation with mongodb 3.4
You need to $unwind the orders array to add the field($addFields) name inside orders and then $group to rollback orders again to the make an array field
db.tables.aggregate([
{ "$lookup": {
"from": "reservations",
"localField": "_id",
"foreignField": "tableId",
"as": "reservation"
}},
{ "$unwind": { "path": '$reservation', 'preserveNullAndEmptyArrays': true }},
{ "$lookup": {
"from": "orders",
"localField": "reservation._id",
"foreignField": "reservationId",
"as": "orders",
}},
{ "$unwind": { "path": '$orders', 'preserveNullAndEmptyArrays': true }},
{ "$lookup": {
"from": "products",
"localField": "orders.productId",
"foreignField": "_id",
"as": "orders.products"
}},
{ "$unwind": { "path": '$orders.products', 'preserveNullAndEmptyArrays': true }},
{ "$addFields": {
"orders.name": "$orders.products.name"
}},
{ "$group": {
"_id": "$_id",
"description": { "$first": "$description" },
"reservation": { "$first": "$reservation" },
"orders": { "$push": "$orders" }
}},
{ "$project": { "orders.products": 0 }}
])
Which is far simple with mongodb 3.6 nested $lookup version
db.tables.aggregate([
{ "$lookup": {
"from": "reservations",
"let": { "reservationId": "$_id" },
"pipeline": [
{ "$match": { "$expr": { "$eq": [ "$tableId", "$$reservationId" ] } } }
],
"as": "reservations"
}},
{ "$lookup": {
"from": "orders",
"let": { "reservationId": "$reservation._id" },
"pipeline": [
{ "$match": { "$expr": { "$eq": [ "$reservationId", "$$reservationId" ] } } },
{ "$lookup": {
"from": "products",
"let": { "productId": "$productId" },
"pipeline": [
{ "$match": { "$expr": { "$eq": [ "$_id", "$$productId" ] } } },
{ "$project": { "_id": false }}
],
"as": "products"
}},
{ "$unwind": "$products" },
{ "$addFields": { "name": "$products.name" } },
{ "$project": { "products": 0 }}
],
"as": "orders"
}}
])

$lookup when localField is nested

MongoDB version 3.4.10 (Application is using Meteor framework)
Objective: Aggregate documents that are referenced by _id into the containing document as required at runtime.
I have Materials, Models, and Catalog collections with the following documents:
Materials
{ "_id" : "cf4KgXw7ZK6ukdzR7", "name" : "parquet_wood_mahogany" }
Models
{
"_id" : "Mwp5eYYZ4GZzvZuoK",
"name" : "top_square_chamfered",
"type" : "top"
}
{
"_id" : "CqhS2m2RcLZ2Bm4eb",
"name" : "skirt_square",
"type" : "skirt"
}
{
"_id" : "dYP22ajALnWBwpBj2",
"name" : "leg_square",
"type" : "leg"
}
Catalog
{
"_id" : "EcRGzPAq79giYKrbY",
...,
"specs" : {
...,
"models" : [
{
"mesh" : "Mwp5eYYZ4GZzvZuoK",
"material" : "cf4KgXw7ZK6ukdzR7"
},
{
"mesh" : "CqhS2m2RcLZ2Bm4eb",
"material" : "cf4KgXw7ZK6ukdzR7"
},
{
"mesh" : "dYP22ajALnWBwpBj2",
"material" : "cf4KgXw7ZK6ukdzR7"
}
]
}
}
Desired returned document format after aggregation:
{
"_id" : "EcRGzPAq79giYKrbY",
...,
"specs" : {
"dimensions" : {
...,
},
"models" : [
{
"mesh" : {
"_id" : "Mwp5eYYZ4GZzvZuoK",
"name" : "top_square_chamfered",
"type" : "top"
},
"material" : {
"_id" : "cf4KgXw7ZK6ukdzR7",
"name" : "parquet_wood_mahogany"
}
},
{
"mesh" : {
"_id" : "CqhS2m2RcLZ2Bm4eb",
"name" : "skirt_square",
"type" : "skirt"
},
"material" : {
"_id" : "cf4KgXw7ZK6ukdzR7",
"name" : "parquet_wood_mahogany"
}
},
{
"mesh" : {
"_id" : "dYP22ajALnWBwpBj2",
"name" : "leg_square",
"type" : "leg"
},
"material" : {
"_id" : "cf4KgXw7ZK6ukdzR7",
"name" : "parquet_wood_mahogany"
}
}
]
}
}
I haven't included any of my query code because it is so far off the mark as to just be noise. I've been trying to use aggregate, with $lookup combinations, but I'm not getting anywhere close to what I'm after. The MongoDB v3.6 pipeline syntax would make this much easier... but I'm at a complete loss in v3.4.
I would like to avoid using multiple database requests to combine this information if at all possible. Any assistance of advice would be greatly appreciated!
EDIT: Working solution -
db.catalog.aggregate([
{ "$lookup": {
"from": 'models',
"localField": "specs.models.mesh",
"foreignField": "_id",
"as": "models.mesh"
}},
{ "$lookup": {
"from": 'materials',
"localField": "specs.models.material",
"foreignField": "_id",
"as": "models.material"
}},
{ "$unwind": "$models.mesh" },
{ "$unwind": "$models.material" },
{ "$group":{
"_id": "$_id",
"title": { "$first": "$title" },
"desc": { "$first": "$desc" },
"thumbnail": { "$first": "$thumbnail" },
"createdBy": { "$first": "$createdBy" },
"createdAt": { "$first": "$createdAt" },
"specs": { "$first": "$specs" },
"models": { "$push": "$models" }
}},
{ "$project": {
"_id": "$_id",
"title": "$title",
"desc": "$desc",
"thumbnail": "$thumbnail",
"createdBy": "$createdBy",
"createdAt": "$createdAt",
"specs.dimensions": "$specs.dimensions",
"specs.models": "$models",
}}
])
You can try below aggregation
db.catalog.aggregate([
{ "$lookup": {
"from": 'models',
"localField": "specs.models.mesh",
"foreignField": "_id",
"as": "models.mesh"
}},
{ "$lookup": {
"from": 'materials',
"localField": "specs.models.material",
"foreignField": "_id",
"as": "models.material"
}},
{ "$unwind": "$models.mesh" },
{ "$unwind": "$models.material" },
{ "$group":{
"_id": "$_id",
"title": { "$first": "$title" },
"desc": { "$first": "$desc" },
"thumbnail": { "$first": "$thumbnail" },
"createdBy": { "$first": "$createdBy" },
"createdAt": { "$first": "$createdAt" },
"specs": { "$first": "$specs" },
"models": { "$push": "$models" }
}},
{ "$project": {
"title": "$title",
"desc": "$desc",
"thumbnail": "$thumbnail",
"createdBy": "$createdBy",
"createdAt": "$createdAt",
"specs.dimensions": "$specs.dimensions",
"specs.models": "$models",
}}
])

$lookup nested array in mongodb

I am struggling with the newish (lovely) lookup operator in MongoDB. I have 3 collections:
artists
{
"_id" : ObjectId("5b0d2b2c7ac4792df69a9942"),
"name" : "Dream Theater",
"started_in" : NumberInt(1985),
"active" : true,
"country" : "US",
"current_members" : [
ObjectId("5b0d2a7c7ac4792df69a9941")
],
"previous_members" : [
ObjectId("5b0d2bf57ac4792df69a9954")
],
"albums" : [
ObjectId("5b0d16ee7ac4792df69a9924"),
ObjectId("5b0d47667ac4792df69a9994")
],
"genres" : [
"prog metal",
"prog rock"
]
}
Albums
{
"_id" : ObjectId("5b0d16ee7ac4792df69a9924"),
"title" : "Images and words",
"released" : ISODate("1992-07-07T00:00:00.000+0000"),
"songs" : [
ObjectId("5b0d15ab7ac4792df69a9916"),
ObjectId("5b0d15ee7ac4792df69a991e"),
ObjectId("5b0d2db37ac4792df69a995d"),
ObjectId("5b0d2dbe7ac4792df69a995e"),
ObjectId("5b0d2dcb7ac4792df69a995f"),
ObjectId("5b0d2dd87ac4792df69a9960"),
ObjectId("5b0d2de27ac4792df69a9961"),
ObjectId("5b0d2dec7ac4792df69a9962")
],
"type" : "LP"
}
{
"title" : "Awake",
"released" : ISODate("1994-10-04T00:00:00.000+0000"),
"songs" : [
ObjectId("5b0d470d7ac4792df69a9991")
],
"type" : "LP",
"_id" : ObjectId("5b0d47667ac4792df69a9994")
}
Songs
{
"_id" : ObjectId("5b0d15ab7ac4792df69a9916"),
"title" : "Pull me under"
}
{
"_id" : ObjectId("5b0d15ee7ac4792df69a991e"),
"title" : "Another day"
}
{
"title" : "Take the time",
"_id" : ObjectId("5b0d2db37ac4792df69a995d")
}
{
"title" : "Surrounded",
"_id" : ObjectId("5b0d2dbe7ac4792df69a995e")
}
{
"title" : "Metropolis - part I",
"_id" : ObjectId("5b0d2dcb7ac4792df69a995f")
}
{
"title" : "Under a glass moon",
"_id" : ObjectId("5b0d2dd87ac4792df69a9960")
}
{
"title" : "Wait for sleep",
"_id" : ObjectId("5b0d2de27ac4792df69a9961")
}
{
"title" : "Learning to live",
"_id" : ObjectId("5b0d2dec7ac4792df69a9962")
}
{
"title" : "6:00",
"_id" : ObjectId("5b0d470d7ac4792df69a9991")
}
I can easily do an aggregation with $lookup to get the detailed albums array, but how do I get also the detailed songs in the corresponding albums?
I would like to extend the following query:
db.artists.aggregate([ {
$lookup: {
from: "albums",
localField: "albums",
foreignField: "_id",
as: "albums"
}
}]).pretty()
If you have mongodb version 3.6 then you can try with nested $lookup aggregation...
db.collection.aggregate([
{ "$lookup": {
"from": Albums.collection.name,
"let": { "albums": "$albums" },
"pipeline": [
{ "$match": { "$expr": { "$in": [ "$_id", "$$albums" ] } } },
{ "$lookup": {
"from": Songs.collection.name,
"let": { "songs": "$songs" },
"pipeline": [
{ "$match": { "$expr": { "$in": [ "$_id", "$$songs" ] } } }
],
"as": "songs"
}}
],
"as": "albums"
}}
])
And for long-winded explanation you can go through $lookup multiple levels without $unwind?
Or If you have mongodb version prior to 3.6
db.collection.aggregate([
{ "$lookup": {
"from": Albums.collection.name,
"localField": "albums",
"foreignField": "_id",
"as": "albums"
}},
{ "$unwind": "$albums" },
{ "$lookup": {
"from": Songs.collection.name,
"localField": "albums.songs",
"foreignField": "_id",
"as": "albums.songs",
}},
{ "$group": {
"_id": "$_id",
"name": { "$first": "$name" },
"started_in": { "$first": "$started_in" },
"active": { "$first": "$active" },
"country": { "$first": "$country" },
"albums": {
"$push": {
"_id": "$albums._id",
"title": "$albums.title",
"released": "$albums.released",
"type": "$albums.type",
"songs": "$albums.songs"
}
}
}}
])

Mongodb aggregate three collections

Learning MongoDB for the past two days and I am trying to aggregate three collections but unable to achieve it
Below are the four collection maintaining in the database
university
{
"_id" : "5834ecf7432d92675bde9d82",
"name": "NIFT"
}
college
{
"_id" : "5834ecf7432d92675bde9d83",
"name": "NIFT Hyderabad",
"university_id":"5834ecf7432d92675bde9d82"
}
departments
{
"_id" : "5834ecf7432d92675bde9d84",
"department_name": "Fashion Technology",
"college_id" : "5834ecf7432d92675bde9d83"
},
{
"_id" : "5834ecf7432d92675bde9d85",
"department_name": "Merchandising",
"college_id" : "5834ecf7432d92675bde9d83"
}
Sections
{
"_id" : "5834ecf7432d92675bde9d86",
"section_name": "A",
"students" : "56",
"department_id":"5834ecf7432d92675bde9d84"
},
{
"_id" : "5834ecf7432d92675bde9d87",
"section_name": "B",
"students" : "60",
"department_id":"5834ecf7432d92675bde9d84"
},
{
"_id" : "5834ecf7432d92675bde9d86",
"section_name": "A",
"students" : "55",
"department_id":"5834ecf7432d92675bde9d85"
},
{
"_id" : "5834ecf7432d92675bde9d87",
"section_name": "B",
"students" : "44",
"department_id":"5834ecf7432d92675bde9d85"
}
Here I am trying to achieve the output in the below format
Expected Output
[{
"_id": "5834ecf7432d92675bde9d83",
"name": "NIFT Hyderabad",
"university_id": "5834ecf7432d92675bde9d82",
"departments": [{
"_id": "5834ecf7432d92675bde9d84",
"department_name": "CSE",
"college_id": "5834ecf7432d92675bde9d83",
"sections": [{
"_id": "5834ecf7432d92675bde9d86",
"section_name": "A",
"students": "56",
"department_id": "5834ecf7432d92675bde9d84"
}, {
"_id": "5834ecf7432d92675bde9d87",
"section_name": "B",
"students": "60",
"department_id": "5834ecf7432d92675bde9d84"
}]
},
{
"_id": "5834ecf7432d92675bde9d85",
"department_name": "Mechanical",
"college_id": "5834ecf7432d92675bde9d83",
"sections": [{
"_id": "5834ecf7432d92675bde9d86",
"section_name": "A",
"students": "55",
"department_id": "5834ecf7432d92675bde9d85"
},
{
"_id": "5834ecf7432d92675bde9d87",
"section_name": "B",
"students": "44",
"department_id": "5834ecf7432d92675bde9d85"
}
]
}
]
}]
But, I am getting department and sections in separate arrays for college but not able to get like in the above format
Query
db.college.aggregate([
{"$match": { "university_id": "5834ecf7432d92675bde9d82" } },
{"$lookup": {
"localField": "_id",
"from": "departments",
"foreignField": "college_id",
"as": "departments"
}},
{"$unwind":"$departments"},
{$group : {_id : "$_id", departments : {$push : "$departments" }}},
{"$lookup": {
"localField": "departments._id",
"from": "sections",
"foreignField": "department_id",
"as": "sections"}
}
])
Can any one help me to solve this issue, it will be very helpful for me.
You can try below aggregation query.
The below query pushes the sections into department when they are joined and $group to push department to create the final structure.
db.college.aggregate([
{
"$match": {
"university_id": "5834ecf7432d92675bde9d82"
}
},
{
"$lookup": {
"localField": "_id",
"from": "departments",
"foreignField": "college_id",
"as": "departments"
}
},
{
"$unwind": {
"path": "$departments",
"preserveNullAndEmptyArrays": true
}
},
{
"$lookup": {
"localField": "departments._id",
"from": "sections",
"foreignField": "department_id",
"as": "departments.sections"
}
},
{
"$group": {
"_id": "$_id",
"name": {
"$first": "$name"
},
"university_id": {
"$first": "$university_id"
},
"departments": {
"$push": "$departments"
}
}
}
])