How to populate a referenced document from `this` object? - node.js

I am using NodeJS, MongoDB, and Mongoose. I am able to populate a referenced document if I use findById(this.id), but not using this directly:
IssuanceSchema.methods.getOrganizationName = async function() {
let issuance = await Issuance.findById(this.id).populate('organization');
console.log(issuance);
let temp = this.populate('organization');
console.log(temp);
console.log(issuance.organization.displayName());
console.log(temp.organization.displayName());
// ...
}
The log shows:
{
_id: 5e849ca9b07ed81bd2eaad89,
organization: {
_id: 5e80a19d8c910f196c11673c,
...
},
}
{
_id: 5e849ca9b07ed81bd2eaad89,
organization: 5e80a19d8c910f196c11673c,
}
SomeName
(node:10231) UnhandledPromiseRejectionWarning: TypeError: temp.organization.displayName is not a function
How can I populate the referenced document directly without the round-about findById?

It seems you are simply missing a call to execPopulate(). In your case that would be:
let temp = this.populate('organization').execPopulate();
Check out the docs:
If you have an existing mongoose document and want to populate some of its paths, you can use the Document#populate() method. Just make sure you call Document#execPopulate() to execute the populate().

Related

Mongoose findOneAndUpdate() using current data

I am try to track the number of downloads per click on a website.
server.js
router.post("/download", async (req, res) => {
let id = req.body.id;
id = parseInt(id);
let doc = await db.findOneAndUpdate({_id: id}, {downloads: 100});
});
Note: This works
But I'm trying to increase the number by 1 each time.
For example: Let's say the current number of downloads is 5, how do I do it that if there's a post request. The number of downloads increases by 1.
const { body: { id } } = req;
const intCasetedId = parseInt(id);
const retrievedDocument = await db.findOneAndUpdate({ id }, { $inc: { downloads: 1 } });
A couple things are happening here.
First I get the id value from the the req argument using a destructuring assignment.
I use only const to ensure I do not mutate variable values.
I also use the object property value shorthand notation to skip '_id' key in the search query argument. Quoting mongoose documentation:
Issues a mongodb findAndModify update command by a document's _id field. findByIdAndUpdate(id, ...) is equivalent to findOneAndUpdate({ _id: id }, ...).
Then I am using '$inc' operator to increment the downloads field by 1.
I would also highly recommend for you to research eslint

Struggle with mongoose query, pushing different Objects into different arrays in a single deeply nested object

I just can't figure out the query and even if it's allowed to write a single query to push 4 different objects into 4 different arrays deeply nested inside the user Object.
I receive PATCH request from front-end which's body looks like this:
{
bodyweight: 80,
waist: 60,
biceps: 20,
benchpress: 50,
timestamp: 1645996168125
}
I want to create 4 Objects and push them into user's data in Mongo Atlas
{date:1645996168125, value:80} into user.stats.bodyweight <-array
{date:1645996168125, value:60} into user.stats.waist <-array
...etc
I am trying to figure out second argument for:
let user = await User.findOneAndUpdate({id:req.params.id}, ???)
But i am happy to update it with any other mongoose method if possible.
PS: I am not using _id given by mongoDB on purpose
You'll want to use the $push operator. It accepts paths as the field names, so you can specify a path to each of the arrays.
I assume the fields included in your request are fixed (the same four property names / arrays for every request)
let user = await User.findOneAndUpdate(
{ id: req.params.id },
{
$push: {
"stats.bodyweight": {
date: 1645996168125,
value: 80,
},
"stats.waist": {
date: 1645996168125,
value: 60,
},
// ...
},
}
);
If the fields are dynamic, use an object and if conditions, like this:
const update = {};
if ("bodyweight" in req.body) {
update["stats.bodyweight"] = {
date: 1645996168125,
value: 80,
};
}
// ...
let user = await User.findOneAndUpdate(
{ id: req.params.id },
{
$push: update,
}
);
The if condition is just to demonstrate the principle, you'll probably want to use stricter type checking / validation.
try this:
await User.findOneAndUpdate(
{id:req.params.id},
{$addToSet:
{"stats.bodyweight":{date:1645996168125, value:80} }
}
)

Unown Way To Change Parameter Inside Mongoose

I am having some issues with saving an object parameter into a mongoose map. My collection looks like this. The collection's name is guildtickets:
{
"_id":"813915771067301888",
"maxopenTickets":"5",
"serverticketnum":"2",
"opentickets": {
"850608478229626891": {
"ticketname":"ticket-0001",
"ticketstatus":"open",
"ticketcreatedby":"843644509324705814"
}
},
"__v":0}
I wish to change the ticketstatus parameter to closed, so the result should be "ticketstatus": "closed". So far I am using:
var queryTicketSchema = await GuildTicketsSchema.findOne({
_id: message.guild.id
});
queryTicketSchema.get(`opentickets`).get(`${message.channel.id}`).ticketstatus = 'closed';
The issue with the code above, is that when logging the collection, the ticketstatus parameter is showed as closed, but while in MongoDB compass, the parameter is still listed as open. Any help is appreciated and more than welcome! Tysm!
Try this:
let ticket = await GuildTicketsSchema.findOne({
_id: message.guild.id
});
ticket.get(`opentickets`).get(`${message.channel.id}`).ticketstatus = 'closed';
await GuildTicketsSchema.findByIdAndUpdate(ticket._id, ticket);

Unable to perform operations over Mongoose result

Product
.findAll()
.then((products) => {
/* Perform operations */
}
The above query returns an array of products. For eg,
[
{
name: Mobile,
price: 10000
},
{
name: Laptop,
price: 20000
},
]
I need to make some changes to the products array (add new fields based on the values of existing fields, and delete those existing fields). I tried few methods, but none are working:
products.forEach((product) => {
product.[updatedPrice] = updatedPrice;
delete product[price];
}
Array.map() is also not working.
The following works, but I don't know the working behind, and why it is happening. Also, how to delete a field using the same.
products.forEach((product) => {
product.set('updatedPrice', updatedPrice, {strict: false})
}
The thing here to note is that in .then((products) products object here is not a JSON object but a Mongoose Document object and the set method you are using is defined by the mongoose. You can refer it from here https://mongoosejs.com/docs/guide.html#strict
We can do 2 things:
use lean() (returns a plain js object)
Product.findAll().lean().then((products) => {
/* Perform operations*/
});
use toJSON() method on products object to convert mongoose object to js object
Product.findAll().then((products) => {
products = products.toJSON();
/* Perform operations*/
});
Thanks

Mongoose get updated document after instance.update()

I am executing a mongoose Model.findById() function in order to return a single instance by utilizing an express route.
Model.findById(modelid)
.then(instance => {
if(instance.isOwnedBy(user)) {
return instance.update({$push: {days: req.params.dayid}}, {new: true})
.then(foo => res.send(foo))
} else {
res.status(401).send("Unauthorized")
}
})
the above code returns an object containing the opTime, electionId...etc instead of returning the newly updated document instance. How am I able return the newly updated document after the instance.update() method?
If instance.isOwnedBy(user) and _id: modelid can be merged into one mongo query, it's better to use findOneAndUpdate() but in that way, if it doesn't find any document match, you can not know which part of the query causes the not found.
And because I don't know much about your models, conditions, I can not answer how to do it with findOneAndUpdate() but there is another way that modify the document and call save() method.
Example base on your code:
Model.findById(modelid)
.then(instance => {
if(instance && instance.isOwnedBy(user)) {
if(instance.days) instance.days.push(req.params.dayid);
else instance.days = [req.params.dayid];
instance.save().then(function(instance){
res.send(instance);
})
} else {
res.status(401).send("Unauthorized")
}
})
Note: You should check if the instance exist or not before modify it.

Resources