Update And Return Document In Mongodb - node.js

I want to get updated documents. This is my original code and it successfully updates but doesn't return the document.
collection.update({ "code": req.body.code },{$set: req.body.updatedFields}, function(err, results) {
res.send({error: err, affected: results});
db.close();
});
I used the toArray function, but this gave the error "Cannot use a writeConcern without a provided callback":
collection.update({ "code": req.body.code },{$set: req.body.updatedFields}).toArray( function(err, results) {
res.send({error: err, affected: results});
db.close();
});
Any ideas?

collection.update() will only report the number of documents that were affected to its own callback.
To retrieve the documents while modifying, you can use collection.findOneAndUpdate() instead (formerly .findAndModify()).
collection.findOneAndUpdate(
{ "code": req.body.code },
{ $set: req.body.updatedFields },
{ returnOriginal: false },
function (err, documents) {
res.send({ error: err, affected: documents });
db.close();
}
);
The returnOriginal option (or new with Mongoose) lets you specify which version of a found document (original [default] or updated) is passed to the callback.
returnOriginal was deprecated in version 3.6. Use returnDocument: "before" | "after" for version 3.6 and later.
Disclaimer: This answer currently refers to the Node.js Driver as of version 3.6. As new versions are released, check their documentation for possibly new deprecation warnings and recommended alternatives.

The solution is to set: {returnOriginal: false}.
collection.findOneAndUpdate(
whereObj,
updateObj,
{returnOriginal: false});

Could not find any way to update many and return the modified records in docs, so I made a workaround.
At least one fault that I can find with below method is, you would not be able to tell if document is modified or already had the value that you are using:
function findAndUpdateMany(filter, updateOptions) {
return collection.find(filter).project({_id: 1}).toArray()
.then(function(matchingIds) {
filter = {_id: {$in: matchingIds}}
return collection.updateMany(filter, updateOptions)
}).then(function() {
return collection.find(filter).toArray()
})
}

A bit late for the party but here's a simple 2022 solution to your question.
I'm using NestJS for this app
const updatedPainting: Partial<IGallery> = {
imageAltTxt: updateGalleryDto.imageAltTxt,
name: updateGalleryDto.name,
dateCreated: updateGalleryDto.dateCreated,
size: updateGalleryDto.size,
description: updateGalleryDto.description,
isFeatured: updateGalleryDto.isFeatured || false,
};
return await this.galleryModel.findOneAndUpdate(
{ _id },
{ $set: { imageUrl, ...updatedPainting } },
{ returnDocument: 'after' },
);

to get the updated doc when performing an update operation on one doc, use findOneAndUpdate() and in the options object, set returnDocument property to 'after'
let options = {returnDocument: 'after'}
const upadatedDoc = collection.findOneAndUpdate({'your query'},{'your update'}, options)

In case you are using mongoose, returnOriginal: false did NOT work for me at v5.11.10,
but new: true worked,
from official mongoose docs:
const filter = { name: 'Jean-Luc Picard' };
const update = { age: 59 };
let doc = await Character.findOneAndUpdate(filter, update, {
new: true
});
doc.name; // 'Jean-Luc Picard'
doc.age; // 59

Checkout the WriteResult object:
http://docs.mongodb.org/manual/reference/method/db.collection.update/#writeresults-update
WriteResult result = collection.update({ "code": req.body.code },{$set: req.body.updatedFields}, function(err, results) {
res.send({error: err, affected: results});
db.close();
});
result should have something like:
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
If you want the updated results, do another query with the primary key.

Related

mongoose: findOne using mongo _id

I get that this can be a duplicated question. I looked up at least 10 related questions and answers, but I am still not able to find the document.
I am trying to get the document using .findOne(). I have the _id that created by MongoDB. But, I get null for every search I try.
await mongoose.connection.db
.collection('testing')
.findOne({ _id: req.body.test_id }, (err, result) => {
if (err) {
res.status(400);
} else {
console.log(`whaaaaaahsidufh ${result}`);
}
});
I tried _id: mongoose.Type.ObjectId(req.body.test_id) and other possible way to search. How can I retrieve the result by using _id on mongoose?
you can use findById();
try {
const test = await mongoose.connection.db.collection('testing').findById(req.body.test_id);
if (test ) {
console.log(`whaaaaaahsidufh ${test}`);
} else {
console.log(`test not found`);
}
}catch(err){
res.status(400);
}

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 findOneAndUpdate not returning raw Mongo response

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'
});
}
}

Selecting only modified subdocument from Mongo

I have a mongoose query like this:
var query = Events.findOneAndUpdate({ '_id': event._id,'participants._id':participant._id},{'$set': {'participants.$': participant}}, {upsert:false,new: true},function(err,result){
if(err){
return res.status(500).jsonp({
error: 'Unable to update participant'
});
}
console.log(result.participants[0]);
res.jsonp(result.participants[0]);
});
and the query works properly modifying the participants subdocument inside Events collection.
The problem:
I need only the modified participant to be returned as JSON and I am not in need of the entire participants array but I am not able to achieve this since I get all the participants when I do console.log(result.participants);
How do I get only the modified subdocument after the query?
You may have to use the native JS filter() method as in the following:
Events.findOneAndUpdate(
{ '_id': event._id, 'participants._id': participant._id },
{ '$set': { 'participants.$': participant } },
{ upsert: false, new: true },
function(err, result){
if(err){
return res.status(500).jsonp({
error: 'Unable to update participant'
});
}
var modified = result.participants.filter(function(p){
return p._id === participant._id
})[0];
console.log(modified);
res.jsonp(modified);
}
);

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.

Resources