Get Even More Visitors To Your Blog, Upgrade To A Business Listing >>

Mongoose - Upserting Documents with Nested Models

Mongoose - Upserting Documents with Nested Models

Problem

I have a basic document with a 'checked_in' flag in my express app:

module.exports = Book= mongoose.model('Book', new Schema({
 name : String,
 checked_in : Boolean
},{ collection : 'Book' }));

I wanted to keep a log of when books are checked in and out so I came up with another schema:

var action = new Schema({
 checked_in: Boolean,     
});

module.exports = Activity = mongoose.model('Activity', new Schema({
 book_id: String,
 actions: [action]
},{ collection : 'Activity' }));

The 'book_id' should be the document id of a book and when I update a book I need to either create or update the Activity log for that book with a new item inside of actions:

exports.update = function(req, res){  
    return Book.findById(req.params.id, function(err, book) {  
       var activity = new Activity({book_id: book.id});
       activity.actions.push({
           checked_in: req.body.checked_in,
       });

       Activity.update({ book_id: book.id}, activity.toObject(), { upsert: true }));

       book.checked_in = req.body.checked_in;
       return device.save(function(err) {
           return res.send(book);
       });
    });
};

The problem I am having is that nothing gets inserted into the Activity collection. If I use .save() then i just get lots of duplicates in the collection.

UPDATE

I've started re-working things with the advice given below but am still not having any luck with this. Here's what I have now:

module.exports = Activity = mongoose.model('Activity', new Schema({
  book_id: Schema.ObjectId,
  actions: [new Schema({
    checked_in: Boolean,
    last_user: String
  })]
},{ collection : 'Activity' }));

Here's the update code now:

exports.update = function(req, res){  
  // TODO: Check for undefined.
  return book.findById(req.params.id, function(err, book) {    
    if(!err) {
      // Update the book.
      book.checked_in = req.body.checked_in;
      book.last_user = req.body.last_user;    
      book.save();

      // If there's no associated activity for the book, create one. 
      // Otherwise update and push new activity to the actions array.
      Activity.findById(book._id, function (err, activity) {
        activity.actions.push({
          checked_in: req.body.checked_in,
          last_user: req.body.last_user
        })

        activity.save();      
      });
    }
  });
};

What I want to end up with is a document for each book with an array of check outs/ins that gets updated each time someone checks a book in or out. i.e:

{
    book_id: "5058c5ddeeb0a3aa253cf9d4",
    actions: [
        { checked_in: true, last_user: 'ralph' },
        { checked_in: true, last_user: 'gonzo' },
        { checked_in: true, last_user: 'animal' }
    ]
}

Eventually I will have a time stamp within each entry.

Problem courtesy of: backdesk

Solution

There are a couple problems:

  1. You're trying to find the book's activity doc using findById using the book's id instead of the activity's id.
  2. You're not handling the case where the book's activity doc doesn't exist yet.

Try this instead:

Activity.findOne({book_id: book._id}, function (err, activity) {
  if (!activity) {
    // No Activity doc for the book yet, create one.
    activity = new Activity({book_id: book._id});
  }
  activity.actions.push({
    checked_in: req.body.checked_in,
    last_user: req.body.last_user
  });

  activity.save();
});
Solution courtesy of: JohnnyHK

Discussion

View additional discussion.



This post first appeared on Node.js Recipes, please read the originial post: here

Share the post

Mongoose - Upserting Documents with Nested Models

×

Subscribe to Node.js Recipes

Get updates delivered right to your inbox!

Thank you for your subscription

×