Mongoose findOneAndUpdate not returning raw Mongo response - node.js

I'm trying to determine whether the document was found in my findOneAndUpdate operation. If it wasn't, I return a 404 not found error. I figured I'd use the "passRawValue" option Mongoose provides, and check for a raw value- if raw is undefined, I know the doc was not found.
However regardless whether the doc is found or not, my raw value is undefined. I've verified that the doc I'm trying to update is in the DB at the time of the query by running a simple "findOne" query just before the update. Where am I going wrong?
let updateItemById = (userId, itemId, params, cb) => {
//this finds and prints the document I'm testing with -- I know its in the DB
// Item.findOne({ "_id" : itemId, ownerId: userId }, (err, doc) => {
// if (doc) {
// console.log("This is the doc: ", doc);
// }
// });
Item.findOneAndUpdate({ "_id" : itemId, ownerId: userId },
{
$set: {
params
}
}, { runValidators: 1, passRawResult: true}, (err, doc, raw) => {
if (err) {
//winston.log
return cb(ErrorTypes.serverError(), false);
}
else if (raw) {
return cb(null, true);
}
else {
return cb(ErrorTypes.notFound(), false);
}
});
}

Hi I have a hunch that you are passing params that has a property that doesn't exist in the document in the database. In such case, nothing was modified, hence db doesn't return raw as the third parameter.
Update:
So I did some few tests of my own, and I see that if we pass option strict:false then your code should work as intended. So your options section will look like this
{ runValidators: 1, passRawResult: true, strict: false, new:true}
Explanation:
Mongoose has a strict option which by default is true. It makes sure that the values being updated is defined in the schema. So when we provide the option strict as false, as described in the [mongoose documentation] (http://mongoosejs.com/docs/api.html#query_Query-findOneAndUpdate) we can achieve updating document with new field.
I also added new:true option which will return you the updated document.
P.S.
I would like to add though, since our upsert is false, which means it won't insert new document when a match is not found, it will return null for doc, and you can simple check on that. Why are you checking on raw? Is there any particular reason for this?

I know it's been awhile but I had the same problem here so I decided to leave an answer that maybe can help other people.
I was able to check whether the findOneAndUpdate() method found a document or not by checking if the doc parameter was null on the callback function:
async Update(request: Request, response: Response) {
const productId = request.params.id;
const query = { _id: productId };
const options = { new: true };
try {
await Product.findOneAndUpdate(query, request.body, options, (err, doc, res) => {
if (doc === null)
return response.status(404).send({
error: 'Product not found'
})
return response.status(204).send();
});
}
catch (err) {
return response.status(400).send({
error: 'Product update failed'
});
}
}

Related

Create document using Mongoose.js with Projection and Unique validation

Is there any way to have the same options as findOneAndUpdate() when creating a document. I realize I can set upsert: true but I am looking to throw an error if the document already exist.
So say I have Courses in a database and when to insert a new one. If I use .create() I get no ability to project, or use lean. :dis
If I use findOneAndUpdate then it does not throw an Error for a document with a duplicate field as it is updating as existing document which I do not want to do.
Basically I would like the abilities that come with .findOneAndUpdate() but I want it to throw an error if it finds a document, since I am searching for it based off the field which I wish to be unique
By looking to your problem , you can try the following approach
function updateUser(user,cb){
UserModel.find({name : user.name}, function (err, docs) {
if (docs.length){
cb('Name exists already',null);
}else{
user.save(function (err, returned) {
var leanObject = returned.toObject();
assert.equal(leanObject.schema, null);
});
}
});
}
So , in the above code , you find for the document and and if we get the result non-empty , we can consider it as an error otherwise we can save that document as new entry .
So to accomplish the desired goals I have done the below. Where I use a query condition that I know will fail. That way I am forcing the upsert and get the benefits of the options.
async addCourse(input, projection = {}) {
const { name } = input;
const conditions = { _id: mongoose.Types.ObjectId() };
const newCourse = await this.model.findOneAndUpdate(conditions, input, {
upsert: true,
new: true,
lean: true,
projection,
runValidators: true,
context: 'query',
setDefaultsOnInsert: true,
});
return newCourse;
}

Mongoose updateOne with parameter {new:true} not showing actual updated value

I am struggling for a couple of hours to show the final value of an updated document (via mongoose updateOne). I successfully modify it as I can see "nModified: 1" when I call the endpoint on Postman, but I am not able to output the actual final document - even when using the parameter {new:true}
This is the code for the route:
// 3. We check if blockid is in this project
Block.findById(req.params.blockid)
.then(block => {
if (!block) {
errors.noblock = "Block not found";
return res.status(404).json(errors);
}
// 4. We found the block, so we modify it
Block.updateOne(
{ _id: req.params.blockid },
{ $set: blockFields }, // data to be updated
{ new: true }, // flag to show the new updated document
(err, block) => {
if (err) {
errors.noblock = "Block not found";
return res.status(404).json(errors);
}
console.log(block);
res.json(block);
}
);
})
.catch(err => console.error(err));
Instead, this is the output I am getting (Mongoose is on debug mode)
Any ideas?
Many thanks
{ new : true } will return the modified document rather than the original. updateOne doesn't have this option. If you need response as updated document use findOneAndUpdate.
Below are the mongoosejs function where you can use { new : true }
findByIdAndUpdate()
findOneAndUpdate()
findOneAndDelete()
findOneAndRemove()
findOneAndReplace()
Thank you #sivasankar for the answer. Here is the updated working version with findOneAndUpdate
And here the expected result:
you should give second param as object of keys value paris of data,
don't pass as $Set : blockfields, just add like below, if it is object containing parameters,
{ $set: blockFields }
Because code should be like this
Block.updateOne(
{ _id: req.params.blockid },
blockFields, // if blockfields is object containing parameters
{ new: true },
(err, block) => {
// lines of code
}
);
For more detail here is link to updateOne function detail updateOne

Mongodb/mongoose omit a field in response [duplicate]

I have a NodeJS application with Mongoose ODM(Mongoose 3.3.1). I want to retrieve all fields except 1 from my collection.For Example: I have a collection Product Which have 6 fields,I want to select all except a field "Image" . I used "exclude" method, but got error..
This was my code.
var Query = models.Product.find();
Query.exclude('title Image');
if (req.params.id) {
Query.where('_id', req.params.id);
}
Query.exec(function (err, product) {
if (!err) {
return res.send({ 'statusCode': 200, 'statusText': 'OK', 'data': product });
} else {
return res.send(500);
}
});
But this returns error
Express
500 TypeError: Object #<Query> has no method 'exclude'.........
Also I tried, var Query = models.Product.find().exclude('title','Image'); and var Query = models.Product.find({}).exclude('title','Image'); But getting the same error. How to exclude one/(two) particular fields from a collection in Mongoose.
Use query.select for field selection in the current (3.x) Mongoose builds.
Prefix a field name you want to exclude with a -; so in your case:
Query.select('-Image');
Quick aside: in JavaScript, variables starting with a capital letter should be reserved for constructor functions. So consider renaming Query as query in your code.
I don't know where you read about that .exclude function, because I can't find it in any documentation.
But you can exclude fields by using the second parameter of the find method.
Here is an example from the official documentation:
db.inventory.find( { type: 'food' }, { type:0 } )
This operation returns all documents where the value of the type field is food, but does not include the type field in the output.
Model.findOne({ _id: Your Id}, { password: 0, name: 0 }, function(err, user){
// put your code
});
this code worked in my project. Thanks!! have a nice day.
You could do this
const products = await Product.find().select(['-image'])
I am use this with async await
async (req, res) => {
try {
await User.findById(req.user,'name email',(err, user) => {
if(err || !user){
return res.status(404)
} else {
return res.status(200).json({
user,
});
}
});
} catch (error) {
console.log(error);
}
In the updated version of Mongoose you can use it in this way as below to get selected fields.
user.findById({_id: req.body.id}, 'username phno address').then(response => {
res.status(200).json({
result: true,
details: response
});
}).catch(err => {
res.status(500).json({ result: false });
});
I'm working on a feature. I store a userId array name "collectedUser" than who is collected the project. And I just want to return a field "isCollected" instead of "collectedUsers". So select is not what I want. But I got this solution.
This is after I get projects from database, I add "isCollected".
for (const item of projects) {
item.set("isCollected", item.collectedUsers.includes(userId), {
strict: false,
})
}
And this is in Decorator #Schema
#Schema({
timestamps: true,
toObject: {
virtuals: true,
versionKey: false,
transform: (doc, ret, options): Partial<Project> => {
return {
...ret,
projectManagers: undefined,
projectMembers: undefined,
collectedUsers: undefined
}
}
}
})
Finally in my controller
projects = projects.map(i => i.toObject())
It's a strange tricks that set undefined, but it really work.
Btw I'm using nestjs.
You can do it like this
const products = await Product.find().select({
"image": 0
});
For anyone looking for a way to always omit a field - more like a global option rather than doing so in the query e.g. a password field, using a getter that returns undefined also works
{
password: {
type: String,
required: true,
get: () => undefined,
},
}
NB: Getters must be enabled with option { toObject: { getters:true } }
you can exclude the field from the schema definition
by adding the attribute
excludedField : {
...
select: false,
...
}
whenever you want to add it to your result,
add this to your find()
find().select('+excludedFiled')

Mongoose: how to check if document is modified via model.findOneAndUpdate()

In mongoose, we can check if an update operation has modified the document with model.update():
model.update(query, update, function(err, raw){
if (raw.nModified >= 1) console.log('document is modified!')
});
Is there a way to do the same with model.findOneAndUpdate()?
model.findOneAndUpdate(query, update, { new: true }, function(err, doc){
if (doc) {
// So MongoDB found the document, but is there a way
// to know the document was indeed modified?
}
});
You can pass the option { passRawResult : true } to mongoose to advice mongoose to pass the raw result of the underlying mongodb driver, in this case mongodb-native, as a third argument to the callback.
mongodb-native documentation for findOneAndUpdate
model.findOneAndUpdate(query, update, { new: true, passRawResult : true }, function(err, doc, res){
// res will look like
// { value: { _id: 56a9fc80a7f9a4d41c344852, name: 'hugo updated', __v: 0 },
// lastErrorObject: { updatedExisting: true, n: 1 },
// ok: 1 }
});
In case the update did not succeed due to no matching document was found a null res will be passed to the callback. In case a document matched but field values where the same as before the update res object will not give you enough information to figure out if values were updated for the matching document.

How do I update a doc in Cloudant using Cloudant Node.js

So, what I'm doing should be really simple, and maybe it is and I'm just doing something wrong. I want to update an existing document in my database but I'm having some issues, can someone please advise?
Nano's Documentation states the following for insert:
db.insert(doc, [params], [callback])
Therefore, I should surely be able to do the following:
var user = {
'firstname' : 'my',
'secondname' : 'name'
};
db.insert(user, {_rev: '2-cc5825485a9b2f66d79b8a849e162g2f'}, function(err, body) {});
However, whenever I try this, it creates an entirely new document. If I do the following then it will indeed update my document, but of course, with nothing in this document other than the _rev:
db.insert({_rev: '2-cc5825485a9b2f66d79b8a849e162g2f'}, function(err, body) {});
So the question is, how do I pass in my object and get it to update, rather than creating a new record?
var user = {
'firstname' : 'my',
'secondname' : 'name',
'_id': <id from prev object>,
'_rev': <rev from prev object>
};
db.insert(user, function(err, body) {});
the _id and _rev are both required in order for the update to work.
they should be in the object that you are sending also.
The first argument in the db.insert(...) command is the document which you want to create/update. If you pass in a doc with a ._rev attribute, then it will replace the document with that same _rev in Cloudant with the doc passed in as the first argument of your db.insert(...). If the doc does not include a ._rev attribute, then Cloudant will create an entirely new document.
This explains the behavior you were experiencing in both the scenarios you tried. In order to make an update to your doc, make sure to include ._id and ._rev attributes, along with the rest of your doc's attributes when you use it as the first argument to your db.insert(...) function.
Got it! Here's the solution:
db.get('user', { revs_info: true }, function(err, doc) {
if (!err) {
console.log(doc);
doc.firstname = 'my';
doc.secondname = 'name';
db.insert(doc, doc.id, function(err, doc) {
if(err) {
console.log('Error inserting data\n'+err);
return 500;
}
return 200;
});
}
});
First get the record id and rev id (_id,_rev)
const newData={email:"aftabfalak956#gmail.com",name:"Aftab Falak"}
cloudant.use("user").find({selector:{_id:"user_id"}}, (err, documents) => {
var revision = documents.docs[0]._rev;
const data={...documents.docs[0],...newData};
cloudant.use("user").insert(data,{_rev:revision},function(err){
if (!err) {
console.log('success', 'The record was updated successfully');
}
else {
console.log('failure', err);
}
});
});

Resources