Issue
I just want to change the value of a key of all the objects inside an array
What I want actually –
The object which I queried from the database is –
{
_id: 61389277fa5c742caf959885,
title: 'What is GRE?',
forumTab: 'GRE',
askedAt: 2021-09-08T10:37:43.979Z,
askedBy: {
_id: 60f0a6a9b4259f7ef9c49cc8,
}
}
I want to add more key-value pairs in the askedBy
key by again querying the database for the User
with the given _id
Now, the user object which is queried is –
{
role: 'student',
_id: 60f0a6a9b4259f7ef9c49cc8,
firstName: 'Rishav',
lastName: 'Raj'
}
Finally I want to return the below object in response –
{
_id: 61389277fa5c742caf959885,
title: 'What is GRE?',
forumTab: 'GRE',
askedAt: 2021-09-08T10:37:43.979Z,
askedBy: {
_id: 60f0a6a9b4259f7ef9c49cc8,
role: 'student',
firstName: 'Rishav',
lastName: 'Raj'
}
}
I am creating a new array questionsToSend
and pushing the object with updated key-value pairs which I am getting after querying the database for each elements in the questions
array, I have created functions for respective query that I need to render in sequence, even after rendering the functions in proper sequence why the new array questionsToSend
is not populating with the objects before returning the response?
router.get("/questions", async (req, res) => {
if (!req.query.forumTab) return res.status(400).send("something went wrong");
const page = parseInt(req.query.page) - 1;
const perPage = parseInt(req.query.perPage);
let questionsToSend = [];
const func0 = async (callback) => {
const questions = await Question.find({ forumTab: req.query.forumTab })
.sort({ askedAt: -1 })
.limit(perPage)
.skip(perPage * page);
console.log("xxxxxxx");
callback(questions);
};
const func1 = async (questions, callBack) => {
questions.forEach(async (question) => {
const askedUserData = await User.findById(question.askedBy._id);
if (!askedUserData) {
const index = questions.indexOf(question);
questions.splice(index, 1);
return;
}
questionsToSend.push({
..._.pick(question, [
"_id",
"title",
"askedAt",
"tags",
]),
askedUserData,
});
console.log(questionsToSend);
});
console.log("yyyyyyyy");
callBack();
};
func0(
(questions) =>
func1(questions, async () => {
console.log("zzzzzzzz");
res.status(200).send(questionsToSend);
})
);
});
Solution
We can use aggregation to achieve this
Question.aggregate([
{
$match: { forumTab: req.query.forumTab }
},
{
$lookup: {
from: 'users',
localField: 'askedBy._id',
foreignField: '_id',
as: "user"
}
},
{ $unwind: "$user"},
{ "$addFields": {
"askedBy": {
"$mergeObjects": ["$askedBy", "$user"]
}
}
},
{ $project: { "user" : 0} },
{ $sort: {"askedAt": -1}},
{ $skip: perPage * page},
{ $limit: perPage},
])
- $match is used to apply filter
- $lookup is used to do a join on a collection. I have assumed the collection name is users.
- $lookup returns the matched result as an array. Converting it to object using $unwind since we get only one back.
- $addFields with $mergeObjects is merging the existing askedBy field and newly user field
- Removing the user field from the result set with $project.
- And then sort, skip and limit.
Answered By – Kannan.dev
This Answer collected from stackoverflow, is licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0