Search code examples
c#asp.net-coreazure-cosmosdbpatch

Patching a single property on a subdocument in CosmosDB


I'm trying to do a partial update of a subdocument in CosmosDb. My object model is this:

public class PhoneBookCategory
{
    public string Id { get; set; }

    public string Name { get; set; }

    public TestObject SubProp { get; set; }
}

public class TestObject
{
    public string StringProp { get; set; }

    public int? IntProp { get; set; }
}

Say I want to update the Name property and SubProp, I'd do this

List<PatchOperation> updates = [];
updates.Add(PatchOperation.Add($"/Name", "new name"));
updates.Add(PatchOperation.Add($"/SubProp", new TestObject { StringProp = "hello"}));
database.GetContainer("collectionName").PatchItemAsync<PhoneBookCategory>(item.Id, new PartitionKey(item.Id), patchOperations)

This works fine. Now let's assume I want to only modify SubProp.StringProp. so, my second PatchOperation would become

updates.Add(PatchOperation.Add($"/SubProp/StringProp", "hello"));

But that just errors out with Bad Request and no useful error message (reason is ()).

I figured 'maybe, it wants a Replace for a subprop instead since in this case we know that SubProp is present, and we just want to change a value on the SubProp document, so I tried this:

updates.Add(PatchOperation.Replace($"/SubProp/StringProp", "hello"));

still, no dice.

So what am I doing wrong? According to Overview, subdocument updates are allowed.

Note that I'm using CosmosClientOptions.SerializerOptions.PropertyNamingPolicy = CosmosPropertyNamingPolicy.CamelCase (in case anyone wonders why I write the patch operations using CamelCase.


Solution

  • Below is a working sample I tested and can display detail error if you still have.

    using Microsoft.Azure.Cosmos;
    using Newtonsoft.Json;
    internal class Program
    {
        private static async Task Main(string[] args)
        {
            var endpoint = "xxx";
            var key = "xxx";
            var itemId = "a835a3c9-fa0a-42f8-b7c6-8d0e93a2e96b";
            try
            {
                var cosmosClient = new CosmosClient(endpoint,key);
                var database = cosmosClient.GetDatabase("MyDatabase");
                var container = database.GetContainer("MyContainer");
    
                var patchOperations = new[]
                {
                    PatchOperation.Replace("/SubProp/StringProp", "hello")
                };
    
                ItemResponse<PhoneBookCategory> itemResponse = await container.PatchItemAsync<PhoneBookCategory>(itemId, new PartitionKey(itemId), patchOperations);
                Console.WriteLine($"Item modified with id {itemResponse.Resource.Id}");
            }
            catch (CosmosException ex)
            {
                Console.WriteLine($"Cosmos DB Exception with Status {ex.StatusCode} Message: {ex.Message}");
            }
        }
    }
    
    public class PhoneBookCategory
    {
        [JsonProperty(PropertyName = "id")]
        public string Id { get; set; }
        public string Name { get; set; }
        public TestObject SubProp { get; set; }
    }
    public class TestObject
    {
        public string StringProp { get; set; }
        public int? IntProp { get; set; }
    }