CosmosDB : Updating a record
Tags: cosmosdb, nosql, crud, programming
Published: 2020-11-20 05:04:37

Updating a record in CosmosDB is not very straight forward. Unlike any relational databases where we can write a simple `update` statement, in CosmosDB (SQL or Document API) we need to first fetch the record and then modify and push. Just like we need to do with the file modification. 

In the below sample written in C# using .NET core 3.1 and the reference to `Microsoft.Azure.Cosmos` nuget package. In CosmosDB we always deal with semi-structure data and because we have an application or some backend system which needs to read it we always need to know. Schema is everywhere, it's the ghost of Schema which haunts us as a developer. 

Before we jump into the CosmosDB a recap on how it is structured, 
`CosmosDB Collection > Database > Container > Document (individual items)`. Also we need a `Partition Key` which kind of groups the data within a single container. Assume you have a container of students then your obvious choice for the `Partition Key` will be their class. You can also index the data by some common column like Id or Email. Indexes are mainly used for quick search. As `Partition Key` defines the data location strategy, once the data grows significantly the bigger partition will split themselves from main storage to another physical storage. Thus allowing the query an ability to fetch quickly from that partition key. Hence designing `Partition Key` is always forward looking. 

```csharp
namespace CosmosDBSDK
{
    using System;
    using System.Threading.Tasks;
    using System.Net;
    using Microsoft.Azure.Cosmos;
    class Program
    {
        //CosmosDB endpoint 
        private static readonly string EndpointUri = "https://cosmosname.documents.azure.com:443/";
        //CosmosDB account key - you should protect it
        private static readonly string PrimaryKey = "kdkdkdkd";
        private static CosmosClient cosmosClient;
        private static Database database;
        private static Container container;

        static async Task Main(string[] args)
        {            
            cosmosClient = new CosmosClient(EndpointUri, PrimaryKey);
            database = await cosmosClient.CreateDatabaseIfNotExistsAsync("HR");
            container = await database.CreateContainerIfNotExistsAsync("Employee", "/LastName");

#region "Add Record - temp"
            var rand = new Random().Next().ToString();
            var emp = new Employee()
            {
                id = rand, FirstName = "Wriju", LastName="Ghosh"
            };

            try
            {
                ItemResponse<Employee> empResponse = await container.ReadItemAsync<Employee>(emp.id, new PartitionKey(emp.LastName));
                Console.WriteLine("Item in database with id: {0} already exists\n", empResponse.Resource.id);
            }
            catch(CosmosException ex) when (ex.StatusCode == HttpStatusCode.NotFound)
            {
                ItemResponse<Employee> empResponse = await container.CreateItemAsync<Employee>(emp, new PartitionKey(emp.LastName));
                Console.WriteLine("Created item in database with id: {0} Operation consumed {1} RUs.\n", empResponse.Resource.id, empResponse.RequestCharge);
            }
#endregion
            //This is for testing. You can enable the two lines below to enter a record then check back to cosmos DB then comeback here to modify. 
            // System.Console.WriteLine("Wait to check the CosmosDB entry... press a key to continue");
            // Console.ReadKey();

            ItemResponse<Employee> empUpdResponse = await container.ReadItemAsync<Employee>(rand, new PartitionKey("Ghosh"));
            var itemBody = empUpdResponse.Resource;
            
            // update FirstName
            itemBody.FirstName = itemBody.FirstName + "_Changed";

            // replace/update the item with the updated content
            empUpdResponse = await container.ReplaceItemAsync<Employee>(itemBody, itemBody.id, new PartitionKey(itemBody.LastName));
            Console.WriteLine("Updated Employee [{0},{1}].\n \tEmployee is now: {2}\n", itemBody.LastName, itemBody.id, empUpdResponse.Resource);
        }        
    }

    //This is the model class, if you do not use id column you need to add JSON property attribute to make it id when it deserializes. 
    public class Employee
    {
        public string id{get;set;}
        public string FirstName { get; set; }
        public string LastName { get; set; }
    }
}

```