Mongodb aggregate lookup by _id is not working - mongodb

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

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,
}
]
}
]

Getting `[ [Object] ]` as nested array of object response on MongoDB Aggregation query

I'm trying to do an aggregation on two collections that has a linkage between them, and I need to access information in an array of objects in one of those collections.
Here are the schemas:
User Schema:
{
_id: ObjectId,
username: String,
password: String,
associatedEvents: [
{
event_id: ObjectId,
isCreator: boolean,
access_level: String,
}
]
}
Event Schema:
{
_id: ObjectId,
title: String,
associated_users: [
{
user_id: ObjectId
}
]
}
I'm attempting to get the users associated to an event for a specific user, and then get their access level information. Here's the aggregation I have:
const eventsJoined = await Event.aggregate([
{
$match: {
$expr: { $in: [id, "$associatedUserIds"] },
},
},
{
$lookup: {
from: "users",
localField: "associatedUserIds",
foreignField: "_id",
as: "user_info",
},
},
{ $unwind: "$user_info" },
{
$unwind: {
path: "$user_info.associatedEvents",
preserveNullAndEmptyArrays: true,
},
},
{
$group: {
_id: "$_id",
title: { $first: "$title" },
description: { $first: "$description" },
startDate: { $first: "$startdate" },
userInfo: { $first: "$user_info" },
usersAssociatedEvents: { $push: "$user_info.associatedEvents" },
},
},
{
$project: {
title: 1,
description: 1,
startDate: 1,
userInfo: 1,
usersAssociatedEvents: "$usersAssociatedEvents",
},
},
]);
And this is the result I'm getting:
[
{
_id: 609d5ad1ef4cdbeb32987739,
title: 'hello',
description: 'desc',
startDate: null,
usersAssociatedEvents: [ [Object] ]
}
]
As you can see, the query is already aggregating the correct data. But the last thing that's tripping me up is the fact that the aggregation is [ [Object] ] for usersAssociatedEvents instead of the actual contents of the object. Any idea on why that would be?

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] } }
}
}
]);

mongoose recursive nesting

in my project a user can create products. each user have a reference to all of its products and each product have a reference to its user.
both the user and the product have a 'name' field.
i need to get all of the users products array, and in that array i want to have the product name and the
user name that created it (and only those fields and no others).
for example:
Users:
{ _id: 1, name: 'josh', productIds: [1,3]}
{ _id: 2, name: 'sheldon', productIds: [2]}
Products:
{ _id: 1, name: 'table', price: 45, userId: 1}
{ _id: 2, name: 'television', price: 25 userId: 2}
{ _id: 3, name: 'chair', price: 14 userId: 1}
i want to get the following result:
{ _id: 1, name: 'josh',
products: {
{ _id: 1, name: 'table', user: { _id: 1, name: 'josh' },
{ _id: 3, name: 'chair', user: { _id: 1, name: 'josh' },
}
}
{ _id: 2, name: 'sheldon',
products: {
{ _id: 2, name: 'television', userId: { _id: 2, name: 'sheldon' }
}
}
i tried the following query that didn't fill the inner userId and left it with only the id (no name):
User.aggregate([
{
$lookup:
{
from: 'products',
localField: 'productIds',
foreignField: '_id',
as: 'products'
}
}
i also tried the following, which did the same as the first query except it only retried the first product for each user:
User.aggregate([
{
$lookup:
{
from: 'products',
localField: 'productIds',
foreignField: '_id',
as: 'products'
}
},
{
$unwind: {
path: "$products",
preserveNullAndEmptyArrays: true
}
},
{
$lookup: {
from: "user",
localField: "products.userId",
foreignField: "_id",
as: "prodUsr",
}
},
{
$group: {
_id : "$_id",
products: { $push: "$products" },
"doc": { "$first": "$$ROOT" }
}
},
{
"$replaceRoot": {
"newRoot": "$doc"
}
}
Product:
const schema = new Schema(
{
name: {
type: String,
required: true
},
price: {
type: Number,
required: true
},
userId: {
type: Schema.Types.ObjectId,
ref: 'User',
required: true
},
}
);
module.exports = mongoose.model('Product', schema);
User:
const schema = new Schema(
{
name: {
type: String,
required: true,
unique: true
},
productIds: [{
type: Schema.Types.ObjectId,
ref: 'Product',
require: false
}],
{ timestamps: true }
);
module.exports = mongoose.model('User', schema);
any help will be highly appreciated
It looks like a perfect scenario for $lookup with custom pipeline and another nested $lookup. The inner one allows you to handle product-> user relationship while the outer one handles user -> product one:
db.Users.aggregate([
{
$project: {
productIds: 0
}
},
{
$lookup: {
from: "Products",
let: { user_id: "$_id" },
pipeline: [
{
$match: {
$expr: {
$eq: [ "$userId", "$$user_id" ]
}
}
},
{
$lookup: {
from: "Users",
localField: "userId",
foreignField: "_id",
as: "user"
}
},
{
$unwind: "$user"
},
{
$project: {
"user.productIds": 0,
"price": 0,
"userId": 0
}
}
],
as: "products"
}
}
])
Mongo Playground

Recursive Mongo Find

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',
}
}
])