Module 4 of 9 25–30% 3 sub-modules · 24 units Domain 2: Develop AI Solutions by Using Azure Data Management Services

Develop AI Solutions with Azure Cosmos DB for NoSQL

Connect to Cosmos DB using the SDK and run queries. Optimize RU consumption with indexing policies and consistency levels. Store embeddings and execute vector similarity search. Implement change feed processor.

Azure Cosmos DBDiskANNChange FeedPython SDK

Last updated: · Aligned with Course AI-200T00-A

Module

Build Queries for Azure Cosmos DB for NoSQL

units
Unit 1

Introduction

3 min

Azure Cosmos DB for NoSQL is a globally distributed, schemaless JSON document database with automatic indexing and single-digit millisecond reads. AI applications use it for product catalogs, user profiles, inference result caching, and conversation history. The key insight: partition key selection determines performance more than anything else.

Exam Tip

Exam pillars: 1) Resource hierarchy (Account→DB→Container→Item) 2) Partition key design rules 3) Request Units (RU) cost model 4) SDK CRUD pattern (point read vs query) 5) Throughput modes (Manual vs Autoscale).
Unit 2

Explore Azure Cosmos DB for NoSQL

10 min

Cosmos DB: Container partitioned by userId — data spread across physical partitions

Cosmos DB ContainerPartition /userIduser-001: 5 RUuser-002: 3 RUuser-003: 8 RUPartition /regionus-east: 12 RUeu-west: 7 RUasia-se: 4 RUVector IndexDiskANN index1536-dim embeddingscosine similarity

1. Resource Hierarchy

Memory aid: Library → Floor → Bookshelf → Book

  1. Account — top-level resource. Unique DNS endpoint: https://myaccount.documents.azure.com:443/. Holds up to 500 databases.
  2. Database — logical namespace. Groups related containers. Can share throughput across containers (shared throughput mode).
  3. Container — unit of scalability. Requires a partition key. Schema-agnostic — items in the same container can have different shapes.
  4. Item — individual JSON document. Must have id + partition key value to be uniquely addressable via point read.

2. Partition Key Design — The Most Important Decision

  1. High cardinality — many distinct values spread data across partitions evenly. Good: userId, tenantId, productId. Bad: status (only true/false), isActive (boolean).
  2. Query-aligned — if you always query by userId, make it the partition key. Same-partition queries cost fewer RUs and are faster.
  3. Immutable — you cannot change a container's partition key. Wrong choice = create new container + migrate all data.

Common Gotcha

Using isActive (boolean) or status as a partition key creates only 2-3 hot partitions and will throttle under load. The exam tests this anti-pattern frequently — it's always wrong.

3. Request Units (RUs) — Cost Model

  1. Point read 1 KB item ≈ 1 RU — cheapest operation. Requires both id AND partition key.
  2. Write 1 KB item ≈ 5–10 RUs — always more expensive than reads.
  3. Simple query (with partition key) ≈ 3–5 RUs
  4. Cross-partition query (scan) ≈ 100+ RUs — fan-out to all partitions. Avoid in hot paths.

Monitor cost via the x-ms-request-charge response header or response.request_charge in SDK.

4. Throughput Modes

  1. Manual throughput — fixed RU/s you specify. Min 400 RU/s for dedicated container. Predictable cost. Use for steady, known workloads.
  2. Autoscale throughput — scales 10%–100% of your set maximum automatically. Min 1,000 RU/s. No over-provisioning. Best for AI workloads with bursty or unpredictable traffic.

Exam Tip

Bursty AI workloads = Autoscale. Predictable steady workload = Manual. The exam gives a scenario and asks which throughput mode — match traffic pattern to mode.

5. Item System Properties

  1. _etag — optimistic concurrency token. Use with if_match to prevent lost updates from race conditions.
  2. _ts — Unix timestamp of last modification. Use for change feed or auditing.
  3. _rid, _self — internal resource identifiers (read-only, auto-generated).
Unit 3

Implement the Cosmos DB NoSQL SDK

12 min

1. Client Initialization — Singleton Pattern

Create CosmosClient ONCE at application startup and reuse it. Recreating per request wastes connections and adds latency (TCP handshake overhead).

from azure.cosmos import CosmosClient
from azure.identity import DefaultAzureCredential

client = CosmosClient(
    "https://myaccount.documents.azure.com:443/",
    credential=DefaultAzureCredential()   # Managed identity in prod, az CLI locally
)
db = client.get_database_client("aidata")
container = db.get_container_client("products")

2. Authentication Options

  1. Account keys — simple, full access. Use for dev/test only. Avoid in production (long-lived, no audit trail).
  2. Entra ID (RBAC) — production standard. Assign Cosmos DB Built-in Data Contributor (read/write) or Cosmos DB Built-in Data Reader (read-only). Works with managed identities.

3. CRUD Operations

# Create — fails if id already exists
container.create_item(body={"id": "product-123", "categoryId": "electronics", "name": "Speaker"})

# Upsert — insert or replace (no conflict error)
container.upsert_item(body={...})

# Point Read — cheapest: ~1 RU for 1 KB (REQUIRES both id AND partition key)
item = container.read_item(item="product-123", partition_key="electronics")

# Optimistic concurrency — prevent lost updates
container.replace_item(item=item["id"], body=item, if_match=item["_etag"])

# Delete
container.delete_item(item="product-123", partition_key="electronics")

Common Gotcha

Point read requires BOTH id AND partition key value. If you only know the id, you must query (much more expensive). Always design your access pattern to use point reads when possible.

4. Idempotent Container Creation

from azure.cosmos import PartitionKey, ThroughputProperties

db = client.create_database_if_not_exists(id="aidata")
container = db.create_container_if_not_exists(
    id="products",
    partition_key=PartitionKey(path="/categoryId"),
    offer_throughput=ThroughputProperties(auto_scale_max_throughput=4000)
)
Unit 4

Build SQL Queries for Cosmos DB

10 min

1. SQL Syntax (JSON-aware)

# Basic filter + projection (c = container alias)
SELECT c.id, c.name, c.price
FROM c
WHERE c.categoryId = "electronics"
  AND c.price < 100

# Array contains
SELECT c.id FROM c WHERE ARRAY_CONTAINS(c.features, "wifi")

2. Parameterized Queries via SDK

query = "SELECT * FROM c WHERE c.categoryId = @cat AND c.price < @price"
params = [{"name": "@cat", "value": "electronics"}, {"name": "@price", "value": 100}]

items = list(container.query_items(
    query=query,
    parameters=params,
    partition_key="electronics"   # Stays in one partition = cheap
))

3. Cross-Partition Queries — Avoid in Hot Paths

Queries without the partition key in WHERE clause fan out to ALL partitions. They cost dramatically more RUs and have higher latency. If you frequently need cross-partition queries, reconsider your partition key choice.

Common Gotcha

Cross-partition queries are expensive. The exam will present a scenario with high RU consumption + latency and ask why — the answer is cross-partition queries due to poor partition key design.

Cosmos DB Master Cheatsheet

HierarchyAccount → Database → Container → Item
Partition key rulesHigh cardinality + query-aligned + immutable
Point read cost~1 RU for 1 KB (cheapest operation)
Write cost~5–10 RUs per 1 KB
Autoscale min1,000 RU/s
Manual min400 RU/s dedicated container
Optimistic concurrency_etag with if_match parameter
Monitor RU costx-ms-request-charge header
Auth (prod)Entra ID + Cosmos DB Built-in Data Contributor
Bad partition keyisActive, status (low cardinality = hot partitions)
Point read requiresBOTH id AND partition key value
Bursty trafficAutoscale throughput
Unit 5

Exercise — Build a Product Query Service

30 min
  1. Create Cosmos DB account and container with /categoryId as partition key
  2. Use Python SDK to insert 10 product items with different categories
  3. Perform a point read using id + partition key — observe 1 RU cost
  4. Run a same-partition query — observe 3-5 RUs
  5. Run a cross-partition query — observe 100+ RUs
  6. Implement optimistic concurrency with _etag and test a conflict scenario
Unit 6

Knowledge Check

5 min
  1. Q: You know a product's id and categoryId. Cheapest fetch? A: Point read — read_item(item=id, partition_key=categoryId) ≈ 1 RU
  2. Q: AI app has bursty traffic. Which throughput mode? A: Autoscale (scales 10%–100% of max automatically)
  3. Q: Two processes updating same item concurrently. How to prevent lost update? A: Optimistic concurrency: if_match=item["_etag"]
  4. Q: Why avoid isActive as partition key? A: Only 2 values (true/false) = 2 hot partitions = throttling under load
  5. Q: RU consumption suddenly spiked 10x. Most likely cause? A: Cross-partition queries due to missing partition key in WHERE clause
Unit 7

Summary

2 min

Choose partition keys with high cardinality aligned to your most common query pattern. Use Autoscale for AI workloads. Prefer point reads (1 RU) over queries (3–100+ RUs). Use the SDK singleton pattern. Protect concurrent writes with _etag. Monitor costs via x-ms-request-charge.

Memory Tricks

Partition key rule of 3: HAI — High cardinality, Aligned to queries, Immutable

RU cost ladder: Point read (1) → Simple query (3–5) → Complex (10+) → Cross-partition (100+)

_etag = "version stamp" for optimistic concurrency — like a database row version

Unit 8

Exam Summary Card

2 min
ScenarioAnswer
Bursty traffic throughputAutoscale (min 1,000 RU/s)
Predictable steady trafficManual (min 400 RU/s)
Cheapest fetch operationPoint read (id + partition key)
Prevent concurrent overwritesif_match with _etag
Bad partition key exampleisActive, status, region (low cardinality)
Good partition key exampleuserId, tenantId, productId (high cardinality)
Cross-partition query cost100+ RUs (fan-out to all partitions)
Production authEntra ID + Built-in Data Contributor role
Module Cheatsheet

Azure Cosmos DB for NoSQL

25–30% PDF

Key Facts

  • Hierarchy — Account → Database → Container → Item
  • Good partition key — High cardinality + query-aligned + immutable (userId, productId)
  • Bad partition key — isActive, status, region — low cardinality = hot partitions
  • Point read — ~1 RU/KB — cheapest op (needs BOTH id + partition key)
  • Cross-partition query — 100+ RU — fan-out to ALL partitions. Avoid in hot paths.
  • Autoscale throughput — 1,000 RU/s min — use for bursty AI workloads
  • Manual throughput — 400 RU/s min — use for steady, predictable workloads
  • _etag — Optimistic concurrency token — use with if_match to prevent lost updates

Commands & Patterns

from azure.cosmos import CosmosClient
from azure.identity import DefaultAzureCredential
client = CosmosClient(
  "https://myacct.documents.azure.com:443/",
  credential=DefaultAzureCredential()
)
ctr = client.get_database_client("db").get_container_client("ctr")
# Point read (~1 RU — cheapest)
item = ctr.read_item(item="id-123", partition_key="electronics")
# Upsert (no conflict error)
ctr.upsert_item(body=&#123;...&#125;)
# Optimistic concurrency
ctr.replace_item(item["id"], body=item, if_match=item["_etag"])
# Parameterized query (with pk = cheap)
list(ctr.query_items(query="SELECT * FROM c WHERE c.cat=@c",
  parameters=[&#123;"name":"@c","value":"electronics"&#125;],
  partition_key="electronics"))
Module

Implement Vector Search and Change Feed in Azure Cosmos DB

units
Unit 1

Introduction to Cosmos DB Vector Search

3 min

Azure Cosmos DB NoSQL supports native vector search — store document embeddings alongside your data in the same container. No separate vector store needed. The change feed enables event-driven architecture: process new/updated documents automatically in real time.

Exam Tip

Vector + Change Feed exam pillars: 1) Vector policy on container (flat/quantizedFlat/diskANN) 2) VectorDistance() in SQL queries 3) Change feed: all inserts + updates, no deletes, ordered per partition 4) Change feed processor with lease container 5) DiskANN = approximate (production), flat = exact (dev).
Unit 2

Vector Policy and Indexing

8 min

Configure Vector Index on Container

from azure.cosmos import CosmosClient, PartitionKey
from azure.identity import DefaultAzureCredential

client = CosmosClient(
    "https://myacct.documents.azure.com:443/",
    credential=DefaultAzureCredential()
)
db = client.get_database_client("ai-db")

# Create container with vector policy
container = db.create_container_if_not_exists(
    id="documents",
    partition_key=PartitionKey("/category"),
    vector_embedding_policy={
        "vectorEmbeddings": [{
            "path": "/embedding",
            "dataType": "float32",
            "dimensions": 1536,
            "distanceFunction": "cosine"
        }]
    },
    indexing_policy={
        "vectorIndexes": [{
            "path": "/embedding",
            "type": "diskANN"
        }]
    }
)

Exam Tip

diskANN = approximate search, production-ready. flat = exact but slow at scale. quantizedFlat = compressed for memory efficiency. Always set dimensions to match your embedding model.
Unit 4

Change Feed Processor

8 min

Process New Documents Automatically

from azure.cosmos.aio import CosmosClient
import asyncio

async def process_changes(changes, context):
    for doc in changes:
        # Auto-embed every new document inserted to container
        text = doc.get("content", "")
        embedding = await generate_embedding_async(text)
        await container.patch_item(
            item=doc["id"],
            partition_key=doc["category"],
            patch_operations=[{
                "op": "add",
                "path": "/embedding",
                "value": embedding
            }]
        )

# Start change feed processor
processor = container.create_change_feed_processor(
    name="embedding-processor",
    handler=process_changes,
    lease_container=lease_container
)
await processor.start()

Exam Tip

Change feed = all inserts + updates, no deletes. Ordered per partition key. Lease container tracks position — enables restart from last checkpoint. Pattern: new doc inserted → change feed → auto-embed → ready for vector search.
Unit 5

Summary

2 min

Cosmos DB vector search: container vector policy (diskANN) + VectorDistance() in SQL. Change feed processor: leases track position, processes all inserts/updates (not deletes), enables auto-embedding pipeline. Combine both: insert document → change feed triggers embedding → VectorDistance() query for RAG retrieval. All in one Cosmos DB container — no separate vector store.

Exam Scenario Walkthrough

High-Scale Vector RAG Architecture with Cosmos DB DiskANN & Change Feed

An AI search platform indexes 50 million product manuals. Users perform cosine vector similarity search across chunk embeddings while background workers process document updates. The system requires sub-50ms query latency without managing a separate external vector database.

Scenario Constraints & Requirements:
  • Store JSON document payloads and 1536-dimensional OpenAI embeddings in the same database.
  • Sub-50ms vector similarity lookups using approximate nearest neighbor indexing.
  • Trigger background re-indexing and translation when new documents are saved without polling.
Architecture Solution:

Configure Cosmos DB for NoSQL with container vector policy set to `diskANN`, execute `VectorDistance()` queries in SQL, and use Change Feed Processor with lease containers.

1. Define Container Vector Embedding & Indexing Policy
indexing_policy = {
    "vectorIndexes": [{"path": "/embedding", "type": "diskANN"}],
    "compositeIndexes": [/* ... */]
}
vector_embedding_policy = {
    "vectorEmbeddings": [{
        "path": "/embedding",
        "dataType": "float32",
        "distanceFunction": "cosine",
        "dimensions": 1536
    }]
}
Key Rule: DiskANN provides high accuracy with low memory footprint by compressing vectors onto SSD.
2. Execute Approximate Vector Search via SQL
SELECT TOP 10 c.id, c.title, c.summary,
       VectorDistance(c.embedding, @queryVector) AS SimilarityScore
FROM c
WHERE c.category = "manuals"
ORDER BY VectorDistance(c.embedding, @queryVector)
Key Rule: Always pass partition key in WHERE filter to avoid multi-partition cross-query RU penalties.
3. Attach Change Feed Processor with Lease Container
processor = source_container.create_change_feed_processor(
    name="auto-translation-worker",
    handler=process_new_chunks,
    lease_container=lease_container
)
await processor.start()
Key Rule: Lease container checkpointing guarantees at-least-once processing across consumer pod restarts.
Key Exam Takeaway

Change Feed in Cosmos DB guarantees insert and update order per partition key, but does NOT include deletes (deletes require soft-delete pattern).

Architectural Comparison

Vector Search: Cosmos DB (DiskANN) vs PostgreSQL (pgvector) vs AI Search

Frequently tested vector database decision matrix in Domain 2.

FeatureCosmos DB NoSQL (DiskANN)PostgreSQL (pgvector)Azure AI Search
Primary ModelGlobal NoSQL Document StoreRelational + Vector ExtensionDedicated Search Engine
Vector AlgorithmDiskANN, QuantizedFlat, FlatHNSW, IVFFlatHNSW, Exhaustive KNN
Index ScalabilityTens of millions on SSDFits best in shared RAM / bufferBillions with managed index tiers
Hybrid Search SupportVectorDistance + SQL WHEREpgvector + tsvector Full-TextNative Hybrid (BM25 + Dense Vectors) + Semantic Ranker
Reranking EngineNone (Client application)None (Client application)Built-in Microsoft Semantic Ranker
Change Feed / CDCNative Change Feed ProcessorPostgres Logical Replication / WALIndexers with change tracking
Exam Traps to Avoid

Common Mistakes & Gotchas in Cosmos DB for NoSQL

✕ The Trap: Assuming Point Reads only need the document `id`.
The Reality: A point read in Cosmos DB (`container.read_item`) strictly requires BOTH `item` (id) AND `partition_key`. Knowing only `id` forces a multi-partition query (expensive).
✓ Correct Answer Rule: Always pass `partition_key` alongside `item` for 1-RU point reads.
✕ The Trap: Expecting Change Feed to capture `DELETE` operations.
The Reality: Cosmos DB change feed default mode only emits document creates and updates. Deleted items vanish from feed.
✓ Correct Answer Rule: Use soft deletes (`isDeleted: true` with TTL expiration) if downstream workers must process deletions.
✕ The Trap: Selecting `Strong` consistency across multi-region write databases.
The Reality: Strong consistency cannot be configured for multi-region writes (bounded staleness or session is standard).
✓ Correct Answer Rule: Understand consistency level trade-offs: Strong (highest cost/latency) vs Session (default, read-your-own-writes).

Recommended Next Steps for Cosmos DB for NoSQL

Quick Quiz

5 questions — test your understanding before moving on

Finished reading this module? Mark it complete to track your progress.

Related Modules — Develop AI Solutions by Using Azure Data Management Services

Frequently Asked Questions — Cosmos DB for NoSQL

How do you enable vector indexing (DiskANN) in Azure Cosmos DB for NoSQL? +

You must define the `vectorEmbeddings` policy and `indexingPolicy.vectorIndexes` with type `diskANN` at container creation time. The vector policy is immutable once the container is created. Queries then use the `VectorDistance(c.embedding, @queryVector)` function.

When should you choose Autoscale throughput over manual RU/s provisioning? +

Choose Autoscale for unpredictable or bursty workloads like AI search queries. Autoscale automatically scales throughput between 10% and 100% of your defined maximum (e.g., 400 to 4,000 RU/s), charging only for the highest RU/s used in each hour.

Why are cross-partition queries expensive in vector search, and how do you optimize them? +

A cross-partition query fans out to every physical partition, multiplying RU consumption and latency. To optimize, include the container partition key (e.g., `tenantId`, `userId`, or `category`) in the `WHERE` clause alongside `VectorDistance()`.

How does the Cosmos DB Change Feed support asynchronous embedding generation? +

The Change Feed acts as a persistent log of document updates. An Azure Function or Container App listens to the Change Feed, extracts newly inserted text, calls an embedding model (e.g., Azure OpenAI), and updates the document with the generated vector.

What is the difference between Flat, QuantizedFlat, and DiskANN vector indexes in Cosmos DB? +

Flat performs exact kNN brute-force search (high RU for large datasets). QuantizedFlat compresses vectors for smaller memory footprint. DiskANN builds an approximate nearest neighbor graph on NVMe SSDs, delivering high recall and fast queries across millions of vectors.