Recursive Mongo Find - mongodb

So I want to find the names of people who are the partners of the current person.
My data looks like this:
{
_id: objectId,
first_name: string,
last_name: string,
partners: [objectId]
}
I have tried this aggregate/lookup - but it returns incorrect results
module.exports.getUserPartners = function( user_id, callback ) {
const query = [
{
$unwind: "$partners"
},
{
$lookup: {
from: "people",
localField: "partners",
foreignField: "_id",
as: "people_partners"
}
},
{
$match: { "_id": user_id }
},
{
$project: {
first_name: 1,
last_name: 1
}
}
];
People.aggregate( query, callback );
}
If my data looks like this: (and I pass '123' as the user_id)
{
_id: '123',
first_name: "bob",
last_name: "smith",
partners: ['234','345']
},{
_id: '234',
first_name: "sally",
last_name: "smartypants",
partners: ['789']
},{
_id: '345',
first_name: "martin",
last_name: "tall",
partners: []
}
I get these results from the above aggregate lookup:
[{
_id: '123',
first_name: "bob",
last_name: "smith"
},{
_id: '123',
first_name: "bob",
last_name: "smith"
}]
when I EXPECT these results:
[{
_id: '234',
first_name: "sally",
last_name: "smartypants"
},{
_id: '345',
first_name: "martin",
last_name: "tall"
}]
*NOTE - I added the $unwind based on recommendation form docs and this article
https://docs.mongodb.com/manual/reference/operator/aggregation/lookup/#unwind-example

Please check this aggregate query. look like its complex.
db.getCollection('people').aggregate([
{$match : { "_id" : "123"} },
{
$unwind:
{
path:"$partners",
preserveNullAndEmptyArrays: true
}
},
{
$lookup: {
from: "people",
localField: "partners",
foreignField: "_id",
as: "people_partners"
}
},
{
$unwind:
{
path:"$people_partners",
preserveNullAndEmptyArrays: false
}
},
{
$project: {
_id : '$people_partners._id',
first_name : '$people_partners.first_name',
last_name : '$people_partners.last_name',
}
}
])

Related

3 level nested lookup with arrays

I am building an apllication with mongoose and node.js that enables you to post, comment, and like both the posts and the comments.
I am tryign to build a query that gets all the information from the db.
saw that answer nested 3 level lookup like in here MongoDB nested lookup with 3 levels.
it works, but the project filter returns me an error and if I remove it, I get some comments that looks like that:
"comments":
{
"user": [],
"likes": []
}
the argigate
postArgigate = [
{
//lookup for the post likes
$lookup: {
from: 'likes',
localField: '_id',
foreignField: 'postCommentID',
as: '_likes'
},
},
//the user wrote the post
{
$lookup: {
from: 'users',
localField: 'userID',
foreignField: '_id',
as: 'user'
},
},
{
$lookup: {
from: 'comments',
localField : 'commentsID',
foreignField : '_id',
as: 'comments'
},
},
//unwind the comments to do a nesting lookup
{
$unwind: {
path: "$comments",
preserveNullAndEmptyArrays: true
}
},
//lookup in the comments likes
{
$lookup: {
from: 'likes',
localField : 'comments._id',
foreignField : 'postCommentID',
as: 'comments._likes'
},
},
//lookup in the user that wrote the comment
{
$lookup: {
from: 'users',
localField: 'comments.userID',
foreignField: '_id',
as: 'comments.user'
},
},
{
$set: {
'comments.likes':'$comments._likes.userID',
'likes': '$_likes.userID'
}
},
{
$project: {
/////////FILTER NOT WORKING////////////
// 'comments': {
// $filter: { input: "$comments", as: "cm", cond: { $ifNull: ["$$cm._id", false] } }
// } , //returns an error
'comments.userID':0,
'comments.user.password':0,
'comments._likes':0,
'userID':0,
'user.password':0,
'commentsID':0,
'_likes': 0,
}
},
{
$group: {
_id : "$_id",
user:{$first:'$user'},
likes:{$first:'$likes'},
date:{$first:'$date'},
content:{$first:'$content'},
comments: { $push: "$comments" },
}
}
]
thank you for you help!
comments
[
{
_id: ObjectId("com1"),
userID:ObjectId("eliran1"),
content: 'comment 1',
date: 2022-03-02T22:55:16.224Z,
},
{
_id:ObjectId("com2"),
userID: ObjectId("eliran1"),
content: 'comment 2',
date: 2022-03-05T18:34:52.890Z,
__v: 0
}
]
posts
[
{
_id: new ObjectId("post1"),
userID: new ObjectId("eliran1"),
content: 'post 1',
commentsID: [],
date: 2022-03-05T18:28:11.487Z,
},
{
_id: new ObjectId("post2"),
userID: new ObjectId("shira1"),
content: 'post 2',
commentsID: [ObjectId("com1"),
ObjectId("com2") ],
date: 2022-03-05T18:34:46.364Z,
}
]
users
[
{
_id: new ObjectId("eliran1"),
user: 'eliran222',
password: '123456789',
email: 'fdfd#fdfd.com33',
gender: true,
__v: 0
},
{
_id: new ObjectId("shira1"),
user: 'shira3432',
password: '123456789',
email: 'fdrf#gfge.com',
gender: false,
}
]
likes
[
{
_id: ObjectId("like1"),
userID: ObjectId("eliran1"),
postCommentID:ObjectId("post1"),
},
{
_id:ObjectId("like2"),
userID: ObjectId("shira1"),
postCommentID:ObjectId("com1"),
}
]
expected results:
[
{
_id: new ObjectId("post1"),
user: {
user: 'eliran222',
email: 'fdfd#fdfd.com33',
gender: true,
},
content: 'post 1',
comments: [],
date: 2022-03-05T18:28:11.487Z,
likes:[{/*eliran`s user*/}]
},
{
_id: new ObjectId("post2"),
userID: {
_id: new ObjectId("shira1"),
user: 'shira3432',
email: 'fdrf#gfge.com',
gender: false,
},
content: 'post 2',
comments: [{
_id: ObjectId("com1"),
user:{
_id:objectId(eliran1)
user: 'eliran222',
email: 'fdfd#fdfd.com33',
gender: true,
},
content: 'comment 1',
date: 2022-03-02T22:55:16.224Z,
likes:[{/*shira`s user*/}]
},
{
_id:ObjectId("com2"),
user: {
objectId(eliran1)
user: 'eliran222',
email: 'fdfd#fdfd.com33',
gender: true,
},
content: 'comment 2',
date: 2022-03-05T18:34:52.890Z,
likes:[]
}],
date: 2022-03-05T18:34:46.364Z,
}
]
}
]

MongoDb: aggregation $lookup

Given following collections.
I have to get the title field and combine to identifier
CREDENTIAL:
{
_id: ..
title: ..
}
USER_CREDENTIAL:
{
_id: ..
credential_id: .. (from credential collection)
created_at: ..
identifier: {
first_name: ..
middle_name: ..
last_name: ..
}
}
The response should be:
{
user_credential_id:
member: {
first_name:
middle_name:
last_name:
title:
created_at:
}
}
$lookup - Join user and user_credential_id with a pipeline to match the condition ($match) and decorate the document ($project).
$unwind - Deconstruct member array to multiple documents.
$project - Decorate the output document.
db.user.aggregate([
{
"$lookup": {
"from": "user_credential",
let: {
"user_credential_id": "$_id",
"title": "$title"
},
pipeline: [
{
$match: {
$expr: {
$eq: [
"$credential_id",
"$$user_credential_id"
]
}
}
},
{
$project: {
first_name: "$identifier.first_name",
middle_name: "$identifier.middle_name",
last_name: "$identifier.last_name",
title: "$$title",
created_at: "$created_at",
}
}
],
"as": "member"
}
},
{
$unwind: "$member"
},
{
$project: {
_id: 0,
user_credential_id: "$_id",
member: 1
}
}
])
Sample Mongo Playground
You can try this query
db.user.aggregate([
{
$lookup: {
from: "credentials",
localField: "credential_id",
foreignField: "_id",
as: "user_credential"
}
},
{
$unwind: "$user_credential"
},
{
$project: {
_id: 0,
user_credential_id: "$credential_id",
member: {
first_name: "$identifier.first_name",
middle_name: "$identifier.middle_name",
last_name: "$identifier.last_name",
title: "$user_credential.title",
created_at: "$created_at",
}
}
}
])
You can check it out here

MongoDB: How to populate the nested object with lookup query?

I am fetching list of records having some nested reference to other collection, I want to populate that nested ObjectId which is inside array of objects, with mongoDb aggregate lookup query.
This is how DB collection structure is:
{
subject: {type: String},
body: {type: String},
recipients: [{
userId: {type: mongoose.Schema.Types.ObjectId, ref: 'User'},
stutus: {type: String, enum: ['pending','accepted','rejected'], default:'pending'}
}],
sender: {type: mongoose.Schema.Types.ObjectId, ref: 'User'}
}
What I am expecting:
[{
subject: 'Some subject here.',
body: 'Lorem ipsum dolor emit set',
recipients: [{
userId: {firstName: 'John', lastName: 'Doe'},
status: 'accepted'
},{
userId: {firstName: 'Jane', lastName: 'Doe'},
status: 'accepted'
}],
sender: {firstName: 'Jacobs', 'lastName': 'Doe'}
},{
subject: 'Some subject here.',
body: 'Lorem ipsum dolor emit set',
recipients: [{
userId: {firstName: 'Jane', lastName: 'Doe'},
status: 'rejected'
},{
userId: {firstName: 'John', lastName: 'Doe'},
status: 'accepted'
}],
sender: {firstName: 'Jacobs', 'lastName': 'Doe'}
}]
Any kind help will be greatly appreciated.
$unwind deconstruct recipients array
$lookup with users collection for recipients.userId
$unwind deconstruct recipients.userId array
$lookup with users collection for sender
$unwind deconstruct sender array
$group by _id and reconstruct recipients array
db.mails.aggregate([
{ $unwind: "$recipients" },
{
$lookup: {
from: "users",
localField: "recipients.userId",
foreignField: "_id",
as: "recipients.userId"
}
},
{ $unwind: "$recipients.userId" },
{
$lookup: {
from: "users",
localField: "sender",
foreignField: "_id",
as: "sender"
}
},
{ $unwind: "$sender" },
{
$group: {
_id: "$_id",
recipients: { $push: "$recipients" },
subject: { $first: "$subject" },
body: { $first: "$body" },
sender: { $first: "$sender" }
}
}
])
Playground
Try This:
db.emails.aggregate([
{ $unwind: "$recipients" },
{
$lookup: {
from: "users",
let: { userId: "$recipients.userId", status: "$recipients.stutus" },
pipeline: [
{
$match: {
$expr: { $eq: ["$_id", "$$userId"] }
}
},
{
$project: {
"_id": 0,
"userId": {
"firstName": "$firstName",
"lastName": "$lastName",
},
"status": "$$status"
}
}
],
as: "recipient"
}
},
{
$lookup: {
from: "users",
let: { userId: "$sender" },
pipeline: [
{
$match: {
$expr: { $eq: ["$_id", "$$userId"] }
}
},
{
$project: {
"_id": 0,
"firstName": 1,
"lastName": 1
}
}
],
as: "sender"
}
},
{
$group: {
_id: "$_id",
subject: { $first: "$subject" },
body: { $first: "$body" },
recipients: { $push: { $arrayElemAt: ["$recipient", 0] } },
sender: { $first: { $arrayElemAt: ["$sender", 0] } }
}
}
]);

Mongodb aggregate lookup by _id is not working

MongoDb Aggregate lookup is not producing result while foreignField as _id.
I have two collections say users and discussions
Sample users Data:
[{
_id: 5f9c50dcfac1f091400225e3,
email: 'Peter.Parker#gmail.com',
details: { fname: 'Peter Test', lname: 'Fulton' },
},
{
_id: 5fa432bfb91fab7db60c70eb,
email: 'Spidy#xxx.com',
details: { fname: 'Frodo', lname: 'Baggins' },
},
{
_id: 5fa8ec7d3ce22610e5d15190,
email: 'tommy#xxx.com',
details: { fname: 'Tommy', lname: 'test' },
},
{
_id: 5fc38bb0b3683651be970180,
email: 'jerry#xxx.io',
},
{
_id: 5fd2340cc443d155ab38383b,
email: 'Dexter#xxx.io',
details: { fname: 'Dexter', lname: 'Lab' },
}]
Sample discussions data:
{_id: ObjectId("5fb2abd6b14fa5683979df58"),
tags: [ 'javascritp', 'css', 'html' ],
title: 'Why is this inline-block element pushed downward?',
post: 'Test Post',
learnerId: ObjectId("5f9c50dcfac1f091400225e3"),
}
Here '_id' of users is linked with 'learnerId' of 'discussions'.
My Aggregate query is like below.
db.users.aggregate([
{ $project: { "details.fname": 1, "details.lname":1,email:1, _id:1}},
{$lookup: {
from: "discussions",
localField: "learnerId",
foreignField: "_id",
as: "discussions"
}}
])
Here 'Peter Test' with _id 5f9c50dcfac1f091400225e3 linked with discussions LeanerId. But I expected discussions will populate in my result. My am seeing empty discussions array in all users collections.
[{
_id: 5f9c50dcfac1f091400225e3,
email: 'Peter.Parker#gmail.com',
details: { fname: 'Peter Test', lname: 'Fulton' },
discussions: []
},
{
_id: 5fa432bfb91fab7db60c70eb,
email: 'Spidy#xxx.com',
details: { fname: 'Frodo', lname: 'Baggins' },
discussions: []
},
{
_id: 5fa8ec7d3ce22610e5d15190,
email: 'tommy#xxx.com',
details: { fname: 'Tommy', lname: 'test' },
discussions: []
},
{
_id: 5fc38bb0b3683651be970180,
email: 'jerry#xxx.io',
discussions: []
},
{
_id: 5fd2340cc443d155ab38383b,
email: 'Dexter#xxx.io',
details: { fname: 'Dexter', lname: 'Lab' },
discussions: []
}]
Can you point out what wrong in my aggregate query here?
You have mismatched the localField and foreignField
db.users.aggregate([
{
$project: {
"details.fname": 1,
"details.lname": 1,
email: 1,
_id: 1
}
},
{
$lookup: {
from: "discussions",
localField: "_id",
foreignField: "learnerId",
as: "discussions"
}
}
])
Working Mongo playground

mongodb how to use aggregate like populate

The code example is in Mongo Playground
https://mongoplayground.net/p/W4Qt4oX0ZRP
Assume the following documents
[
{
_id: "5df1e6f75de2b22f8e6c30e8",
user: {
name: "Tom",
sex: 1,
age: 23
},
dream: [
{
label: "engineer",
industry: "5e06b16fb0670d7538222909",
type: "5e06b16fb0670d7538222951",
},
{
label: "programmer",
industry: "5e06b16fb0670d7538222909",
type: "5e06b16fb0670d7538222951",
}
],
works: [
{
name: "any engineer",
company: "5dd7fd51b0ae1837a08d00c8",
skill: [
"5dc3998e2cf66bad16efd61b",
"5dc3998e2cf66bad16efd61e"
],
},
{
name: "any programmer",
company: "5dd7fd9db0ae1837a08d00e2",
skill: [
"5dd509e05de2b22f8e67e1b7",
"5dd509e05de2b22f8e67e1bb"
],
}
]
}
]
I tried to use aggregate $lookup $unwind
db.coll.aggregate([
{
$unwind: {
path: "$dream",
}
},
{
$lookup: {
from: "industry",
localField: "dream.industry",
foreignField: "_id",
as: "dream.industry"
},
},
{
$unwind: {
path: "$dream.industry",
}
},
{
$lookup: {
from: "type",
localField: "dream.type",
foreignField: "_id",
as: "dream.type"
},
},
{
$unwind: {
path: "$dream.type",
}
},
{
$unwind: {
path: "$works",
}
},
{
$lookup: {
from: "company",
localField: "works.company",
foreignField: "_id",
as: "works.company"
},
},
{
$unwind: {
path: "$works.company",
}
},
{
$lookup: {
from: "skill",
localField: "works.skill",
foreignField: "_id",
as: "works.skill"
},
},
])
Executing the above code did not get the desired result!
This is what i expect
{
_id: "5df1e6f75de2b22f8e6c30e8",
user: {
name: 'Tom',
sex: 1,
age: 23
},
dream: [
{
label: 'engineer',
industry: {
_id: "5e06b16fb0670d7538222909", // Industry doc _id
name: 'IT',
createdAt: "2019-12-28T01:35:44.070Z",
updatedAt: "2019-12-28T01:35:44.070Z"
},
type: {
_id: "5e06b16fb0670d7538222951", // Type doc _id
name: 'job',
createdAt: "2019-12-28T01:35:44.070Z",
updatedAt: "2019-12-28T01:35:44.070Z"
},
},
{
label: 'programmer',
industry: {
_id: "5e06b16fb0670d7538222909", // Industry doc _id
name: 'IT',
createdAt: "2019-12-28T01:35:44.070Z",
updatedAt: "2019-12-28T01:35:44.070Z"
},
type: {
_id: "5e06b16fb0670d7538222951", // Type doc _id
name: 'job',
createdAt: "2019-12-28T01:35:44.070Z",
updatedAt: "2019-12-28T01:35:44.070Z"
}
}
],
works: [
{
name: 'any engineer',
company: {
_id: "5dd7fd51b0ae1837a08d00c8", // Company doc _id
name: 'alibaba',
area: 'CN',
},
skill: [
{
_id: "5dc3998e2cf66bad16efd61b", // Skill doc _id
name: 'Java'
},
{
_id: "5dc3998e2cf66bad16efd61e", // Skill doc _id
name: 'Php'
},
]
},
{
name: 'any programmer',
company: {
_id: "5dd7fd9db0ae1837a08d00e2", // Company doc _id
name: 'microsoft',
area: 'EN',
},
skill: [
{
_id: "5dd509e05de2b22f8e67e1b7", // Skill doc _id
name: 'Golang'
},
{
_id: "5dd509e05de2b22f8e67e1bb", // Skill doc _id
name: 'Node.js'
}
]
},
]
}
The expected result is dream is an array, works is an array, and dream.industry changed from ObjectId to document, dream.type changed from ObjectId to document, works.company changed from ObjectId to document
When I use populate, I can do it easily
Model.find()
.populate('dream.industry')
.populate('dream.type')
.populate('works.company')
.populate('works.skill')
.lean()
I refer to the following questions
mongoose aggregate lookup array (Almost the same as my question, But not resolved)
$lookup on ObjectId's in an array
hope to get everyone's help, thank you!
To make it easier i would not change the current pipeline but just add a $group stage to end of it in order to re-structure the data.
{
$group: {
_id: "$_id",
user: {$first: "$user"},
dream: {$addToSet: "$dream"},
works: {$addToSet: "$works"}
}
}
With that said if you are using Mongo version 3.6+ i do recommend you use the "newer" version of $lookup to re-write your pipeline to be a bit more efficient by avoiding all these $unwind's.