Search code examples
mongodbexpressmongoosejson-patch

Validating Mongoose Array Updates with JSON Patch


I am currently building an API which uses the JSON patch specification to do partial updates to MongoDB using the Mongoose ORM.

I am using the node module mongoose-json-patch to apply patches to my documents like so:

var patchUpdate = function(req,  res){
  var patches = req.body;
  var id = req.params.id;
  User.findById(id, function(err, user){
    if(err){ res.send(err);}
    user.patch(patches, function(err){
      if(err){ res.send(err);}
      user.save(function(err){
        if(err) {res.send(err);}
        else {res.send("Update(s) successful" + user);}
      });
    });
  });
};

My main issues occur when I am trying to remove or replace array elements with the JSON patch syntax:

var patches = [{"op":"replace", "path": "/interests/0", "value":"Working"}]

var user = {
    name: "Chad",
    interests: ["Walking", "Eating", "Driving"]
}

This should replace the first item in the array ("Walking") with the new value ("Working"), however I can't figure out how to validate what is actually being replaced. If another request removed /interests/0 prior to the patch being applied, "Eating" would be replaced by "Working" instead of "Walking", which would no longer exist in the array.

I would like to be sure that if the client thinks he is editing "Walking", then he will either successfully edit it, or at least get an error.


Solution

  • After running into the same issue like this myself i'll share my solution. The spec (described here) describes six operations, one of which is test. The source describes the test operation as

    Tests that the specified value is set in the document. If the test fails, then the patch as a whole should not apply.

    To ensure that you're changing the values that you're expecting you should validate the state of the data. You do this by preceeding your replace or remove operation with a test operation, where the value is equal to the expected data state. If the test fails, the following operations will not be executed.

    With the test operation your patch data will look like this:

    var patches = [
      {"op":"test", "path": "/interests/0", "value": currentValue}, //where currentValue is the expected value
      {"op":"replace", "path": "/interests/0", "value":"Working"}
    ]