MongoDB Aggregate - how to have TWO '$lookup' operations work? - mongodb

I have the following aggregate query:
db.getCollection('village').aggregate([
{
"$match": { _id: "111" }
},
{
"$lookup": {
from: "character",
localField: "chieftainId",
foreignField: "_id",
as: "chieftain"
},
"$lookup": {
from: "character",
localField: "villagerIds",
foreignField: "_id",
as: "villager"
}
},
{ "$project" : { "villagerIds" : 0}}
])
And this is its result:
{
"_id" : "111",
"name" : "MyVillage",
"chieftainId" : "222",
"reputation" : 0,
"villagers" : [
{
"_id" : "333",
"name" : "Bortan",
"age" : 21,
"bloodlineId" : "7f02191f-90af-406e-87ff-41d5b4387999",
"villageId" : "foovillage",
"professionId" : "02cbb10a-6c0f-4249-a932-3f40e12d32c5"
},
{
"_id" : "444",
"name" : "Blendi",
"age" : 21,
"bloodlineId" : "b3a8ffeb-27aa-4e2e-a8e6-b382554f326a",
"villageId" : "foovillage",
"professionId" : "45dc9350-c84a-491d-a49a-524834dd5773"
}
]
}
As you can see villagerIds has been resolved to villagers. However chieftainId has not been resolved to chieftain. When I omit the second $lookup (with villager) then chieftain is being resolved successfully. It seems that only one $lookup will be effective and not both. Any idea how I can make both work?

Use separate pipeline for both $lookup
db.getCollection('village').aggregate([
{
"$match": { _id: "111" }
},
{
"$lookup": {
from: "character",
localField: "chieftainId",
foreignField: "_id",
as: "chieftain"
}
},
{
"$lookup": {
from: "character",
localField: "villagerIds",
foreignField: "_id",
as: "villager"
}
},
{ "$project" : { "villagerIds" : 0}}
])

Related

Aggregate Query returns null array

I have three collections in EmployeeDB.
Employee
{
"user_id" : "EMP001",
"FirstName" : "Manoj",
"LastName" : "sharma",
"Status" : "0/1",
"CreateDate" : "1988-10-11T18:30:00.000Z"
}
EmpContact
{
"user_id" : "EMP001",
"Phone" : "9999999999",
"Email" : "xyz#gmail.com"
}
EmpInfo
{
"user_id" : "EMP001",
"Gender" : "Male",
"Age" : NumberInt(20),
"Designation" : "manager",
"Salary" : "5000"
}
I need to group the three collections and display all fields.
I tried the below code,
db.Employee.aggregate([
{$lookup: {from: "EmpContact",localField: "user_id",foreignField: "user_id",as: "contact"}},
{$unwind:"$EmpContact"},
{$lookup: {from: "EmpInfo",localField: "user_id",foreignField: "user_id",as: "empinfo"}},
{$unwind:"$EmpInfo"},
{
$match:{user_id : "EMP001"}
}
]).toArray();
But I got an empty array, Please help me...
The problem comes from your $unwind.
Since you name your field as contact in the $lookup, the field EmpContact doesn't exist.
The same thing for the second unwind, EmpInfo doesn't exist but empinfo does
Here is the full query
db.Employee.aggregate([
{
$lookup: {
from: "EmpContact",
localField: "user_id",
foreignField: "user_id",
as: "contact"
}
},
{
$unwind: "$contact"
},
{
$lookup: {
from: "EmpContact",
localField: "user_id",
foreignField: "user_id",
as: "empinfo"
}
},
{
$unwind: "$empinfo"
},
{
$match: {
user_id: "EMP001"
}
}
])
Try it here

MongoDB Aggregation - How can I" $lookup" a nested document "_id"?

I successfully thanks to the help of the people here managed to $lookup two IDs in my document with their representive document in another collection. The next step I need to take is to further lookup a "nested" ID (refering to a document in another collection).
I tried to simply put another $lookup pipeline up but that just worked part-wise.
So it happens that an "empty" document was included into the chieftain attributes and all other attributes of chieftain where somewhat removed.
See my current aggregate:
db.getCollection('village').aggregate([
{
"$match": { _id: "111" }
},
{
"$lookup": {
from: "character",
localField: "chieftainId",
foreignField: "_id",
as: "chieftain"
}
},
{
"$lookup": {
from: "character",
localField: "villagerIds",
foreignField: "_id",
as: "villagers"
}
},
{
"$lookup": {
from: "bloodline",
localField: "chieftain.bloodline",
foreignField: "_id",
as: "chieftain.bloodline"
}
},
{ "$project" : { "villagerIds" : 0, "chieftainId" : 0}},
{ "$unwind" : "$chieftain" }
])
The result of that is the following:
{
"_id" : "111",
"name" : "MyVillage",
"reputation" : 0,
"chieftain" : {
"bloodline" : []
},
"villagers" : [
{
"_id" : "333",
"name" : "Bortan",
"age" : 21,
"bloodlineId" : "7f02191f-90af-406e-87ff-41d5b4387999",
"villageId" : "foovillage",
"professionId" : "02cbb10a-6c0f-4249-a932-3f40e12d32c5"
},
{
"_id" : "444",
"name" : "Blendi",
"age" : 21,
"bloodlineId" : "b3a8ffeb-27aa-4e2e-a8e6-b382554f326a",
"villageId" : "foovillage",
"professionId" : "45dc9350-c84a-491d-a49a-524834dd5773"
}
]
}
I expected the chieftain part to look like this (this is how the chieftain document looks like without the 'last' $lookup I added):
"chieftain" : {
"_id" : "222",
"name" : "Bolzan",
"age" : 21,
"bloodlineId" : "7c2926f9-2f20-4ccf-846a-c9966970fa9b", // this should be resolved/lookedup
"villageId" : "foovillage",
},
At the point of the lookup, chieftan is an array, so setting the chieftan.bloodline replaces the array with an object containing only the bloodline field.
Move the { "$unwind" : "$chieftain" } stage to before the bloodline lookup stage so the lookup is dealing with an object.

MongoDB $lookup

I have a join collection - and I want to pull back PEOPLE data with their Parents...
How can I do this -
PEOPLE
[
{
"_id" : ObjectId("3a9ccf7de6348936d88b3601"),
"first_name" : "John",
"last_name" : "Doe"
},
{
"_id" : ObjectId("3a9ccf7de6348936d88b3602"),
"first_name" : "Jane",
"last_name" : "Doe"
},
{
"_id" : ObjectId("3a9ccf7de6348936d88b3603"),
"first_name" : "Bobby",
"last_name" : "Doe"
}
]
RELATIONS
[
{
"_id" : ObjectId("5aa9a283e40f140014485116"),
"person_id" : ObjectId("3a9ccf7de6348936d88b3603"),
"parent_id" : ObjectId("3a9ccf7de6348936d88b3601"),
"position": "father"
},
{
"_id" : ObjectId("5aa9a283e40f140014485116"),
"person_id" : ObjectId("3a9ccf7de6348936d88b3603"),
"parent_id" : ObjectId("3a9ccf7de6348936d88b3602"),
"position": "mother"
}
]
I want something like this:
[
{
"_id" : ObjectId("3a9ccf7de6348936d88b3603"),
"first_name" : "Bobby",
"last_name" : "Doe",
"relations: : [
{
"_id" : ObjectId("3a9ccf7de6348936d88b3602"),
"first_name" : "Jane",
"last_name" : "Doe",
"position": "mother"
},
{
"_id" : ObjectId("3a9ccf7de6348936d88b3601"),
"first_name" : "John",
"last_name" : "Doe",
"position": "father"
}
]
}
]
I know I need aggregate and $lookup. but I cant get past the most basic
db.getCollection('people')
.aggregate([
{ $lookup: {
from: 'relations',
localField: 'person_id',
foreignField: '_id',
as: 'relations'
}
}
])
You need to run $lookup twice and second one should have people as a "from" value:
db.people.aggregate([
{
$lookup: {
from: "relations",
localField: "_id",
foreignField: "person_id",
as: "relations"
}
},
{
$lookup: {
from: "People",
localField: "relations._id",
foreignField: "_id",
as: "relations"
}
}
])
Mongo Playground
here's another way to do it using a nested lookup/sub-pipeline. also shows the position of relations.
db.people.aggregate(
[
{
$lookup:
{
from: 'relations',
let: { person_id: '$_id' },
pipeline: [
{
$match: {
$expr: { $eq: ["$person_id", "$$person_id"] }
}
},
{
$lookup: {
from: "people",
localField: "parent_id",
foreignField: "_id",
as: "person"
}
},
{
$replaceWith: {
$mergeObjects: [{ $arrayElemAt: ["$person", 0] }, "$$ROOT"]
}
},
{
$project: {
_id: "$parent_id",
position: 1,
first_name: 1,
last_name: 1
}
}
],
as: 'relations'
}
}
])
https://mongoplayground.net/p/EJB-1WfanuY

Aggregation when one document contains the values of multiple Documents

am trying to aggregate values of multiple documents in one, but unable to do it,
db.doc1.find().pretty();
{
"addressId" : ObjectId("5901a5f83541b7d5d3293768"),
"socialId" : ObjectId("5901b0f6d318b072ceea44fb")
}
db.doc2.find().pretty();
{
"_id" : ObjectId("5901a5f83541b7d5d3293768"),
"address" : "Gurgaon",
"mob" : "9876543211"
}
db.doc3.find().pretty();
{
"_id" : ObjectId("5901b0f6d318b072ceea44fb"),
"fbURLs" : "http://www.facebook.com",
"twitterURLs" : "http://www.twitter.com"
}
My Query
db.doc2.aggregate([
{ $match: { "_id" : ObjectId("5901a5f83541b7d5d3293768") } },
{
$lookup:
{
from: "doc1",
localField: "_id",
foreignField: "addressId",
as: "Doc2Details"
}
}
]).pretty();
db.doc3.aggregate([
{ $match: { "_id" : ObjectId("5901b0f6d318b072ceea44fb") } },
{
$lookup:
{
from: "doc1",
localField: "_id",
foreignField: "socialId",
as: "tireConfig"
}
}
]).pretty();
I want match values from Doc1 and combine values of doc2 and doc3, can someone help me with this?

How to $slice results of foreign collection documents array when mongodb $lookup is used

Here is my code
AbcSchema.aggregate([
{ $match: query },
{
$lookup: { from: 'xyz', localField: '_id', foreignField: 'place_id', as: 'xyzArray' }
}
])
Right now Im getting this result :
{
_id : "abc1",
abcfield1 : "...",
abcfield2 : "...",
xyzArray : [{_id : "xyz1", place_id : "abc1", xyzfield1 : "..."},
{_id : "xyz2", place_id : "abc1", xyzfield1 : "..."},
{_id : "xyz3", place_id : "abc1", xyzfield1 : "..."},
...] //all matching results
}
So now lets say I want only 2 documents in xyzArray, then how can I achieve that?
My requirement is to get limit the 'xyzArray' length to 'n' .
Here is the dynamic query. You can change the number inside the $slice to get the required number of array elements.
db.AbcSchema.aggregate([
{ $match: {_id : "abc1"} },
{
$unwind: "$_id"
},
{
$lookup: { from: 'xyz', localField: '_id', foreignField: 'place_id', as: 'xyzArray' }
},
{ $project: { abcfield1 : 1,
abcfield2 : 1,
xyzArrayArray: { $slice: ["$xyzArray", 2] }
}
}
]).pretty();
One possible solution using $project and $arrayElemAt
db.AbcSchema.aggregate([
{ $match: {_id : "abc1"} },
{
$unwind: "$_id"
},
{
$lookup: { from: 'xyz', localField: '_id', foreignField: 'place_id', as: 'xyzArray' }
},
{ $project: { abcfield1 : 1,
abcfield2 : 1,
firstxyzArray: { $arrayElemAt: [ "$xyzArray", 0 ] },
secondxyzArray: { $arrayElemAt: [ "$xyzArray", 1 ] }
}
}
]).pretty();
Sample Output:-
{
"_id" : "abc1",
"abcfield1" : "11",
"abcfield2" : "22",
"firstxyzArray" : {
"_id" : "xyz1",
"place_id" : "abc1",
"xyzfield1" : "xyzf1"
},
"secondxyzArray" : {
"_id" : "xyz2",
"place_id" : "abc1",
"xyzfield1" : "xyzf1"
}
}