Convert from localField to let - $lookup - node.js

I'm trying to adapt my code so I could after use the pipeline to $project only the fields I need.
This first code is achieving the result expected;
Order.aggregate([
{ $unwind: "$candidate" },
{ $match: { "candidate.groupId": "A" } },
{
$lookup: {
from: "users",
localField: "candidate.autonomousId",
foreignField: "_id",
as: "candidateData",
},
},
]).toArray();
The second one doesn't return the same output. It returns an empty candidateData.
Order.aggregate([
{ $unwind: "$candidate" },
{ $match: { "candidate.groupId": "A" } },
{
$lookup: {
from: "users",
let: { id: "$candidate.autonomousId" },
pipeline: [{ $match: { $expr: { $eq: ["$_id", "$$id"] } } }],
as: "candidateData",
},
},
]).toArray();
What am I doing wrong when I try to write in the second way? I couldn't find my syntax mistake.

Since candidate.autonomousId is an array (contrary to its name) and you are using the $eq operator to compare $$id (array) with _id (ObjectId), the pipeline returns no User documents in candidateData. You should use the $in aggregation operator instead.
db.orders.aggregate([
{
$unwind: "$candidate"
},
{
$match: {
"candidate.groupId": "A"
}
},
{
$lookup: {
from: "users",
let: {
id: "$candidate.autonomousId"
},
pipeline: [
{
$match: {
$expr: {
$in: [
"$_id",
"$$id"
]
}
}
}
],
as: "candidateData",
},
},
])
MongoPlayground
I would recommend renaming the let variable ids for clarity.

Related

retrieve nested data from mongodb record depend on condition

I have a board record like this
{
_id:"6257d2edfbc4d4d1d53bb7b8"
tasks:["624acd4fce5fbb5ce20ddb88","624acd68ce5fbb5ce20de3b5"]
}
This is my tasks collection
[
{
_id:"624acd4fce5fbb5ce20ddb88",
status:"inProgress"
},
{
_id:"624acd68ce5fbb5ce20de3b5"
status:"done"
}
]
I am trying to retrieve the board data and the count of inProgress tasks and done tasks in aggregate like this but it show me this error
input to $filter must be an array not object
Board.aggregate([
{
$facet: {
totalDocs: [{ $match: { $and: [data] } }],
totalInProgress: [
{
$lookup: {
from: "tasks",
localField: "tasks",
foreignField: "_id",
as: "tasks",
},
},
{ $unwind: { path: "$tasks", preserveNullAndEmptyArrays: true } },
{
$project: {
tasks: {
$filter: {
input: "$tasks",
as: "task",
cond: { $eq: ["$$task.status", "inProgress"] },
},
},
},
},
],
},
},
]);

How to lookup in another collection with objectID of main collection in mongodb

I am trying to get the data from another collection via lookup
collection "users"
{
"_id":{
"$oid":"60bf4bb31f45d98903d1851f"
},
"name":"Dave",
"center":"THGJ556",
}
collection "addresses"
{
"_id":{
"$oid":"60bf4bb31f45d98903d1851f"
},
"userId":"60bf4bb31f45d98903d1851f",
}
collection "applications"
{
"_id":{
"$oid":"60bf4bb31f45d98903d1851f"
},
"userId":"60bf4bb31f45d98903d1851f",
"centerId":"THGJ556",
},
{
"_id":{
"$oid":"60bf4bb31f45d98903d3647j"
},
"userId":"60bf4bb31f45d98903d1851f",
"centerId":"JHGJ5476",
}
Now I need data from all the tables.
here is my code:
users.aggregate([
{
$lookup: {
from: "addresses",
localField: "_id",
foreignField: "userId",
as: "addressData"
}
},
{
$lookup: {
from: "applications",
pipeline: [
{ $match:
{ userId:"$_id", centerId: "JHGJ5476"}
},
],
as: "applicationData"
}
},
] ,function(err, result) {
if (err) {
console.log(err)
} else {
console.log(result)
}
});
I am doing something wrong while using aggregate and match in pipeline.
I am getting addressData correctly, but I get nothing [] in applicationData because I suspect something is wrong with userId:"$_id"
As docs explain:
A $match stage requires the use of an $expr operator to access the variables. $expr allows the use of aggregation expressions inside of the $match syntax.
So you have to use $expr into $match stage and also a let stage.
let stage is to define variable to use into $expr: id: $_id.
$expr used with $and and $eq is to get both conditions.
db.users.aggregate([
{
$lookup: {
from: "addresses",
localField: "_id",
foreignField: "userId",
as: "addressData"
}
},
{
$lookup: {
from: "applications",
let: {"id": "$_id"},
pipeline: [
{
$match: {
"$expr": {
"$and": [
{"$eq": ["$userId","$$id"]},
{"$eq": ["$centerId","JHGJ5476"]}
]
}
}
}
],
as: "applicationData"
}
}
])
Check this example.

Return all results in aggregate if match query parameter is null

My Aggregate query:
const categoryId = req.query.categoryId
const results = await Question.aggregate([
{
$match:{
$and : [
{ category : mongoose.Types.ObjectId(categoryId) },
{ category : {$ne : null} }
]
}
},{
$lookup: {
from: "answer",
let: { questionId: "$_id" },
pipeline: [{ $match: { $expr: { $eq: ["$$questionId", "$questionId"] } } }],
as: "answerCount"
}
},{ $addFields: { answerCount: { $size: "$answerCount" }}}, {
$lookup: {
from: "users",
let : {id : "$creator"},
as : "creator",
pipeline : [
{$match : {$expr : {$eq: ["$$id","$_id"]}}},
{$project : {name : 1, profilePhoto : 1}}
]
}
}, {$unwind: "$creator"},{
$lookup: {
from: "categories",
let : { id: "$category" },
as : "category",
pipeline: [
{ $match : { $expr: { $eq: ["$_id", "$$id"] } }},
{ $project: { name: 1}}
]
}
}, {$unwind : "$category"},{
$unset: ["createdAt", "updatedAt", "__v"]
}
])
Now using $match query I fetch only the Questions belonging to a specific category. What I want to do is if the categoryId is null, it should return all the results. Right now it returns an empty array. How do i go about doing that?
Try This:
const categoryId = req.query.categoryId
let conditions = {
// You can also have some default condition that always results true
};
if (categoryId) {
conditions = {
"category": mongoose.Types.ObjectId(categoryId)
// More conditions in future...
}
}
const results = await Question.aggregate([
{
$match: conditions
},
{
$lookup: {
from: "answer",
let: { questionId: "$_id" },
pipeline: [{ $match: { $expr: { $eq: ["$$questionId", "$questionId"] } } }],
as: "answerCount"
}
},
{ $addFields: { answerCount: { $size: "$answerCount" } } },
{
$lookup: {
from: "users",
let: { id: "$creator" },
as: "creator",
pipeline: [
{ $match: { $expr: { $eq: ["$$id", "$_id"] } } },
{ $project: { name: 1, profilePhoto: 1 } }
]
}
},
{ $unwind: "$creator" },
{
$lookup: {
from: "categories",
let: { id: "$category" },
as: "category",
pipeline: [
{ $match: { $expr: { $eq: ["$_id", "$$id"] } } },
{ $project: { name: 1 } }
]
}
},
{ $unwind: "$category" },
{
$unset: ["createdAt", "updatedAt", "__v"]
}
]);
also read about preserveNullAndEmptyArrays property in $unwind operator if required.
https://docs.mongodb.com/manual/reference/operator/aggregation/unwind/

Mongodb $lookup using with multiple criteria mongodb

{
$lookup: {
from: "Comment",
let: {
p_id: "$_id",
d_id: "$data_id",
},
pipeline: [
{
$match: {
$expr: {
$and: [
{
$eq: [
"$_id",
"$$p_id"
]
},
{
$eq: [
"$data_id",
"$$d_id"
]
}
]
}
}
}
],
as: "subComment"
}
}
https://mongoplayground.net/p/GbEgnVn3JSv
I am good at mongoplayground but tried to put there my thought
I want to fetch the comment of posts based on doc_id and post_id for mainComment query looks good to me but subcommand is not good. Please guide on this
Its simple as a post can have multiple comment need comment count base on Post.data._id which is equal to Comment.doc_id and Post._id is in Comment.post_id
Not sure what "mainComment" and "subComment" are, I believe you missed the dollar sign before them
{
$project: {
_id: 1,
main_comments_count: {
$size: "$mainComment"
},
sub_comments_count: {
$size: "$subComment"
},
}
}
Update
What you did wrong in the playground is that you used $data in the lookup.let stage. $data is a document and the field you actually want to lookup is $data._id.
sidenote: if you are looking up using just one field, you can simply use the localField and foreign in the lookup stage. Using let and pipeline is not necessary there.
db.setting.aggregate([
{
$lookup: {
from: "site",
"let": {
"pid": "$data._id" //here
},
"pipeline": [
{
"$match": {
"$expr": {
"$in": [
"$doc_id",
"$$pid"
]
}
}
}
],
"as": "subComment"
}
},
{
$addFields: {
countRecord: "$subComment"
}
}
])
i.e. this gives the same output
db.setting.aggregate([
{
$lookup: {
from: "site",
localField: "data._id",
foreignField: "doc_id",
as: "subComment"
}
},
{
$addFields: {
countRecord: "$subComment"
}
}
])

Mongodb lookup with match in aggregate returns an empty array

This is my user document
{
"_id":"02a33b9a-284c-4869-885e-d46981fdd679",
"context":{
"email":"someemail#gmail.com",
"firstName":"John",
"lastName":"Smith",
"company":[
"e2467c93-114b-4613-a842-f311a8c537b3"
],
},
}
and a company document
{
"_id":"e2467c93-114b-4613-a842-f311a8c537b3",
"context":{
"name":"Coca Cola",
"image":"someimage",
},
};
This is my query for users
let users = await Persons.aggregate(
[{$project:
{
name: {$concat: ['$context.firstName', ' ', '$context.lastName']},
companyId: {$arrayElemAt: ['$context.company', 0]}}
},
{$match: {name: searchRegExp}},
{$lookup: {from: 'companies', let: {company_id: {$arrayElemAt: ['$context.company', 0]}}, pipeline:
[
{
$match: {
$expr: {
$eq: ['$_id', '$$company_id']
}
}
},
{
$project: {name: '$context.name'}
}
],
as: 'company'}}
]).toArray()
When I run this query I get company field as an empty array, what am I doing wrong here?
Your first pipeline stage $project only outputs _id, name and companyId so then when you're trying to refer to $context.company in your $lookup there will be an empty value. You can use $addFields instead:
{
$addFields: {
name: {
$concat: [
"$context.firstName",
" ",
"$context.lastName"
]
},
companyId: {
$arrayElemAt: [
"$context.company",
0
]
}
}
}
Mongo Playground
When you add field companyId: {$arrayElemAt: ['$context.company', 0]}}, then you can use the simple version of $lookup. There is no need to set it twice, once as companyId: ... and in let: {company_id: ...}
db.user.aggregate([
{
$addFields: {
name: { $concat: ["$context.firstName", " ", "$context.lastName"] },
companyId: { $arrayElemAt: ["$context.company", 0] }
}
},
{
$lookup: {
from: "company",
localField: "companyId",
foreignField: "_id",
as: "company"
}
}
])

Resources