JsonDocumnet replace and REST RFC

According to the REST RFC 2616, POST target/id - means to only update the fields you’ve provided (this is an incremental update).

I’m aware of bucket.replace being used to replace the whole JsonDocument but since the RFC needs the ability to replace only a subset of the JsonDocumnet content, I was wondering If there was an optimal way of doing this given a JsonObject that contains only the parts to be replaced.

I can use .put to modify the fetched JsonDocument content one key at a time but I don’t know how can I replace a subset given a JsonObject with the new values, furthermore I’m concerned about not being able to update a document without sending the entire modified document to Couchbase, which might be an overkill If I need to modify just a few values.

Hi @Lethe

There’s no way to put several fields at once on the JsonObject currently, so you’d have to set changed fields individually (iterate on your JsonObject that holds only diffs using getNames(), put these values, maybe recursively)…
This kind of code could be made part of the JsonObject API though (and if you’re willing to contribute, feel very welcome to do so :wink: :wink: )

It can be kind of an overkill but for now there’s now way around it: when you mutate a document, you have to transmit the whole new version of the document to the server (that’s in the protocol).

:bulb: in the future the protocol will evolve to authorize sub-document granularity for mutations

Nice to hear that, you could also think about adding json schema RFC v4 validation to the package, that would totally rock and provide users the ability to control what can or cant get persisted in a platform independent way.

Once I’m done with a solid recursive update of a JsonDocumnet method, I’will be glad to contribute by posting it here or through pull request if you find it more convenient :blush: .

Time to give back to the community, as promised here is the code I’m using to provide a json recurisve merge like functionality.

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import java.io.IOException;
import java.util.Iterator;

/**
 *
 * @author Lethe
 */
public class JsonUtil {

  public static String merge(String json, String update) {
    try {
      ObjectMapper mapper = new ObjectMapper();
      JsonNode jsonNode = mapper.readTree(json);
      JsonNode updateNode = mapper.readTree(update);
      return merge(jsonNode, updateNode).toString();
    } catch (IOException ex) {
      return null;
    }
  }

  public static JsonNode merge(JsonNode jsonNode, JsonNode updateNode) {
    Iterator<String> fieldNameIterator = updateNode.fieldNames();
    String fieldName;
    JsonNode currentNode;
    while (fieldNameIterator.hasNext()) {
      fieldName = fieldNameIterator.next();
      currentNode = jsonNode.get(fieldName);
      if (currentNode != null && currentNode.isObject()) {
        // Recursive object
        merge(currentNode, updateNode.get(fieldName));
      } else {
        if (jsonNode instanceof ObjectNode) {
          // Update field
          ((ObjectNode) jsonNode).put(fieldName, updateNode.get(fieldName));
        }
      }
    }
    return jsonNode;
  }

}

If you like I could make a pull request adding this logic in a merge method to the JsonObject class using your Jackson dependency instead, so it can be used like

JsonObject jsonObject = jsonObject1.merge(jsonObject2);

With its respective junit testing and javadoc.

2 Likes

I’d :heart: that!

however, there’s a pre-requisite to contributing to the SDK which is to sign the CLA, by creating an account on our Gerrit Code Review tool. We use Gerrit for code-reviews (nothing is usually directly pushed through Github directly).

You’ll need an OpenID account… Since Google don’t provide this anymore (disregard the “login with Google” link in gerrit), I found that a good provider is Launchpad/UbuntuOne

You can then do a PR on Github that I’ll review and merge or, if you’re already familiar with it, do it in Gerrit (and put even more :star: in my eyes ;)).

Also I see that you are dealing with Strings and JsonNodes ? As a JsonObject method, the code would be different, maybe a bit more straightforward, like putAll on a Map?

If this is too much strain, I totally understand, just open a ticket in our Jira (you should be able to login with your forum login) and I’ll provide a merge (or putAll) method later in JsonObject.

I’check Gerrit and the OpenID and see what I can do :smile:.

The reason I used JsonNodes and String in my generic example is because, after doing a merge in my REST service I pass the String to the Json Schema validator, and if everything is ok then I persist it with Couchbase. Since the Json Schema validator is an external lib it knows nothing about Couchbase JsonObject and forces me to handle the validation as Strings, but I completely agree with you If this method were to be added as a putAll under JsonObject or Map nor the parameters or the return statement should be handled directly as Strings.

As soon I’ll find a bit of time this week I’ll give it a try and let you know so you can tell me what you think. :blush:

awesome, thanks @Lethe :thumbsup: