Azure · lesson 12 of 19
Beginner
4Intermediate
13- 5 Azure Virtual Network Basics
- 6 Azure SQL Basics
- 7 Azure Functions Fundamentals
- 8 Bicep and ARM Templates Basics
- 9 Azure Monitor Basics
- 11 Azure Cosmos DB: Items and Partition Keys
- 12 Azure Cosmos DB: Queries
- 13 Azure Cosmos DB: ETags and Patch
- 14 Azure Service Bus: Queues and Settlement
- 15 Azure Service Bus: Dead-Lettering
- 16 Azure Service Bus: Topics and Filters
- 17 Azure Key Vault: Secrets and Versions
- 18 Azure Key Vault: Soft-Delete and Recovery
Also part of Azure
5- + Dead-Letter Queues: What Happens When Retries Keep Failing from Event-Driven Architecture
- + Idempotent Consumers: Surviving a Duplicate Delivery from Event-Driven Architecture
- + Envelope Encryption and Key Rotation Without Re-encrypting Data from Cryptography
- + Deployments and Rollouts from Kubernetes
- + Services and Networking from Kubernetes
Azure Cosmos DB: Queries
Run a parameterised SQL query and a cross-partition aggregate against Cosmos DB's vNext emulator with the Python SDK, and find enable_cross_partition_query is never enforced client-side — it's just a request option the server acts on.
By SkillByExample · Published
Cosmos DB’s query language looks like a subset of SQL, but it runs against JSON documents, not rows with a fixed schema — and on a real multi-partition account, a query that isn’t scoped to one partition key has to fan out across every physical partition to find every match. This lesson runs against the same vNext emulator image as the previous lesson, which — as the first Practice task below shows — only ever has one physical partition, so the fan-out itself isn’t something this emulator can demonstrate directly.
A Parameterised Filter and a Cross-Partition Aggregate
from azure.cosmos import CosmosClient, PartitionKey
ENDPOINT = "http://localhost:8081"
KEY = "C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw=="
client = CosmosClient(ENDPOINT, credential=KEY)
db = client.create_database_if_not_exists("storedb")
container = db.create_container_if_not_exists(
id="orders", partition_key=PartitionKey(path="/customer")
)
for order in [
{"id": "o0", "customer": "alice", "total": 30},
{"id": "o1", "customer": "bob", "total": 12},
{"id": "o2", "customer": "alice", "total": 55},
]:
container.upsert_item(order)
filtered = list(container.query_items(
query="SELECT c.id, c.total FROM c WHERE c.total > @min",
parameters=[{"name": "@min", "value": 20}],
enable_cross_partition_query=True,
))
print("orders over 20:", filtered)
total = list(container.query_items(
query="SELECT VALUE SUM(c.total) FROM c",
enable_cross_partition_query=True,
))
print("sum of all totals:", total)
$ docker run -d --name az-cosmos-query-demo -e PROTOCOL=http mcr.microsoft.com/cosmosdb/linux/azure-cosmos-emulator:vnext-EN20261019
7a930d7ca148aabf7ea628997ee694a36ea740fef94031896a090376653f37f1
$ until docker logs az-cosmos-query-demo 2>&1 | grep -q 'System is now fully ready to accept requests'; do sleep 0.5; done
$ docker run --rm --network container:az-cosmos-query-demo -v "$(pwd)":/app -w /app python:3.14-slim bash -c "pip install -q azure-cosmos==4.17.1 2>&1 | tail -1 && python cosmos_query.py"
[notice] To update, run: pip install --upgrade pip
orders over 20: [{'id': 'o0', 'total': 30}, {'id': 'o2', 'total': 55}]
sum of all totals: [97]
SELECT VALUE SUM(...) returns [97] — a one-element list holding the raw number, not
[{"$1": 97}] the way a plain SELECT SUM(c.total) FROM c would. Both queries ran across all
three orders spanning two different partition key values (alice, bob); enable_cross_partition_query=True
documents that on purpose, but — as Practice 1 below shows — the SDK never checks for it
locally; it’s purely a request option the server is free to act on. Pass it anyway: it’s the
one line in the call that tells the next reader “yes, this fans out to every partition on
purpose,” which the query text alone doesn’t say.
Practice
1. Predict what happens if you drop enable_cross_partition_query entirely, then run it and check.
from azure.cosmos import CosmosClient
ENDPOINT = "http://localhost:8081"
KEY = "C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw=="
client = CosmosClient(ENDPOINT, credential=KEY)
container = client.get_database_client("storedb").get_container_client("orders")
result = list(container.query_items(query="SELECT c.id FROM c"))
print("no enable_cross_partition_query at all:", result)
# the SDK's low-level client exposes the physical partition key ranges directly
ranges = list(container.client_connection._ReadPartitionKeyRanges(container.container_link))
print("physical partition key ranges on this emulator:", len(ranges))
$ docker run --rm --network container:az-cosmos-query-demo -v "$(pwd)":/app -w /app python:3.14-slim bash -c "pip install -q azure-cosmos==4.17.1 2>&1 | tail -1 && python cosmos_query_no_cross.py"
[notice] To update, run: pip install --upgrade pip
no enable_cross_partition_query at all: [{'id': 'o0'}, {'id': 'o1'}, {'id': 'o2'}]
physical partition key ranges on this emulator: 1
All three orders came back whether or not enable_cross_partition_query was passed — the SDK’s
own source shows why: the flag is only ever copied into a request option, never checked against
the query locally. The partition key range count confirms the other half of it: this container
has exactly one physical partition, no matter how many distinct partition key values it holds,
so there’s nothing for a query to fan out across on this emulator in the first place.
2. Count orders per customer with GROUP BY instead of fetching every item and counting in Python.
from azure.cosmos import CosmosClient
ENDPOINT = "http://localhost:8081"
KEY = "C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw=="
client = CosmosClient(ENDPOINT, credential=KEY)
container = client.get_database_client("storedb").get_container_client("orders")
counts = list(container.query_items(
query="SELECT c.customer, COUNT(1) AS orders FROM c GROUP BY c.customer",
enable_cross_partition_query=True,
))
print("orders per customer:", sorted(counts, key=lambda r: r["customer"]))
$ docker run --rm --network container:az-cosmos-query-demo -v "$(pwd)":/app -w /app python:3.14-slim bash -c "pip install -q azure-cosmos==4.17.1 2>&1 | tail -1 && python cosmos_query_groupby.py"
[notice] To update, run: pip install --upgrade pip
orders per customer: [{'customer': 'alice', 'orders': 2}, {'customer': 'bob', 'orders': 1}]
alice has two orders in this data set, bob has one — GROUP BY does the counting inside
the query engine, the same reason it’s preferable over fetching every item cross-partition just
to count them in application code.
3. Run a query scoped to one partition key and confirm it works without enable_cross_partition_query.
from azure.cosmos import CosmosClient, PartitionKey
ENDPOINT = "http://localhost:8081"
KEY = "C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw=="
client = CosmosClient(ENDPOINT, credential=KEY)
container = client.get_database_client("storedb").get_container_client("orders")
alice_orders = list(container.query_items(
query="SELECT c.id, c.total FROM c WHERE c.customer = @who",
parameters=[{"name": "@who", "value": "alice"}],
partition_key="alice",
))
print("alice's orders (single-partition query):", alice_orders)
$ docker run --rm --network container:az-cosmos-query-demo -v "$(pwd)":/app -w /app python:3.14-slim bash -c "pip install -q azure-cosmos==4.17.1 2>&1 | tail -1 && python cosmos_query_single_partition.py"
[notice] To update, run: pip install --upgrade pip
alice's orders (single-partition query): [{'id': 'o0', 'total': 30}, {'id': 'o2', 'total': 55}]
Passing partition_key="alice" instead of enable_cross_partition_query=True is what actually
scopes this to one partition — the WHERE c.customer = @who clause is redundant for that
purpose, since partition_key= alone routes the request. The WHERE clause is still doing
real work here, just a different job: filtering within that one partition, the same way it
would inside any query.
Cleanup
$ docker rm -f az-cosmos-query-demo
az-cosmos-query-demo
Next: conditional writes with ETags and atomic patch operations.