Azure Cosmos DB Partial Document Update: Getting Started

APPLIES TO: NoSQL

This article provides examples illustrating for how to use Partial Document Update with .NET, Java, and Node SDKs. This article also details common errors that you may encounter. Code samples for the following scenarios have been provided:

  • Executing a single patch operation
  • Combining multiple patch operations
  • Conditional patch syntax based on filter predicate
  • Executing patch operation as part of a Transaction

Support for Partial document update (Patch API) in the Azure Cosmos DB .NET v3 SDK is available from version 3.23.0 onwards. You can download it from the NuGet Gallery

Note

A complete partial document update sample can be found in the .NET v3 samples repository on GitHub.

  • Executing a single patch operation

    ItemResponse<Product> response = await container.PatchItemAsync<Product>(
        id: "e379aea5-63f5-4623-9a9b-4cd9b33b91d5",
        partitionKey: new PartitionKey("road-bikes"),
        patchOperations: new[] {
            PatchOperation.Replace("/price", 355.45)
        }
    );
    
    Product updated = response.Resource;
    
  • Combining multiple patch operations

    List<PatchOperation> operations = new ()
    {
        PatchOperation.Add($"/color", "silver"),
        PatchOperation.Remove("/used"),
        PatchOperation.Increment("/price", 50.00)
    };
    
    ItemResponse<Product> response = await container.PatchItemAsync<Product>(
        id: "e379aea5-63f5-4623-9a9b-4cd9b33b91d5",
        partitionKey: new PartitionKey("road-bikes"),
        patchOperations: operations
    );
    
  • Conditional patch syntax based on filter predicate

    PatchItemRequestOptions options = new()
    {
        FilterPredicate = "FROM products p WHERE p.used = false"
    };
    
    List<PatchOperation> operations = new ()
    {
        PatchOperation.Replace($"/price", 100.00),
    };
    
    ItemResponse<Product> response = await container.PatchItemAsync<Product>(
        id: "e379aea5-63f5-4623-9a9b-4cd9b33b91d5",
        partitionKey: new PartitionKey("road-bikes"),
        patchOperations: operations,
        requestOptions: options
    );
    
  • Executing patch operation as a part of a Transaction

    TransactionalBatchPatchItemRequestOptions options = new()
    {
        FilterPredicate = "FROM products p WHERE p.used = false"
    };
    
    List<PatchOperation> operations = new ()
    {
        PatchOperation.Add($"/new", true),
        PatchOperation.Remove($"/used")
    };
    
    TransactionalBatch batch = container.CreateTransactionalBatch(
        partitionKey: new PartitionKey("road-bikes")
    );
    batch.PatchItem(
        id: "e379aea5-63f5-4623-9a9b-4cd9b33b91d5",
        patchOperations: operations,
        requestOptions: options
    );
    batch.PatchItem(
        id: "892f609b-8885-44df-a9ed-cce6c0bd2b9e",
        patchOperations: operations,
        requestOptions: options
    );
    
    TransactionalBatchResponse response = await batch.ExecuteAsync();
    bool success = response.IsSuccessStatusCode;
    

Support for Server-Side programming

Partial Document Update operations can also be executed on the server-side using Stored procedures, triggers, and user-defined functions.

this.patchDocument = function (documentLink, patchSpec, options, callback) {
    if (arguments.length < 2) {
        throw new Error(ErrorCodes.BadRequest, sprintf(errorMessages.invalidFunctionCall, 'patchDocument', 2, arguments.length));
    }
    if (patchSpec === null || !(typeof patchSpec === "object" || Array.isArray(patchSpec))) {
        throw new Error(ErrorCodes.BadRequest, errorMessages.patchSpecMustBeObjectOrArray);
    }

    var documentIdTuple = validateDocumentLink(documentLink, false);
    var collectionRid = documentIdTuple.collId;
    var documentResourceIdentifier = documentIdTuple.docId;
    var isNameRouted = documentIdTuple.isNameRouted;

    patchSpec = JSON.stringify(patchSpec);
    var optionsCallbackTuple = validateOptionsAndCallback(options, callback);

    options = optionsCallbackTuple.options;
    callback = optionsCallbackTuple.callback;

    var etag = options.etag || '';
    var indexAction = options.indexAction || '';

    return collectionObjRaw.patch(
        collectionRid,
        documentResourceIdentifier,
        isNameRouted,
        patchSpec,
        etag,
        indexAction,
        function (err, response) {
            if (callback) {
                if (err) {
                    callback(err);
                } else {
                    callback(undefined, JSON.parse(response.body), response.options);
                }
            } else {
                if (err) {
                    throw err;
                }
            }
        }
    );
}; 

Note

Definition of validateOptionsAndCallback can be found in the .js DocDbWrapperScript on GitHub.

  • Sample parameter for patch operation

    function () {
       var doc = {
          "id": "exampleDoc",
          "field1": {
             "field2": 10,
             "field3": 20
          }
       };
       var isAccepted = __.createDocument(__.getSelfLink(), doc, (err, doc) => {
             if (err) throw err;
             var patchSpec = [
                {"op": "add", "path": "/field1/field2", "value": 20}, 
                {"op": "remove", "path": "/field1/field3"}
             ];
             isAccepted = __.patchDocument(doc._self, patchSpec, (err, doc) => {
                   if (err) throw err;
                   else {
                      getContext().getResponse().setBody(docPatched);
                   }
                }
             }
             if(!isAccepted) throw new Error("patch was't accepted")
          }
       }
       if(!isAccepted) throw new Error("create wasn't accepted")
    }
    

Troubleshooting

Here's a list of common errors that you might encounter while using this feature:

Error Message Description
Invalid patch request: check syntax of patch specification The Patch operation syntax is invalid. For more information, see the partial document update specification
Invalid patch request: Can't patch system property SYSTEM_PROPERTY. System-generated properties like _id, _ts, _etag, _rid aren't modifiable using a Patch operation. For more information, see: Partial Document Update FAQs
The number of patch operations can't exceed 10 There's a limit of 10 patch operations that can be added in a single patch specification. For more information, see: Partial Document Update FAQs
For Operation(PATCH_OPERATION_INDEX): Index(ARRAY_INDEX) to operate on is out of array bounds The index of array element to be patched is out of bounds
For Operation(PATCH_OPERATION_INDEX)): Node(PATH) to be replaced has been removed earlier in the transaction. The path you're trying to patch doesn't exist.
For Operation(PATCH_OPERATION_INDEX): Node(PATH) to be removed is absent. Note: it may also have been removed earlier in the transaction.  The path you're trying to patch doesn't exist.
For Operation(PATCH_OPERATION_INDEX): Node(PATH) to be replaced is absent. The path you're trying to patch doesn't exist.
For Operation(PATCH_OPERATION_INDEX): Node(PATH) isn't a number. Increment operation can only work on integer and float. For more information, see: Supported Operations
For Operation(PATCH_OPERATION_INDEX): Add Operation can only create a child object of an existing node(array or object) and can't create path recursively, no path found beyond: PATH. Child paths can be added to an object or array node type. Also, to create nth child, n-1th child should be present
For Operation(PATCH_OPERATION_INDEX): Given Operation can only create a child object of an existing node(array or object) and can't create path recursively, no path found beyond: PATH. Child paths can be added to an object or array node type. Also, to create nth child, n-1th child should be present

Next steps