422

Below is my code

var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/test');

var Cat = mongoose.model('Cat', {
    name: String,
    age: {type: Number, default: 20},
    create: {type: Date, default: Date.now} 
});

Cat.findOneAndUpdate({age: 17}, {$set:{name:"Naomi"}},function(err, doc){
    if(err){
        console.log("Something wrong when updating data!");
    }

    console.log(doc);
});

I already have some record in my mongo database and I would like to run this code to update name for which age is 17 and then print result out in the end of code.

However, why I still get same result from console(not the modified name) but when I go to mongo db command line and type "db.cats.find();". The result came with modified name.

Then I go back to run this code again and the result is modified.

My question is: If the data was modified, then why I still got original data at first time when console.log it.

Al Fahad
  • 2,378
  • 5
  • 28
  • 37
Dreams
  • 8,288
  • 10
  • 45
  • 71

16 Answers16

754

Why this happens?

The default is to return the original, unaltered document. If you want the new, updated document to be returned you have to pass an additional argument: an object with the new property set to true.

From the mongoose docs:

Query#findOneAndUpdate

Model.findOneAndUpdate(conditions, update, options, (error, doc) => {
  // error: any errors that occurred
  // doc: the document before updates are applied if `new: false`, or after updates if `new = true`
});

Available options

  • new: bool - if true, return the modified document rather than the original. defaults to false (changed in 4.0)

Solution

Pass {new: true} if you want the updated result in the doc variable:

//                                                         V--- THIS WAS ADDED
Cat.findOneAndUpdate({age: 17}, {$set:{name:"Naomi"}}, {new: true}, (err, doc) => {
    if (err) {
        console.log("Something wrong when updating data!");
    }

    console.log(doc);
});
XCS
  • 27,244
  • 26
  • 101
  • 151
  • 31
    This appears to be broken for me, it still returns the old document with new: true. – PDN Apr 21 '16 at 07:57
  • @PDN What version of mongoose/mongo do you have? That might be messing with how it works. – Ashelyn Dawn May 11 '16 at 20:57
  • 5
    makes sense to me since you already have access to the new document – danday74 Jul 23 '16 at 05:40
  • 4
    it worked for me, I'm using moogose version 4.6.3, thanks – cesar andavisa Oct 20 '16 at 12:16
  • for those reading here, who might still find that `"new": true` doesn't work, check that the field you update and expect to see in the found object is added to the Schema – Kirill Slatin Nov 06 '16 at 16:39
  • 1
    I get the new doc, but `_id` is null, so its not really the new doc. – chovy Jan 16 '17 at 04:59
  • Yes, check your schema properly written to do CRUD operations. – Prasad Shinde Oct 18 '17 at 10:46
  • works if i use findAndModify, but not findOneAndUpdate ! – kroe Feb 06 '18 at 04:02
  • 7
    NodeJs MongoDB Native uses - `{ returnOriginal: false }` – Nick Grealy Feb 07 '19 at 03:11
  • @XCS your solution still works. I can't thank you enough. This is my code ```js const findOneOrCreateUser = await User.findOneAndUpdate( { email: data.email }, { $set: { data, }, }, { new: true }, { upsert: true }, ); ``` – Niyongabo Eric Feb 25 '20 at 21:25
  • For me works with `{ upsert: true, new: true }` not with `... { new: true }, { upsert: true } ...`. Mongoose 5.9.9 – NicDD4711 Jun 21 '20 at 11:00
  • 1
    @user3408530 Upsert tells mongo to create a new document if one does not already exist. So, you are trying to update a document that doesn't actually exist. – XCS Jun 22 '20 at 13:11
  • I want mongoose to return both, the updated document as well as original one, original for checking if the document was really found and updated and updated one to send to the user back on the front end – Tayyab Ferozi Dec 16 '20 at 18:42
151

For anyone using the Node.js driver instead of Mongoose, you'll want to use {returnOriginal:false} instead of {new:true}.

2021 - Mongodb ^4.2.0 Update
{ returnDocument: 'after' }

callmemath
  • 8,185
  • 4
  • 37
  • 50
Pedro Hoehl Carvalho
  • 2,183
  • 2
  • 19
  • 25
  • 2
    Thank you! This works for me mongodb node version 2.2.27 – Kevin Ng Aug 29 '17 at 06:33
  • 27
    This is kind of an idiot API. Why not use the same signatures for Mongoose as the native API? Why not return the updated doc by default? Mongoose is one of the more irritating libs I use everyday. – Askdesigners May 13 '19 at 14:41
  • 8
    I'm using `"mongodb": "^3.6.9"` and `{ returnOriginal:false }` is deprecated. Use `{ returnDocument: 'after' }` instead. – kunthet Jun 17 '21 at 04:41
  • Thank you! This works for me. @kunthet I was actually using this, as the documentation explained that the suggested `{ returnOriginal: false }` is deprecated. But for some reason, I cannot get that to work, while it works as expected using the suggestion. – Daniel Sep 03 '21 at 08:31
100

So, "findOneAndUpdate" requires an option to return original document. And, the option is:

MongoDB shell

{returnNewDocument: true}

Ref: https://docs.mongodb.com/manual/reference/method/db.collection.findOneAndUpdate/

Mongoose

{new: true}

Ref: http://mongoosejs.com/docs/api.html#query_Query-findOneAndUpdate

Node.js MongoDB Driver API:

{returnOriginal: false}

2021 - Mongodb ^4.2.0 Update
{ returnDocument: 'after' }

Ref: http://mongodb.github.io/node-mongodb-native/3.0/api/Collection.html#findOneAndUpdate

callmemath
  • 8,185
  • 4
  • 37
  • 50
Tsuneo Yoshioka
  • 7,504
  • 4
  • 36
  • 32
47

By default findOneAndUpdate returns the original document. If you want it to return the modified document pass an options object { new: true } to the function:

Cat.findOneAndUpdate({ age: 17 }, { $set: { name: "Naomi" } }, { new: true }, function(err, doc) {

});
19

Mongoose maintainer here. You need to set the new option to true (or, equivalently, returnOriginal to false)

await User.findOneAndUpdate(filter, update, { new: true });

// Equivalent
await User.findOneAndUpdate(filter, update, { returnOriginal: false });

See Mongoose findOneAndUpdate() docs and this tutorial on updating documents in Mongoose.

vkarpov15
  • 3,614
  • 24
  • 21
17

For whoever stumbled across this using ES6 / ES7 style with native promises, here is a pattern you can adopt...

const user = { id: 1, name: "Fart Face 3rd"};
const userUpdate = { name: "Pizza Face" };

try {
    user = await new Promise( ( resolve, reject ) => {
        User.update( { _id: user.id }, userUpdate, { upsert: true, new: true }, ( error, obj ) => {
            if( error ) {
                console.error( JSON.stringify( error ) );
                return reject( error );
            }

            resolve( obj );
        });
    })
} catch( error ) { /* set the world on fire */ }
flash
  • 1,663
  • 16
  • 16
Assaf Moldavsky
  • 1,681
  • 1
  • 19
  • 30
  • 19
    Mongoose will return a promise if you don't provide a callback function. There is no need to create your own promise! – joeytwiddle Sep 19 '17 at 03:35
  • 2
    @joeytwiddle Mongoose **will not** return a Promise if you don't provide a callback. Instead it returns a Query object that provides only a small subset of the Promise API. This is according to the Mongoose documentation. – Jamie Ridding Sep 02 '18 at 22:59
13

This is the updated code for findOneAndUpdate. It works.

db.collection.findOneAndUpdate(    
  { age: 17 },      
  { $set: { name: "Naomi" } },      
  {
     returnNewDocument: true
  }    
)
Paul Roub
  • 36,322
  • 27
  • 84
  • 93
Jobin Mathew
  • 483
  • 4
  • 7
6

If you want to return the altered document you need to set the option {new:true} API reference you can use Cat.findOneAndUpdate(conditions, update, options, callback) // executes

Taken by the official Mongoose API http://mongoosejs.com/docs/api.html#findoneandupdate_findOneAndUpdate you can use the following parameters

A.findOneAndUpdate(conditions, update, options, callback) // executes
A.findOneAndUpdate(conditions, update, options)  // returns Query
A.findOneAndUpdate(conditions, update, callback) // executes
A.findOneAndUpdate(conditions, update)           // returns Query
A.findOneAndUpdate()                             // returns Query

Another implementation thats is not expressed in the official API page and is what I prefer to use is the Promise base implementation that allow you to have .catch where you can deal with all your various error there.

    let cat: catInterface = {
        name: "Naomi"
    };

    Cat.findOneAndUpdate({age:17}, cat,{new: true}).then((data) =>{
        if(data === null){
            throw new Error('Cat Not Found');
        }
        res.json({ message: 'Cat updated!' })
        console.log("New cat data", data);
    }).catch( (error) => {
        /*
            Deal with all your errors here with your preferred error handle middleware / method
         */
        res.status(500).json({ message: 'Some Error!' })
        console.log(error);
    });
Jonathan Thurft
  • 4,087
  • 7
  • 47
  • 78
6

I know, I am already late but let me add my simple and working answer here

const query = {} //your query here
const update = {} //your update in json here
const option = {new: true} //will return updated document

const user = await User.findOneAndUpdate(query , update, option)
Aljohn Yamaro
  • 2,629
  • 25
  • 22
4

Below shows the query for mongoose's findOneAndUpdate. Here new: true is used to get the updated doc and fields is used for specific fields to get.

eg. findOneAndUpdate(conditions, update, options, callback)

await User.findOneAndUpdate({
      "_id": data.id,
    }, { $set: { name: "Amar", designation: "Software Developer" } }, {
      new: true,
      fields: {
        'name': 1,
        'designation': 1
      }
    }).exec();
3

2021 - Mongodb ^4.2.0 Update

This applies to the mongodb node driver, NOT mongoose.

It seems like the latest version of the Mongodb node driver uses the following syntax, if you are searching and updating using "collection.findOneAndUpdate":

.findOneAndUpdate(query, update, { returnDocument: 'after' | 'before' })

Couldn't find the answer here myself while searching, so posting this in case others are in the same situation.

Crucial
  • 41
  • 1
  • 4
  • This does not provide an answer to the question. Once you have sufficient [reputation](https://stackoverflow.com/help/whats-reputation) you will be able to [comment on any post](https://stackoverflow.com/help/privileges/comment); instead, [provide answers that don't require clarification from the asker](https://meta.stackexchange.com/questions/214173/why-do-i-need-50-reputation-to-comment-what-can-i-do-instead). - [From Review](/review/late-answers/30518999) – mbuechmann Dec 07 '21 at 08:13
1

In some scenarios {new: true} is not working. Then you can try this.

{'returnNewDocument':true}
Dharman
  • 30,962
  • 25
  • 85
  • 135
Prakash Harvani
  • 1,007
  • 7
  • 18
1
const updatedDocument = await Model.findOneAndUpdate(req.params.id, req.body, {
    upsert: true,
    new: true,
  })

use this to get update document

1

Make sure you check if you're using Mongoose or MongoDB

if using Mongoose - use {new: true}

const query = await Model.findOneAndUpdate({filter}, {update}, {new: true});

if using MongoDB - use {returnNewDocument: true}

const query = await Model.findOneAndUpdate({filter}, {update}, {returnNewDocument: true});

Make sure you're using the correct one since, because if you don't, it's just gonna get ommited and you're get the old document.

0
export function newDocumentOnUpdatePlugin(schema) {
  schema.pre(
    ['update', 'findOneAndUpdate', 'updateOne', 'updateMany'],
    function (next) {
      this.setOptions({ new: true });
      next();
    },
  );
}

I have created this plugin if anyone need this functionality across the whole app and want to avoid repetition. Just use this as a global plugin

All2Pie
  • 310
  • 3
  • 13
0

This option is necessary to get updated document instance

{ new: true }
Zehan Khan
  • 21
  • 3