Introduction

Azure Cosmos DB is a globally distributed database service provided by Microsoft, that supports multiple data models with supported APIs. It accommodates various data models, enabling developers to create scalable and high-performance applications. When using Cosmos DB with C#, interaction typically occurs through its supported APIs.

A data model refers to the way data is structured and stored within the database. This concept is essential because Cosmos DB is a multi-model database, meaning it supports multiple data models and APIs to address diverse application requirements. These APIs enable Cosmos DB to operate as a versatile, multi-model database while ensuring efficiency, consistency, and scalability.

Here’s an overview of the data models and a breakdown of the APIs available in Cosmos DB.

Data Models with Usage and Supported APIs


1. Document Mode

This model is built to store and manage data in a flexible, schema-agnostic manner, often utilizing JSON-like documents.

1.1. Usage of Data Model

It is particularly well-suited for applications that deal with hierarchical or semi-structured data. Each document is independent, supporting nested structures and varying fields, which removes the necessity for fixed schemas. Common use cases include storing data with flexible schemas, such as user profiles or product catalogs.

1.2. Supported API

This model support, particularly through its SQL API, allows developers to perform rich queries, indexing, and transactions on JSON documents, ensuring efficient data retrieval and manipulation.

The MongoDB API is also compatible with the MongoDB wire protocol, so you can use MongoDB drivers, tools, and query syntax. You need to be familiar with MongoDB queries and operators.

MongoDB API is preferred if you have an existing MongoDB application and want to migrate to Cosmos DB with minimal changes.

Below is a C# example to illustrate the ‘Document Model’ with its SQL API

/* 
   Program to creates CosmosClient, Database and container  
   Product creation, insertion and query on the basis on category 
*/

using Microsoft.Azure.Cosmos; 
using System; 
using System.Threading.Tasks; 

public class Program 
{ 
    private static readonly string EndpointUri = "<your-cosmosdb-endpoint>"; 
    private static readonly string PrimaryKey = "<your-cosmosdb-key>"; 
    private static readonly string DatabaseId = "ProductCatalog"; 
    private static readonly string ContainerId = "Products"; 

    public static async Task Main(string[] args) 
    { 
        CosmosClient cosmosClient = new CosmosClient(EndpointUri, PrimaryKey); 
        Database database = await cosmosClient.CreateDatabaseIfNotExistsAsync(DatabaseId); 
        Container container = await database.CreateContainerIfNotExistsAsync(ContainerId, "/category"); 
        Console.WriteLine("Database and container created successfully."); 

        var product = new 
        { 
            id = Guid.NewGuid().ToString(), 
            name = "Wireless Mouse", 
            category = "Electronics", 
            price = 29.99, 
            attributes = new 
            { 
                brand = "Logitech", 
                color = "Black", 
                wireless = true 
            } 
        }; 

        ItemResponse<dynamic> response = await container.CreateItemAsync(product, new PartitionKey(product.category)); 
        Console.WriteLine($"Inserted product with ID: {response.Resource.id}"); 

        var sqlQueryText = "SELECT * FROM c WHERE c.category = 'Electronics'"; 
        QueryDefinition queryDefinition = new QueryDefinition(sqlQueryText); 
        FeedIterator<dynamic> queryResultSetIterator = container.GetItemQueryIterator<dynamic>(queryDefinition); 

        while (queryResultSetIterator.HasMoreResults) 
        { 
            FeedResponse<dynamic> currentResultSet = await queryResultSetIterator.ReadNextAsync(); 
            foreach (var item in currentResultSet) 
            { 
                Console.WriteLine($"Product ID: {item.id}"); 
                Console.WriteLine($"Name: {item.name}"); 
                Console.WriteLine($"Category: {item.category}"); 
                Console.WriteLine($"Price: {item.price}"); 
                Console.WriteLine("Attributes:"); 
                Console.WriteLine($"  Brand: {item.attributes.brand}"); 
                Console.WriteLine($"  Color: {item.attributes.color}"); 
                Console.WriteLine($"  Wireless: {item.attributes.wireless}"); 
                Console.WriteLine(); 
            } 
        } 
    } 
}

2. Key-Value Model

The model is one of the simplest and most efficient data models, where data is stored as a collection of key-value pairs. Each key is distinct and corresponds directly to a specific value.

2.1. Usage of Key-Value Model

This model is ideal for scenarios requiring fast lookups, caching, or storing simple data structures.

2.2. Supported API

This model is supported through its Table API, which is compatible with Azure Table Storage.

C# Example. Key-Value Model with Cosmos DB Table API

/*  
    This program demonstarte, how to create a CloudStorageAccount, CloudTableClient to interact with the Table API and a reference to the table (if it doesn't exist) 
	How to Insert a key-value pair and retrieve a value by key 
*/ 

using Microsoft.Azure.Cosmos.Table; 
using System; 
using System.Threading.Tasks; 

public class Program 
{ 
    private static readonly string ConnectionString = "<your-cosmosdb-table-api-connection-string>"; 
    private static readonly string TableName = "MyKeyValueTable"; 

    public static async Task Main(string[] args) 
    { 
		CloudStorageAccount storageAccount = CloudStorageAccount.Parse(ConnectionString); 
		CloudTableClient tableClient = storageAccount.CreateCloudTableClient(); 
		CloudTable table = tableClient.GetTableReference(TableName); 
		await table.CreateIfNotExistsAsync(); 

        var entity = new DynamicTableEntity("partitionKey", "rowKey"); 
        entity.Properties.Add("Name", new EntityProperty("M Imran Ansari")); 
        entity.Properties.Add("Age", new EntityProperty(35)); 
 
        TableOperation insertOperation = TableOperation.InsertOrReplace(entity); 
        await table.ExecuteAsync(insertOperation); 

        Console.WriteLine("Key-value pair inserted successfully."); 

        TableOperation retrieveOperation = TableOperation.Retrieve<DynamicTableEntity>("partitionKey", "rowKey"); 
        TableResult result = await table.ExecuteAsync(retrieveOperation); 

        if (result.Result != null) 
        { 
            var retrievedEntity = (DynamicTableEntity)result.Result; 
            Console.WriteLine($"Retrieved Name: {retrievedEntity.Properties["Name"].StringValue}"); 
            Console.WriteLine($"Retrieved Age: {retrievedEntity.Properties["Age"].Int32Value}"); 
        } 
        else 
        { 
            Console.WriteLine("Key-value pair not found."); 
        } 
    } 
}

3. Column-Family Mode

This data model organizes data into columns and rows, but unlike traditional relational databases, it groups columns into families. Each column family contains a set of related columns, and each row can have a different set of columns within a family.

3.1. Usage of Column-Famil Model

This model is highly efficient for read and write operations, especially when dealing with large-scale, sparse datasets. It is commonly used in applications like time-series data, recommendation systems, and big data analytics.

3.2. Supported API

This model is supported through its Cassandra API, which is compatible with Apache Cassandra. Here is an explanation and a C# example of how to use the Column-Family Model in Cosmos DB.

C# Example. Column-Family Model with Cosmos DB Cassandra API

/*  
    This program demonstrates, how to create a Cassandra cluster instance, a session to interact with the Cassandra API and a keyspace (if it doesn't exist) 
	How to create a table with a column family, Insert data into the table and retravel 
*/ 

using Cassandra; 
using System; 

public class Program 
{ 
    private static readonly string ContactPoint = "<your-cosmosdb-cassandra-endpoint>"; 
    private static readonly string Username = "<your-username>"; 
    private static readonly string Password = "<your-password>"; 
    private static readonly string Keyspace = "mykeyspace"; 
    private static readonly string TableName = "mycolumnfamilytable"; 

    public static void Main(string[] args) 
    { 
		Cluster cluster = Cluster.Builder() 
		.WithCredentials(Username, Password) 
		.WithPort(10350) // Default port for Cosmos DB Cassandra API 
		.AddContactPoint(ContactPoint) 
		.Build(); 

        ISession session = cluster.Connect(); 
        session.Execute($"CREATE KEYSPACE IF NOT EXISTS {Keyspace} WITH REPLICATION = {{ 'class' : 'NetworkTopologyStrategy', 'datacenter1' : 1 }};"); 
        session.Execute($"USE {Keyspace};"); 

        session.Execute($@" 
            CREATE TABLE IF NOT EXISTS {TableName} ( 
                user_id UUID PRIMARY KEY, 
                profile MAP<TEXT, TEXT>, 
                preferences MAP<TEXT, TEXT> 
            );"); 
  
		Console.WriteLine("Table created successfully."); 

        var userId = Guid.NewGuid(); 
        var insertQuery = $@" 
            INSERT INTO {TableName} (user_id, profile, preferences) 
            VALUES ({userId}, {{ 'name': 'M Imran Ansari', 'age': '35' }}, {{ 'theme': 'dark', 'notifications': 'enabled' }});"; 

        session.Execute(insertQuery); 
        Console.WriteLine("Data inserted successfully."); 
        var selectQuery = $"SELECT * FROM {TableName} WHERE user_id = {userId};"; 
        var result = session.Execute(selectQuery); 
 
        foreach (var row in result) 
        { 
            Console.WriteLine($"User ID: {row["user_id"]}"); 
            Console.WriteLine("Profile:"); 
            foreach (var entry in row["profile"] as System.Collections.Generic.IDictionary<string, string>) 
            { 
                Console.WriteLine($"{entry.Key}: {entry.Value}"); 
            } 

            Console.WriteLine("Preferences:"); 

            foreach (var entry in row["preferences"] as System.Collections.Generic.IDictionary<string, string>) 
            { 
                Console.WriteLine($"{entry.Key}: {entry.Value}"); 
            } 
        } 
    } 
} 

4. Graph Model

The Graph Model in Azure Cosmos DB is designed to represent and store data as a collection of nodes (vertices) and edges (relationships).

4.1. Usage of Graph Model

This model is ideal for applications that require managing complex relationships, such as social networks, recommendation engines, fraud detection systems, or knowledge graphs.

4.2. Supported API

This model is supported through its Gremlin API to interact with the Graph Model, allowing you to perform graph traversals and queries using the Gremlin query language.

Here is an explanation and a C# example of how to use the Graph Model in Cosmos DB.

C# Example. Graph Model with Cosmos DB Gremlin API (Social Network)

/* 
	This program demonstrates, how to create a Gremlin server connection and client 
	How to add vertices (nodes), edges (relationships) and query the graph 
*/ 

using Gremlin.Net.Driver; 
using Gremlin.Net.Structure.IO.GraphSON; 
using System; 
using System.Collections.Generic; 
using System.Threading.Tasks; 

public class Program 
{ 
    private static readonly string Hostname = "<your-cosmosdb-gremlin-endpoint>"; 
    private static readonly int Port = 443; 
    private static readonly string AuthKey = "<your-cosmosdb-key>"; 
    private static readonly string Database = "socialnetwork"; 
    private static readonly string Collection = "users"; 
  
    public static async Task Main(string[] args) 
    { 
        var gremlinServer = new GremlinServer(Hostname, Port, enableSsl: true, username: $"/dbs/{Database}/colls/{Collection}", password: AuthKey); 

        using (var gremlinClient = new GremlinClient(gremlinServer, new GraphSON2Reader(), new GraphSON2Writer(), GremlinClient.GraphSON2MimeType)) 
        { 
            await AddVertexAsync(gremlinClient, "person", new Dictionary<string, object> { { "id", "1" }, { "name", "M Imran Ansari" }, { "age", 35 } }); 
            await AddVertexAsync(gremlinClient, "person", new Dictionary<string, object> { { "id", "2" }, { "name", "M Saqlain" }, { "age", 25 } }); 
            await AddVertexAsync(gremlinClient, "person", new Dictionary<string, object> { { "id", "3" }, { "name", "Kashif Shahzad" }, { "age", 28 } }); 
  
            await AddEdgeAsync(gremlinClient, "1", "2", "knows"); 
            await AddEdgeAsync(gremlinClient, "2", "3", "knows"); 
  
            Console.WriteLine("Vertices and edges added successfully."); 

            var query = "g.V('1').out('knows').values('name')"; 
            var result = await gremlinClient.SubmitAsync<dynamic>(query); 
  
            Console.WriteLine("People Alice knows:"); 
            foreach (var name in result) 
            { 
                Console.WriteLine(name); 
            } 
        } 
    } 
  
    private static async Task AddVertexAsync(GremlinClient gremlinClient, string label, Dictionary<string, object> properties) 
    { 
        var query = $"g.addV('{label}')"; 
        foreach (var property in properties) 
        { 
            query += $".property('{property.Key}', '{property.Value}')"; 
        } 
        await gremlinClient.SubmitAsync<dynamic>(query); 
    } 
  
    private static async Task AddEdgeAsync(GremlinClient gremlinClient, string fromId, string toId, string label) 
    { 
        var query = $"g.V('{fromId}').addE('{label}').to(g.V('{toId}'))"; 
        await gremlinClient.SubmitAsync<dynamic>(query); 
    } 
}

5. Relational-Like Model

Cosmos DB is a NoSQL database, but it supports relational-like modeling using its Core (SQL) API and features like embedded documents, denormalization, and partitioning. Since it doesn’t support traditional relational joins across containers like SQL Server, you need to model data differently to optimize queries.

5.1. Methods to Implement a Relational-Like Model in Cosmos DB:

5.1.1. Embedded Documents (Denormalization)

Here is an example of an order object:

{
	"id": "order123",
	"customerId": "cust001",
	"orderDate": "2024-01-30",
	"items": [
		{
			"productId": "prod101",
			"quantity": 2,
			"price": 25.99
		},
		{
			"productId": "prod102",
			"quantity": 1,
			"price": 12.5
		}
	]
}

5.1.2. Reference Model (Normalization)

Here is an example of orders and items collection:

// Orders Collection: 
{ 
  "id": "order123", 
  "customerId": "cust001", 
  "orderDate": "2024-01-30", 
  "itemIds": ["item001", "item002"] 
} 

// Items Collection: 
{ 
  "id": "item001", 
  "productId": "prod101", 
  "quantity": 2, 
  "price": 25.99 
} 

5.1.3. Using Cosmos DB's JOINs in Queries

Example. Querying embedded items using a JOIN

SELECT o.id, i.productId, i.quantity, i.price 
FROM Orders o 
JOIN i IN o.items
WHERE o.id = "order123" 

5.1.4. Partitioning for Scalability

Comparison of APIs

API Primary Use Case Query Language Compatible With
SQL API Document-based workloads SQL-like queries Native Cosmos DB SDKs (e.g., .NET)
MongoDB API MongoDB-compatible applications MongoDB query syntax MongoDB drivers and tools
Gremlin API Graph-based workloads Gremlin traversal language Gremlin.NET
Table API Key-value workloads Table Storage API Azure Table SDKs
Cassandra API Column-family workloads Cassandra Query Language Cassandra drivers and tools


Mapping Data Models to APIs

Data Model Supported API Primary Use Case
Document Model SQL API, MongoDB API Web apps, e-commerce, content management.
Key-Value Model Table API Caching, session state, user settings.
Column-Family Model Cassandra API IoT telemetry, time-series data.
Graph Model Gremlin API Social networks, recommendations.
Relational-Like Model SQL API Relational database migration.


Conclusion

Azure Cosmos DB stands out as a powerful, globally distributed database service that supports multiple data models and APIs, making it a versatile solution for modern application development. Its ability to handle diverse data structures—such as documents, key-value pairs, graphs, and columnar data—while maintaining high performance, scalability, and consistency makes it an ideal choice for developers building complex, data-driven applications. By leveraging the appropriate API and data model in C#, developers can efficiently manage and interact with data, ensuring their applications meet both current and future demands. Whether you're working with the SQL API for document storage, the Gremlin API for graph data, or other supported APIs, Cosmos DB provides the tools and flexibility needed to deliver robust, scalable solutions.