Look for only specific key in child collection in $lookup - mongodb

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.

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 - 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.

how to connect multiple level collection in mongodb using aggregate?

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

MongoDB Merge two properties from different collections into one and sort them

I'm faced into the issue when I'm trying to merge the results of two MongoDB lookup's into one property, and then I want to unwind them and sort.
I have a problem with merging the results of lookup's.
Here is the actual code:
db.getCollection('collectionA').aggregate([
{
$lookup:
{
from: 'collectionB',
localField: "_id",
foreignField: "collectionAKey",
as: "collectionBInfo"
}
},
{
$lookup:
{
from: 'collectionC',
localField: "_id",
foreignField: "collectionAKey",
as: "collectionCInfo"
}
},
/// then I just want to create one property from both of lookup's, unwind them and sort by the timestamp
{
$unwind: "$mergedCollectionsAandB"
},
{
$sort: {
"mergedCollectionsAandB.timestamp": -1
}
}
])
Here is a models of collections:
CollectionA
_id
name
CollectionB
_id
timestamp
collectionAKey
CollectionC
_id
timestamp
collectionAKey
I assume that it's possible by using $mergeObjects MongoDB operator, but I'm stuck a little bit how to do it in a right way. Is that possible? Thanks in advance.
So the final version of my query looks like that, it's what I was looking for:
db.getCollection('collectionA').aggregate([
{
$lookup:
{
from: 'collectionB',
localField: "_id",
foreignField: "collectionAKey",
as: "collectionBInfo"
}
},
{
$lookup:
{
from: 'collectionC',
localField: "_id",
foreignField: "collectionAKey",
as: "collectionCInfo"
}
},
{
$project: {
"mergedCollectionsAandB": { $concatArrays: ["$collectionBInfo", "$collectionCInfo"] }
}
},
{
$unwind: "$mergedCollectionsAandB"
},
{
$sort: {
"mergedCollectionsAandB.timestamp": -1
}
}
])

Need a workaround for lookup of a string to objectID foreignField

I'm new to mongodb and currently I'm facing this problem,
db.medical_records.aggregate([
{
"$group": {
"_id": {
"disease_id": "$disease_id" //a string
}, "count": { "$sum": 1 }
}
},
{
"$addFields": {
"disease_id": { "$toObjectId": "$disease_id" }
// i tried to change it into objectID so i could $lookup it
}
},
{
"$lookup": {
"from": "diseases",
"localField": "disease_id",
"foreignField": "_id",
"as": "disease"
}
}
])
this is an example of my medical record collection
{
"_id" : ObjectId("5989c8f13f3958120800682e"),
"disease_id" : "5989c8f13f3958120800682f",
"patient_id" : "5989c8f13f3958120800681f"
}
disease collection
{
"_id" : ObjectId("5989c8f13f3958120800682f"),
"name" : "Culpa autem officia.",
"code" : "Est aperiam."
}
and the result I expect is kind of,
{
"_id" : {disease_id: 5989c8f13f3958120800682f},
"count" : 20,
"disease" : {
"_id" : ObjectId("5989c8f13f3958120800682f"),
"name" : "Culpa autem officia.",
"code" : "Est aperiam."
}
}
I need to join my medical record collection to disease collection as queried above.
When I tried to lookup it to disease collection it failed as foreignField is not the same type as the localField. I've been trying for some time to find a workaround on this problem. And the query above returned another error,
Unrecognized expression '$toObjectId'
This problem might have been asked several times, but I really need a workaround on this problem, please help
New in 4.0: https://docs.mongodb.com/manual/reference/operator/aggregation/toObjectId/
// Define stage to add convertedId field with converted _id value
idConversionStage = {
$addFields: {
convertedId: { $toObjectId: "$_id" }
}
};
// Define stage to sort documents by the converted qty values
sortStage = {
$sort: { "convertedId": -1 }
};
db.orders.aggregate( [
idConversionStage,
sortStage
])
Whew... After going through all the docs and stackoverflow answers, I will like to recommend a simple fix.
When saving your users_id or any document Object id in another collection make sure the dataType remains ObjectId or cast it to ObjectId before saving the document.
Then using the documentation on Mongodb alone without trying to cast or bind.
db.orders.aggregate([
{
$lookup:
{
from: "users",
localField: "user_id",
foreignField: "_id",
as: "inventory_docs"
}
}
])
This case user_id is coming from your current model then _id is coming from your users collections which both are already ObjectId by Origin.
When saving a ref to a new collection for example. user_id in orders collection. make sure the dataType is ObjectID. then you can use the ObjectID without having to convert. For example vehicles and drivers collection. by default aggregate will match dataType to dataType this also maintain the speed of your query.
await Vehicle.aggregate([{
$lookup: {
from: 'users',
localField: "driver_id",
foreignField: "_id",
as: "users"
}
}]).then(vehicles => {
data.status = 200;
data.message = "Vehicles retreived!";
data.data = vehicles;
}).catch(err => {
// console log value
console.log(err);
data.status = 500;
data.message = "Error retreiving vehicle";
data.data = JSON.stringify(err);
});
Answer only valid for versions < 4 of MongoDB:
This cannot be done with you current data structure.
Also see here: Mongoose $lookup where localField is a string of an ObjectId in foreignField
And here: Is it possible to compare string with ObjectId via $lookup
However, why don't you change the data in your medical record collection to this:
{
"_id" : ObjectId("5989c8f13f3958120800682e"),
"disease_id" : ObjectId("5989c8f13f3958120800682f"), // note the ObjectId
"patient_id" : ObjectId("5989c8f13f3958120800681f") // note the ObjectId
}
Given this format you can get what you want using the following query:
db.medical_records.aggregate([
{
"$group": {
"_id": {
"disease_id": "$disease_id" //a string
}, "count": { "$sum": 1 }
}
},
{
"$lookup": {
"from": "diseases",
"localField": "_id.disease_id",
"foreignField": "_id",
"as": "disease"
}
}
])
EDIT based on your comment below:
There is no $toObjectId operator in MongoDB. Try searching the documentation and you'll find nothing! So there simply is no way to achieve your goal without changing your data. I do not know the "eloquent laravel-mongodb" framework you're mentioning but based on its documentation I am thinking your models might need some tuning. How do you model your relationship right now?