109

When I try to change any part of the data returned by a Mongoose Query it has no effect.

I was trying to figure this out for about 2 hours yesterday, with all kinds of _.clone()s, using temporary storage variables, etc. Finally, just when I though I was going crazy, I found a solution. So I figured somebody in the future (fyuuuture!) might have the save issue.

Survey.findById(req.params.id, function(err, data){
    var len = data.survey_questions.length;
    var counter = 0;

    _.each(data.survey_questions, function(sq){
        Question.findById(sq.question, function(err, q){
            sq.question = q; //has no effect

            if(++counter == len) {
                res.send(data);
            }
        });
    });
});
Toli
  • 5,547
  • 8
  • 36
  • 56
  • 2
    possible duplicate of [How do you turn a Mongoose document into a plain object?](http://stackoverflow.com/questions/7503450/how-do-you-turn-a-mongoose-document-into-a-plain-object) – Blakes Seven Jul 21 '15 at 09:56
  • possible duplicate of https://stackoverflow.com/q/9952649/4748042 – Deepak Jan 31 '20 at 07:07

3 Answers3

179

For cases like this where you want a plain JS object instead of a full model instance, you can call lean() on the query chain like so:

Survey.findById(req.params.id).lean().exec(function(err, data){
    var len = data.survey_questions.length;
    var counter = 0;

    _.each(data.survey_questions, function(sq){
        Question.findById(sq.question, function(err, q){
            sq.question = q;

            if(++counter == len) {
                res.send(data);
            }
        });
    });
});

This way data is already a plain JS object you can manipulate as you need to.

JohnnyHK
  • 305,182
  • 66
  • 621
  • 471
  • 9
    Btw @JohnnyHK just wanted to say thanks again. A year and a half later was helping a client debug something. He spent a weekend trying to figure something out, turns out it was because he was trying to modify the Mongoose Object ;P – Toli Jul 14 '14 at 13:54
  • 1
    2 years later and still crushing it. Didn't even realize lean() was there. – Petrogad Dec 17 '15 at 12:43
  • When using `findOne`, I can just modify the object and then call `data.save()` which seems to work just fine (I'm appending to an array) – developius Feb 08 '16 at 20:38
  • What about when using `aggregate` instead of a simple `find`? – Fizzix Jun 01 '16 at 00:04
  • 1
    @Fizzix `aggregate` always provides its results as plain objects, so there's no need for `lean()`. – JohnnyHK Jun 01 '16 at 03:59
  • 1
    3 years later and spent and entire hour trying to figure it out. Saved my whole day! Thanks – Noy Oct 19 '16 at 10:22
  • and you will loose all virtual method magic with this approach. – codebased Apr 05 '18 at 23:17
  • 2
    Thank you! This really helped, but why is it impossible to modify the object? What kind of special object is it? – Robert Feduș Apr 13 '20 at 22:44
50

I think the Mongoose documentation doesn't make this clear enough, but the data returned in the query (although you can res.send() it) is actually a Mongoose Document object, and NOT a JSON object. But you can fix this with one line...

Survey.findById(req.params.id, function(err, data){
    var len = data.survey_questions.length;
    var counter = 0;

    var data = data.toJSON(); //turns it into JSON YAY!

    _.each(data.survey_questions, function(sq){
        Question.findById(sq.question, function(err, q){
            sq.question = q;

            if(++counter == len) {
                res.send(data);
            }
        });
    });
});
Toli
  • 5,547
  • 8
  • 36
  • 56
1

I was using mongoose and here are the workaround I did to resolve it:

1): Mongoose returns MongooseDocument objects and not plain JSON objects. So use the lean() method on object which will convert it into JSON, and from there you can change it

const leanDoc = await MyModel.findOne().lean();

---------------------------------OR---------------------------------

2): Create a deep copy of the result returned by find query as shown below

Books.find({}).then(books => { 
    books = JSON.parse(JSON.stringify(books));
    //now you can update the books object
}
vivek sharma
  • 251
  • 3
  • 11