How to populate array of objects in mongoose - mongodb

I have this code -
const getAllCourses = async (req, res) => {
const courses = await Course.find({});
try {
await courses.populate('professor')
res.send({ status: 200, data: { courses } });
} catch (err) {
res.status(500).send({ status: 500, message: "Internal server error." });
}
};
Also, this is the response I'm getting from postman -
{
"status": 200,
"data": {
"courses": [
{
"_id": "61dc47f58f88c1a7e9bd36b6",
"name": "Course1",
"professor": "61dc1299431cd669faad7d0f",
"students": [
{
"student": "61dc0b7f103b531f105e8e4c",
"_id": "61dc47f58f88c1a7e9bd36b7"
},
{
"student": "61dc220885886a9f1d8e94d0",
"_id": "61dc47f58f88c1a7e9bd36b8"
}
],
"createdAt": "2022-01-10T14:51:33.313Z",
"updatedAt": "2022-01-10T14:51:33.313Z",
"__v": 0
},
{
"_id": "61dc47fb8f88c1a7e9bd36bf",
"name": "Course2",
"professor": "61dc1299431cd669faad7d0f",
"students": [
{
"student": "61dc0b7f103b531f105e8e4c",
"_id": "61dc47fb8f88c1a7e9bd36c0"
},
{
"student": "61dc220885886a9f1d8e94d0",
"_id": "61dc47fb8f88c1a7e9bd36c1"
}
],
"createdAt": "2022-01-10T14:51:39.704Z",
"updatedAt": "2022-01-10T14:51:39.704Z",
"__v": 0
}
]
}
}
Now what I'm trying to do is to populate the professor and the students but it doesn't seem to work.
I tried populating "courses.professor", "course.professor", "professor" but nothing worked for me.
What am I missing?

Here is an example of how this can be done for a document with an array friends on it:
User.
findOne({ name: 'Val' }).
populate({
path: 'friends',
// Get friends of friends - populate the 'friends' array for every friend
populate: { path: 'friends' }
});

I just solved it by chaining the populate method directly after the Course.find({}) and it just worked, I'm not sure why though.
Solution:
const getAllCourses = async (req, res) => {
const courses = await Course.find({}).populate('professor').populate('students.student');
try {
res.send({ status: 200, data: { courses } });
} catch (err) {
res.status(500).send({ status: 500, message: err.message });
}
};

Related

Group by and Get Max Value MongoDb

I would like to get the highest number of counts for each numId and display it on my front end in a table.
Here is an example of my database:
{
"_id": {
"$oid": "6294777f677b4c647e28771a"
},
"numId": "5",
"respondee": "0x9d95bcaa5b609fa97a7ec860bec115aa94f85ba9",
"__v": 0,
"originalResponse": "test2",
"submittedAt": {
"$date": {
"$numberLong": "1653897087357"
}
},
"addresses": [
"0x39c878a3df98002ddba477a7aa0609fb5a27e2ff",
"0xe3342d6522ad72f65d6b23f19b17e3fb12161f90"
],
"count": 2
},
{
"_id": {
"$oid": "6294836e677b4c647e287e93"
},
"numId": "5",
"respondee": "0xe3342d6522ad72f65d6b23f19b17e3fb12161f90",
"__v": 0,
"originalResponse": "test3",
"submittedAt": {
"$date": {
"$numberLong": "1653900142375"
}
},
"addresses": [
],
"count": 0
}
I have written something like this but I'm not sure how to group the results according to the numId
import Response from '../../../models/Response.model';
import db from '../../../utils/config/db';
import nc from 'next-connect';
import { onError } from '../../../utils/error';
const handler = nc({
onError,
});
//GET all
handler.get(async (req, res) => {
await db.connect();
let responses = await Response.find({ });
//To group responses by numId
// Sort responses by votes in ascending order
responses = responses.sort((a, b) => {
return a.count - b.count;
});
let topResponses = responses.filter((response) => {
return response.count === responses[0].count;
});
// Check if respondee has the highest count response
if (
topResponses.length > 0 &&
topResponses.find((response) => {
return response.respondee === respondee;
})
) {
// Get the response
let response = topResponses.find((response) => {
return response.respondee === respondee;
});
// Get the response
let responseString = response.response;
// Get the count
let count = response.count;
}
await db.disconnect();
});
export default handler;
I have figured out the answer by referring from another stackoverflow:
Group by and Get Max Value MongoDb
let responses = await Response.aggregate([
{ $sort: { votes: -1 } },
{ $group: { _id: '$baseId', group: { $first: '$$ROOT' } } },
{ $replaceRoot: { newRoot: '$group' } },
]);
res.send(responses);

How can I send updated object from subdocument array of objects using mongoose as response

I want to send updated object as response from subdocument.
From my carts model, I am querying the subdocument array which is cartItems. After querying and doing crud operation, I don't want to send the full cartItems array as response. I want to send the updated object as response.
exports.removeCartItem = async (req, res) => {
const { itemId } = req.params
const { email } = req.user
const { id } = req.body
const targetUser = await User.findOne({ email }).exec()
const cartItemRemoved = Cart.findOneAndUpdate(
{
user: targetUser._id,
},
{
$pull: { cartItems: { _id: itemId } },
},
{ new: true },
).exec((err, data) => {
if (err) {
console.log(er)
}
res.json(data)
})
}
This is the response I am getting right now:
{
"user": "621def0665c08eff01794f6e",
"cartItems": [
{
"product": "6228edb603d5e7ca773a2b04",
"quantity": 5,
"price": 200,
"_id": "622b04741bab0093c963ef18"
}
],
"_id": "622b04741bab0093c963ef17",
"__v": 0
}
I want send the updated object as response - something like below:
{
"product":"6228edb603d5e7ca773a2b04",
"quantity": 5,
"price": 200,
"_id": "622b04741bab0093c963ef18"
}

How to return the a formatted response from a mongo query/projection?

I'm trying to create an API to validate a promocode. I have minimal experience with mongo and the backend in general so I'm a bit confused in what is the best approach to do what I'm trying to accomplish.
I have this PromoCode form in the client. When a user types a promocode I would like for my backend to
verify if the code exists in one of the docs.
if it exists then return that code, the value for that code and the couponId
if the code doesn't exist then return an error.
My db is structured like this. The user will type one of those codes inside the codes: []
{
"_id": {
"$oid": "603f7a3b52e0233dd23bef79"
},
"couponId": "rate50",
"value": 50,
"codes": ["K3D01XJ50", "2PACYFN50", "COKRHEQ50"]
},
{
"_id": {
"$oid": "603f799d52e0233dd23bef78"
},
"couponId": "rate100",
"value": 100,
"codes": ["rdJ2ZMF100", "GKAAYLP100", "B9QZILN100"]
}
My route is structure like this:
router.post('/promoCode', (req, res, next) => {
const { promoCode } = req.body;
console.log('this is the req.body.promoCode on /promoCode', promoCode)
if (!promoCode) {
throw new Error('A promoCode needs to be passed')
}
promoCodesModel
.validatePromoCode(req.body.promoCode)
.then((response) => {
console.log('response inside /promoCode', response)
res.status(200).json({ data: response })
})
.catch((error) => {
res.status(400).json({ result: 'nok', error: error })
})
})
The validatePromoCode function is the following:
const validatePromoCode = async (code) => {
try {
let promoCode = await PromoCodesModel.find(
{"codes": code},
{_id: 0, codes: { $elemMatch: { $eq: code }} })
console.log('This is the promocode', promoCode)
return promoCode
} catch (err) {
throw new Error (err.stack)
}
}
All this seems to sort of work since I get the following response when the code is typed correctly
{
"data": [
{
"codes": [
"COKRHEQ50"
]
}
]
}
when typed incorrectly I get
{
"data": []
}
What I would like to get back is. (How can I accomplish this ?). Thanks
// when typed correctly
{
"data": { value: 50, couponId: "rate50", code: "COKRHEQ50" }
}
// when typed incorrectly
{
"error": "this is not valid code"
}
TL;DR: I would like to return a formatted query with specific values from a mongo query or an error object if that value does not exist on the document object.
Ok just figured it out
To be able to get the this responsed (what I wanted):
{
"data": [
{
"codes": [
"K3D01XJ50"
],
"couponId": "rate50",
"value": 50
}
]
}
I ended up having to do this on validatePromoCode
onst validatePromoCode = async (code) => {
try {
let promoCode = await PromoCodesModel.find(
{ codes: code },
{ _id: 0, codes: { $elemMatch: { $eq: code } }, couponId: 1, value: 1 },
)
return promoCode
} catch (err) {
throw new Error(err.stack)
}
}
But is there a better way on doing this ? Thanks

Mongoose nested object not updating 'cannot create field "foo" in element'

I have a similar issue to this question.
I'm trying to create a new field using "findAndUpdate". I've tried all the methods, $set, $push, $addSet... none of them seem to be working and I keep getting the same error.
Here's the code:
router.post('/accept', auth, async (req, res) => {
const useremail = user.email
const originalEvent = await Event.findOneAndUpdate({eventId: 61469041, isOrganizer: true, "attendees.email": useremail},
{"$push":{"attendees.status" : "accepted"}},
{new: true})
res.status(200).json({originalEvent, event})
}
catch (e) {
res.status(400).json({ msg: e.message, success: false });
}
});
Here's the error code:
"Cannot create field 'status' in element {attendees: [ { _id: ObjectId('5f80a02a82dceb2810e0aa66'), email: "bob#gmail.com", name: "Bob" } ]}"
Here's the object I'm trying to update:
{
"organizer": {
"email": "alex#gmail.com",
"name": "Alex"
},
"_id": "5f80a02a82dceb2810e0aa65",
"title": "Go to the beach",
"eventId": 61469041,
"isOrganizer": true,
"user": "5f05f23417ca6ab69ccc4cf2",
"attendees": [
{
"_id": "5f80a02a82dceb2810e0aa66",
"email": "bob#gmail.com",
"name": "Bob"
}
],
"__v": 0,
}
Expected outcome:
{
"organizer": {
"email": "alex#gmail.com",
"name": "Alex"
},
"_id": "5f80a02a82dceb2810e0aa65",
"title": "Go to the beach",
"eventId": 61469041,
"isOrganizer": true,
"user": "5f05f23417ca6ab69ccc4cf2",
"attendees": [
{
"_id": "5f80a02a82dceb2810e0aa66",
"email": "bob#gmail.com",
"name": "Bob",
"status": "accepted"
}
],
"__v": 0,
}
SOLVED with this:
const originalEvent = await Event.findOneAndUpdate({eventId: eventId, "isOrganizer": true,
"attendees": {$elemMatch: {email: useremail}}
},
{ $set: { "attendees.$.status": "accepted"} }
)
res.status(200).json(originalEvent)
}
Referencing attendees.status doesn't make sense because in your schema attendees is not an object (with fields such as status) but an array. But you can do it differently. If you have the index of the attendee you want to mutate, you can do { $set: { "attendees.0.status": "accepted" } }, where 0 is the index in the array.
Also, with regards to the first half of your question, the error you're seeing is because $push works on arrays. So in order for your operation to work, you'd have to first initialize such an object {attendees: { status: [] } }.
If the field is not an array, the operation will fail. (docs)

how do i push a document on a nested mongodb

if I want to add a review, with author,rating and reviewText to the first title, how do I do it.
{
"_id": ObjectId("58dd21c3cb77090b930b6063"),
"bookAuthor": "wwww",
"titles": [{
"title": "this is title",
"_id": ObjectId("58dd3f2701cc081056135dae"),
"reviews": [],
"favouredBy": [
"bb, aa, cc"
]
}, {
"title": "this is the second tittle",
"_id": ObjectId("58dd42a59f12f110d1756f08"),
"reviews": [],
"favouredBy": [
"all"
]
}],
"__v": 0
}
from with in the application this is what I tried but keep getting "TypeError: Cannot read property 'push' of undefined" error
module.exports.reviewsCreate = function(req, res) {
if (req.params.bookid) {
Bok
.findById(req.params.bookid)
.select('titles')
.exec(
function(err, book) {
if (err) {
sendJSONresponse(res, 400, err);
} else {
doAddReview(req, res, book);
}
}
);
} else {
sendJSONresponse(res, 404, {
"message": "Not found, bookid required"
});
}
};
var doAddReview = function(req, res, book, author) {
console.log(book);
if (!book) {
sendJSONresponse(res, 404, "bookid not found");
} else {
book.titles.reviews.push({
author: author,
rating: req.query.rating,
reviewText: req.query.reviewText
});
book.save(function(err, book) {
var thisReview;
if (err) {
sendJSONresponse(res, 400, err);
} else {
sendJSONresponse(res, 201, book);
}
});
}
};