how to connect multiple level collection in mongodb using aggregate? - mongodb

i have 3 collection
vehicleModel
{
id_vehicleModel: {type:Number}
vehicleModelName: {type:String}
}
vehicleModel_vehicleBodyType
{
id_vehicleModel: {type:Number}
id_vehicleBodyType: {type:Number}
}
vehicleBodyType
{
id_vehicleBodyType: {type:Number}
vehicleBodyTypeName: {type:String}
}
i want to join that 3 collection so i can get result
{
id_vehicleModel: 1
vehicleModelName: "Tesla"
VModel_VBodyType: {
id_vehicleModel: 1,
id_vehicleBodyType: 4
VBodyType: {
id_vehicleBodyType: 4
vehicleBodyTypeName: "Sport"
}
}
}
i've try to aggregate with
schemaVehicleModel.aggregate([
{
$lookup: {
from: "vehicleModel_vehicleBodyType",
localField: "id_vehicleModel",
foreignField: "id_vehicleModel",
as: "VModel_VBodyType",
},
},
{ $unwind: "$VModel_VBodyType" },
])
but how can i aggregate the next collection?

You can refer to sample here: https://mongoplayground.net/p/np_4eGhIM3C
Your aggregation would look like below:
db.vehicleModel.aggregate([
{
"$lookup": {
"from": "vehicleModel_vehicleBodyType",
"localField": "id_vehicleModel",
"foreignField": "id_vehicleModel",
"as": "vehicleModel_vehicleBodyType_Join"
}
},
{
"$unwind": "$vehicleModel_vehicleBodyType_Join"
},
{
"$lookup": {
"from": "vehicleBodyType",
"localField": "vehicleModel_vehicleBodyType_Join.id_vehicleBodyType",
"foreignField": "id_vehicleBodyType",
"as": "vehicleBodyType_Join"
}
},
{
"$unwind": "$vehicleBodyType_Join"
}
])
Aggregation stages:
lookup: to join vehicleModel with vehicleModel_vehicleBodyType based on field id_vehicleModel
unwind: to convert joined array objects to objects
lookup: to join result with vehicleBodyType based on field id_vehicleBodyType
unwind: to convert joined array objects to objects
you can add project to get the desired fields in output.
Please note it does not matter how many lookups you want to do key here is you should now "localField" & "foreignField" and then just do the lookup the way you do for any first lookup stage

Related

Aggregate Budgets by Customers

I have two collections: customers and budgets.
I need to get all customers with the related budgets inside an array.
My problem is, I need to start the aggregate from the budgets collection.
Also, I need to return customers who don't have any budgets related.
I need a list with something like this:
customer: {
Id: Guid,
Name: string,
CpfCnpj: number,
AccountantId: Guid
Budgets: []
}
How can I do that?
Here the example
You may want to "$project"/etc. the results somewhat differently, but here's one way to output a document for each customer with an array of their budgets (or an empty array if there is no budgets document for them).
db.budgets.aggregate([
{ // get customer docs with possible budgets
"$unionWith": {
"coll": "customers",
"pipeline": [
{
"$lookup": {
"from": "budgets",
"localField": "_id",
"foreignField": "CustomerId",
"as": "budgets"
}
}
]
}
},
{ // only keep budgets with a customer
"$match": {
"name": {"$exists": true}
}
},
{ // "budgets" set to empty array if missing
"$set": {
"budgets": {
"$ifNull": ["$budgets", [] ]
}
}
}
])
Try it on mongoplayground.net.
If "$unionWith" (introduced in MongoDB version 4.4) is unavailable, here's another way to do it by "transforming" (first four stages below) the original queried collection (here, budgets) into the desired collection (here, customers). The remainder of the pipeline is a simple "$lookup" to get the desired info.
db.budgets.aggregate([
{"$limit": 1},
{
"$lookup": {
"from": "customers",
"pipeline": [],
"as": "customers"
}
},
{"$unwind": "$customers"},
{"$replaceWith": "$customers"},
{
"$lookup": {
"from": "budgets",
"localField": "_id",
"foreignField": "CustomerId",
"as": "budgets"
}
}
])
Try it on mongoplayground.net.

MongoDB: aggregation $lookup with lossy data type

I have two collections:
cats
balls
"cats" collection has documents with key "ballId" of type string
"balls" collection has documents with key "_id" of type ObjectId
An $lookup inside an aggregation is able to retrieve results if the join is done on keys with the same data type. However in my case, "ballId" and "_id" are of different types. This code retrieves the cats but doesn't retrieve the related balls:
collection('cats').aggregate([
{ $match:{} },
{
$lookup: {
from: "balls",
localField: "ballId",
foreignField: "_id",
as: "balls"
}
}
]);
How can I use $lookup with lossy data type?
Use $lookup with pipeline stage.
Join both collections by converting balls' _id to string ($toString) and next compare both values as string ($eq).
db.cats.aggregate([
{
$match: {}
},
{
$lookup: {
from: "balls",
let: {
ballId: "$ballId"
},
pipeline: [
{
$match: {
$expr: {
$eq: [
{
"$toString": "$_id"
},
"$$ballId"
]
},
}
}
],
as: "balls"
}
}
])
Sample Mongo Playground

MongoDB - Select from database A documents which exists in database B without join

I am trying to select documents from one database which ids are present in other database. This query won't do:
db.getCollection('A').aggregate([
{
$lookup: {
from: "B",
localField: "_id",
foreignField: "_id",
as: "bid",
}
}
])
Because it just joins the documents which exists in B and it still gives me documents which does not present in B (the "bid" field is empty). I also want to get the original A document without additional fields.
The $lookup is actually correct way to do. You just need 1 more $unwind to filter out unmatched document and $project to keep the original schema in collection A.
db.A.aggregate([
{
"$lookup": {
"from": "B",
"localField": "_id",
"foreignField": "_id",
"as": "bid"
}
},
{
"$unwind": "$bid"
},
{
"$project": {
// remove the lookup field
bid: false
}
}
])
Here is the Mongo playground for your reference.

Look for only specific key in child collection in $lookup

Lets say I have collections as:
linkedDetails:
{
_id:ObjectId("1234avshjd"),
book_id:ObjectId("16262ahahha"),
author_id:ObjectId("127hjajaj")
}
{
_id:ObjectId("223ahha78"),
book_id:ObjectId("1681awtsy"),
author_id:ObjectId("127hjajaj")
}
{
_id:ObjectId("97ahj238"),
book_id:ObjectId("126ashs17"),
author_id:ObjectId("127hjajaj")
}
{
_id:ObjectId("138hajq12"),
book_id:ObjectId("12742ahjsn"),
author_id:ObjectId("4728haja72")
}
and another collection as:
bookDetails:
{
_id:ObjectId("16262ahahha"),
book_name:"harry potter",
book_price: 10
}
{
_id:ObjectId("1681awtsy"),
book_name:"lotr",
book_price: 10
}
{
_id:ObjectId("126ashs17"),
book_name:"song of sea",
book_price: 10
}
I want to fetch record for
author_id:ObjectId("127hjajaj")
from linkedDetails collection match for author_id.
Lookup record from bookDetails collection based on book_id present
in linkedDetails collection for fetched author_id
From bookDetails collection get only book name.
For this I tried as:
linkedDetails.aggregate({
{"$match": {
author_id:ObjectId("4728haja72")
}},
{"$lookup":{
"from":'bookDetails',
"localField": '_id',
"foreignField": 'book_id',
}}
})
If anyone needs any further information please let me know.
Your localField will be book_id and foreignField will be id.
linkedDetails.aggregate(
{
"$match": {
author_id: ObjectId("4728haja72")
}
},
{
"$lookup": {
"from": 'bookDetails',
"localField": 'book_id',
"foreignField": '_id',
"as": "books"
}
}
)
Here is the mongo playground for it.

How to get left join data using multiple condition in mongodb

I have two table
master_document_table document_table
id | title id | master_document_id | userId
1 | Profile
select * from master_document_table as md
left join document_table as d on md.id = d.master_document_id and d.userId = 2
Result:
id | title | master_document_id | userId
1 | Profile null null
how can this be achieved using mongodb i have tried & also did some research from stack overflow and did not got the expected result.
From mongo v3.2, it is possible to aggregate two or more than two collections.
But here one thing, you need to know is, you won't get the same structure as sql. You will get your data on nested document.
Your tables will look something like this in mongodb:
master_document_table (collection / table)
{
_id: ObjectId("5f28d53c00613e6ada45f702"),
title: "Profile",
}
document_table (collection / table)
{
_id: ObjectId("5f28d53c00613e6ada45e790"),
master_document_id: ObjectId("5f28d53c00613e6ada45f702"), // master_doc_table's ref id.
userId: ObjectId("5f28d53c00613e6ada327f231"), // user's ref id.
}
Your query will be:
db.document_table.aggregate([
{$match: {title: "Profile"}}, // you can give any condition here.
{
$lookup:
{
from: "master_document_table",
localField: "master_document_id",
foreignField: "_id",
as: "master_document_id"
}
}
])
Your query result will look something like:
[{
_id: ObjectId("5f28d53c00613e6ada45e790"),
master_document_id: {
_id: ObjectId("5f28d53c00613e6ada45f702"),
title: "Profile"
},
userId: ObjectId("5f28d53c00613e6ada327f231"),
}]
Now you can restructure it based on your requirement.
I would like to recommend you
https://docs.mongodb.com/manual/reference/operator/aggregation/match/
&
https://docs.mongodb.com/manual/reference/operator/aggregation/lookup/
article from mongodb docs.
Hope this will help to resolve your problem.
Inorder to join two tables you can use the $lookup aggregation pipeline operator in version 3.6 and newer. Since you need the results like LEFT OUTER JOIN, you have to $unwind the array to objects and filter out the desired results using $match conditions (ie document_table.userId = 2). Dont forget to use preserveNullAndEmptyArrays to true when unwinding.
Sample code for reference:
db.master_document_table.aggregate([
{ "$lookup": {
"from": "document_table",
"localField": "id",
"foreignField": "master_document_id",
"as": "document_table"
}},
{ $unwind: { path: "$document_table", preserveNullAndEmptyArrays: true } },
{
$match:{
$or:[
{ "document_table": { $exists: false } },
{"document_table.userId" : 2 }
]
}
}
])
I some how managed to get desire result thanks for the help
db.master_document_table.aggregate([
{
$lookup: {
from: 'document_table',
localField: 'id',
foreignField: 'master_document_id',
as: 'users'
}
},
{
$project: {
title: 1,
users: {
$filter: {
input: '$users',
as: 'user',
cond: {
$eq: ['$$user.userId', 2]
}
}
}
}
}
]);