Mongoose save() not being processed - node.js

Pardon the noob question, but can anyone spot what am I doing wrong here? For some reason it looks like it does not go through the save() hook, as nothing is printed to the console...
router.post('/users', (req, res, err) => {
var user = new User(req.body);
user.save().then(function(user) {
console.log('user save()');
})
.catch((err) => {
res.send(err);
});
});
When I'm printing to the console the user var, before the save(), I get to see the right data.
Thanks.
EDIT: Resolved.
I've added the auto flag to _id in my schema, as the error was that no _id was provided/generated at insert() time.
_id: {
type: Schema.Types.ObjectId,
auto: true,
},

Related

Deleting object from mongoose database not working if site is not refreshed

app.delete("/api/persons/:id", (req, res, next) => {
Person.findByIdAndDelete(req.params.id)
.then((result) => {
res.status(204).end();
})
.catch((error) => next(error));
});
Not sure how to even explain this properly, but there is my delete method. It works fine for objects that are allready in the databases, but if I add a new one and I dont refresh the site, I get error: Cast to ObjectId failed for value "undefined" (type string) at path "_id" for model "Person"
Below is my mongoose schema if that helps:
const personSchema = new mongoose.Schema({
name: { type: String, required: true },
number: { type: Number, required: true },
});
personSchema.set("toJSON", {
transform: (document, returnedObject) => {
returnedObject.id = returnedObject._id.toString();
delete returnedObject._id;
delete returnedObject.__v;
},
});
My guess is you're optimistically updating your frontend with the new Person without waiting for a successful DB response with the new data. That is a valid technique, but gets you into trouble if you're not careful.
My suggestion would be to also send the new value from the database back to your app right away say it can stay in sync. You likely have no _id value on the front end if you're optimistically updating the app before a DB response.
Something like this:
app.post("api/person/new", async (req, res) => {
try {
const person = new Person(req.body)
await person.save()
res.status(201).send({ person })
} catch (err) {
res.status(500).send({ err })
}
})
And then more importantly, your API handler on the frontend needs to take that returned person value and use it to update the values on your front end, so it has access to the _id property for immediate deletion. And if there's an error creating the person for any reason, you can remove the person from the front end, or handle it however you wish.
I don't know what your app is built with, so I can write a sample bit of code for it.

How to save cloudinary url to mongoDb

I'm trying to save Cloudinary url to MongoDB but really don't know what is wrong with my code, because it's not working.
here is my code :
exports.test = asyncHandler(async (req, res, next) => {
const email = req.params.email;
console.log
cloudinary.uploader
.upload(req.file.path, { folder: 'avatar' })
.then((result) => {
console.log(result);// shows correctly on console
const { secure_url, public_id } = result;
console.log('url:',secure_url)// url & secure_url shows correctly on console
console.log('public_url:',public_id);
Resume.findOneAndUpdate(
{
email:email,
},
{ $set: { imagePath: secure_url} },
{
new: true,
fields: {
imagePath: 1,
},
}
);
console.log('upload successful!!');
})
.catch((error) => {
console.log(error);
});
});
I use $set because I want the field created if it didn't exist before. Also, I get the public_id and secure_url successfully from Cloudinary, but it didn't save in my database.
here is the output from console.log(result):
{
asset_id: '1ee919b68e258c9778097e40671ac710',
public_id: 'seawedkarowxgnipz8hq',
url: 'http://res.cloudinary.com/workaman/image/upload/v1656322947/seawedkarowxgnipz8hq.png',
secure_url: 'https://res.cloudinary.com/workaman/image/upload/v1656322947/seawedkarowxgnipz8hq.png',
original_filename: 'file_cuyajt',
}
and here is how i defined the model:
const ResumeSchema = new mongoose.Schema({
imagePath:{
type:String,
required:false
},
cloudinary_Id:{
type:String,
required:false
},
})
It seems I'm missing out on something but I really can't figure it out. when I submit from the frontend, I get the message "console.log('upload successfully!!')" but nothing is saved.
I think the issue here is that you're mixing up the MongoDB Node.js SDK with Mongoose a little bit. In the code that you shared you have both ResumeSchema and Resume - just check that those are correct.
In Mongoose the findOneAndUpdate() method does not have a $set option. The MongoDB CLI and subsequently their Node.js Driver also has findOneAndUpdate(), but the signature of that function and it's usage options somewhat differ.
Without seeing the rest of your code it's hard to tell what is exactly going on but my gut feeling is that you've mixed up these two methods. I hope this helps.

How can I retrieve documents' properties from a pre hook?

I posted this question yesterday because I didn't know how to solve my problem.
Change variable value in document after some time passes?
I was told I need to use a pre hook. I tried to do it, but "this" would refer to the query, not to the document. So I couldn't retrieve the documents to check if the 4 weeks passed. (check the question, you will get it)
Because I don't know how to make this .pre('find') to use variables from each of my document (so it checks if the 4 weeks passed) I was thinking about looping through all of them and checking if 4 weeks passed.
router.get('/judet/:id([0-9]{2})', middleware.access2, function(req, res)
{
var title = "Dashboard";
Somer.find({}, function(err, someri)
{
if(err)
{
console.log(err);
}
else
{
res.render("dashboard", {title: title, id:req.params.id, someri:someri});
}
});
}); ///get route
var someriSchema = new mongoose.Schema({
nume: {type: String, required: true},
dateOfIntroduction: {type:Date, default: Date.now, get: formatareData},
});
someriSchema.pre('find', function(next) {
console.log(this.dateOfIntroduction); <- this will return undefined, because this refers to the query, actually
next();
});///schema and the pre hook. I thought I could use it like this, and inside the body of the pre hook I can check for the date
Here's what I am talking about:
router.get('/judet/:id([0-9]{2})', middleware.access2, function(req, res)
{
var title = "Dashboard | Best DAVNIC73";
Somer.find({}, function(err, someri)
{
if(err)
{
console.log(err);
}
else
{
someri.forEach(function(somer)
{
///check if 4 weeks passed and then update the deactivate variable
})
res.render("dashboard", {title: title, id:req.params.id, someri:someri});
}
});
});
but I think this will be very bad performance-wise if I will get many entries in my DBs and I don't think this is the best way to do this.
So, if I was told correctly and I should use a pre hook for obtaining what I've said, how can I make it refer to the document?
Ok, I think I understood your requirements. this is what you could do:
/*
this will always set a documents `statusFlag` to false, if the
`dateOfIntroduction` was before Date.now()
*/
const mongoose = require('mongoose')
someriSchema.pre('find', function(next) {
mongoose.models.Somer.update(
{ datofIntroduction: { $lte: new Date() }},
{ statusFlag : false})
.exec()
.then((err, result) => {
// handle err and result
next();
});
});
The only problem I see, is that you are firing this request on every find.
in query middleware, mongoose doesn't necessarily have a reference to
the document being updated, so this refers to the query object rather
than the document being updated.
Taken straight from the documentation of mongoose
I pointed you yesterday to their documentation; but here is a more concrete answer.
someriSchema.post('find', function(res) {
// res will have all documents that were found
if (res.length > 0) {
res.forEach(function(someri){
// Do your logic of checking if 4 weeks have passed then do the following
someri.deactivated = true
someri.save()
})
}
})
What this basically do is for every found schema you would update their properties accordingly, your res can have only 1 object if you only queried 1 object. your second solution would be to do the cron
EDIT: This is what you would do to solve the async issue
const async = require('async')
someriSchema.post('find', function(res) {
async.forEach(res, function(someri, callback) {
// Do your logic of checking if 4 weeks have passed
// then do the following - or even better check if Date.now()
// is equal to expiryDate if created in the model as suggested
// by `BenSow`
// Then ONLY if the expiry is true do the following
someri.deactivated = true
someri.save(function (err) {
err ? callback(err) : callback(null)
})
}, function(err){
err ? console.log(err) : console.log('Loop Completed')
})
})

Changing response doc object from findOneAndUpdate

Say I got this lil chunk of code:
Room.findOneAndUpdate({ Roomid: roomid }, { $push: { UsersMeta: UserMeta}}, { new: false }, function (err, room) {
if (err) console.log(err);
console.log('room output:');
console.log(room);
client.emit('others', room);
})
which is searching for one document in db, updates it, and then sends this room doc in pre-updated state back to client. What I need is to make some changes to responded room, particularly remove those _id, __v, and, possibly, any other custom part of doc.
What I was trying to do:
use toObject.transform while creating schema
var RoomSchema = mongoose.Schema({
Roomid: { type: String, unique: true },
///stuff///
});
RoomSchema.options.toObject.transform = function (doc, ret, options) {
// remove the _id of every document before returning the result
delete ret._id;
}
failed: recieved cannot set property 'transform' of undefined error.
Change mentioned chunk of code to:
Room.find({ Roomid: roomid })
.update({ $push: { UsersMeta: UserMeta} })
.select({ _id: 0 })
.exec(function (err, room) {
if (err) console.log(err);
console.log('room output:');
console.log(room);
client.emit('others', room);
})
Failed: always recieve [] in room output.
Now I stopped at manually setting {_id: false} on Schema declaration, completely getting rid of _id in the first place. As I want use custom random id's for rooms, it seems that I don't need those _id anyway. Yet I'm not sure, that such a treatment will not cause some unpleasant consequences.
And, moreover, problem of possible need to leave some non _id doc properties out of response - is an unsolved mystery for me.
Thank you for attention.
you can do the following and it should work;
RoomSchema.set('toJSON', {
transform: function (doc, ret, options) {
delete ret._id;
delete ret.__v;
}
});

Mongoose not saving document

I've been working on a simple update function, that looks as follows:
exports.update = function update(req, res, next){
User.findById(req.param('userId'))
.exec(function (err, user) {
if(err) return next(err);
if(!user)
return res.sendData(null, 404, lang.userNotFound);
var info = req.param('info');
// Get the update to be made, in JSON format
if(info !== Object(info)) info = JSON.parse(info);
user.info = setProperties(user.info, info);
// console.log(user) gives the updated user object
user.save(function(err, usr) {
if (err) return next(err);
// console.log(usr) shows the updated user object
res.sendData();
});
});
};
function setProperties(object, update) {
if(update!== Object(update))
object = update;
else {
for (var prop in update) {
if (object.hasOwnProperty(prop))
object[prop] = setProperties(object[prop], update[prop]);
else
object[prop] = update[prop];
}
}
return object;
}
But unfortunately, although everything seems to work, in my Database nothing changes. I do not get any errors. Could anyone shed some light on this mystery? Thanks!
For reference (if it is relevant), here is my Schema:
var UserSchema = new Schema({
createdAt: {type: Date, default: Date.now},
lastActivity: {type: Date, default: Date.now},
info : {type : Schema.Types.Mixed},
});
Ah, I found the problem. Mongo was indeed performing the save, but it didn't know that an attribute had changed.
In the end, fixing it was as simple as adding
user.markModified('info');
And then everything performs as expected. It's a shame that Mongoose does not recognize this for us. Thanks to #vinz243 for pointing me in the right direction!

Resources