Azure Cosmos DB - Possible to create a copy of JSON document

Viewed 1014

We have a customer device data usage document (e.g. with name "ABC_XYZ") that when customer deprovision device, should be archived for analytical purpose. The solution is to create another document (e.g. with name "ABC_XYZ_09252017_1748") and delete current one (same document will hold more data in future when device is provisioned again).

To achieve this, I was hoping to find a method in SDK but could not find any. Only Create (CreateDocumentAsync) and Replace (ReplaceDocumentAsync).

I could create a new one with new name and then copy data from old one into new one and then delete the old one. Wondering is there one single API e.g. CopyDocumentAsync or any other better way to achieve this?

2 Answers

There is no "copy document" API call (or related SDK call). You'd need to read the current document (which you can do with a direct read, vs a query), then create a new document (followed by deleting the original).

If you want this to occur atomically, you would need to put the query + write + delete into a stored procedure (which treats its operations as a single transaction).

Alternatively, you could leave your original document intact and mark it as deleted (with an additional property), and then filter on that property when doing your normal reads/queries.

Here's an example of how you might implement this as a stored procedure:

function storedProcedure(copyDocId) {
  let context = getContext();
  let response = context.getResponse();
  let collection = context.getCollection();
  let collectionLink = collection.getAltLink();

  let documentLink = collection.getAltLink() + "/docs/" + copyDocId;

  let canRead = collection.readDocument(documentLink, {}, readDocumentCallback);
  if (!canRead) throw "Unable to check for existence of document";

  function readDocumentCallback(err, doc, responseOptions) {
    if (err) throw err;
    doc.id = doc.id + 'XYZ'; // or however you determine this

    let canDelete = collection.deleteDocument(documentLink, {},
      function (err, deleted) {
        if (err) throw err;

        let canCreate = collection.createDocument(collectionLink, doc,
          function (err, newDoc) {
            if (err) throw err;

            response.setBody(newDoc);
          }
        );

        if (!canCreate) throw "Unable to create copy";
      }
    );

    if (!canDelete) throw "Unable to delete old document";
  }
}

Once the SPROC has been added to your collection, invoking it from your WebAPI is simple:

var sprocUri = UriFactory.CreateStoredProcedureUri(databaseId, collectionId, sprocName);
var newDoc await documentClient.ExecuteStoredProcedureAsync<dynamic>(sprocUri, idOfDocToCopy);
Related