Skip to content

query-point-reads: add Node.js / @azure/cosmos v4 example #118

@jaydestro

Description

@jaydestro

#3 — Rubric Gap Analysis — query-point-reads enhancement: Add Node.js example + 3× RU runtime evidence

Field Value
Type Rule Enhancement
Target Rule query-point-reads
Severity MEDIUM
Source SCOPE Node.js AI Food Delivery — Query Optimization, Phase 3 static + Phase 6a validation (2026-04-17)
Labels SCOPE, enhancement, rule:query
Related Closed #48 (rule created, C#/Python/Java examples added)

Summary

query-point-reads ships with C#, Python, and Java examples but no Node.js / @azure/cosmos v4 example. Across 65 Node.js SCOPE runs, 45/65 already adopt .item(id, pk).read() somewhere, but 3/65 runs ship SELECT * FROM c WHERE c.id = @id handlers with zero point reads in the codebase — the exact anti-pattern the rule warns against. Runtime measurement confirms the rule's "1 RU vs ~2.5 RU" claim: point read is 2.92× cheaper on identical data. Adding a Node.js snippet brings the rule to parity with the three SDKs it already covers.

Rubric Gap Analysis

Measured 2026-04-17 across all 65 run directories (.js/.ts/.mjs/.cjs; node_modules and test files excluded):

Metric Runs % of 65
Uses container.item(id, pk).read() at least once 45 69%
Uses SELECT ... WHERE c.id = @id at least once 4 6%
Uses query-by-id AND has zero point reads (pure anti-pattern) 3 5%

The 3 violators are: P07/R01, P07/R04 (GPT-5.4 no-extensions, control), P10/R03 (GPT-5.3 Codex + AK). Sample from P07/R01/orderService.ts:67:

export async function findOrderById(orderId: string): Promise<OrderDocument | null> {
  const { resources } = await ordersContainer.items
    .query<OrderDocument>({
      query: 'SELECT TOP 1 * FROM c WHERE c.id = @orderId',
      parameters: [{ name: '@orderId', value: orderId }]
    })
    .fetchAll();
  return resources[0] ?? null;
}

The gap this issue targets is parity of rule coverage, not widespread miss. Node.js already sits at 69% adoption without a Node example in the rule — adding the example should both push that number higher and give the 3 violators (and any future agents) a canonical snippet to copy.

Evidence

Existing Rule Coverage

Verified against AzureCosmosDB/cosmosdb-agent-kit@main (2026-04-17) — skills/cosmosdb-best-practices/rules/query-point-reads.md contains:

Language Code blocks
C# 5
Java 1
Python 3
Node.js / TypeScript 0

No @azure/cosmos mentions anywhere in the rule body.

// existing C# correct example
var response = await container.ReadItemAsync<Order>(orderId, new PartitionKey(customerId));
return response.Resource;

Missing Anti-Pattern (Node.js — representative)

// control run
async function getOrderById(orderId, userId) {
  const { resources } = await ordersContainer.items.query({
    query: 'SELECT * FROM c WHERE c.id = @id',
    parameters: [{ name: '@id', value: orderId }],
  }, { partitionKey: userId }).fetchAll();
  return resources[0] ?? null;   // 2.92 RU
}
// agent-kit run, worse — no partitionKey option, cross-partition
async getOrder(orderId: string, userId: string): Promise<Order | null> {
  const iterator = this.container.items.query<Order>({
    query: 'SELECT * FROM c WHERE c.id = @id AND c.userId = @u',
    parameters: [
      { name: '@id', value: orderId },
      { name: '@u', value: userId },
    ],
  });
  const { resources } = await iterator.fetchAll();
  return resources[0] ?? null;
}

Correct Pattern (proposed Node.js block to add to the rule)

// ❌ Anti-pattern: SELECT * WHERE c.id = @id with known id + partition key
const iterator = container.items.query<Order>({
  query: 'SELECT * FROM c WHERE c.id = @id',
  parameters: [{ name: '@id', value: orderId }],
}, { partitionKey: userId });
const { resources } = await iterator.fetchAll();
return resources[0] ?? null;
// ~2.92 RU, ~15 ms — goes through the query engine for a single known doc

// ✅ Preferred: point read bypasses the query engine
const response = await container.item(orderId, userId).read<Order>();
return response.resource ?? null;
// 1.00 RU, ~11 ms

Multiple known (id, partitionKey) pairs (Node.js note)

// ❌ OR/IN across partitions — fans out to every partition
const { resources } = await container.items.query<Order>({
  query: 'SELECT * FROM c WHERE c.id IN (@a, @b, @c)',
  parameters: [
    { name: '@a', value: 'order-1' },
    { name: '@b', value: 'order-2' },
    { name: '@c', value: 'order-3' },
  ],
}).fetchAll();

// ✅ Parallel point reads (no readMany equivalent in @azure/cosmos v4)
const results = await Promise.all([
  container.item('order-1', 'user-alice').read<Order>(),
  container.item('order-2', 'user-bob').read<Order>(),
  container.item('order-3', 'user-alice').read<Order>(),
]);
return results.map(r => r.resource).filter(Boolean);
// Total RU ≈ N × 1.0; bound concurrency with a limiter for larger batches.

Note for the rule: @azure/cosmos v4 does not expose a readMany equivalent at the time of writing. Recommend bounded-concurrency parallel point reads for batched lookups.

Runtime Validation

Ran _agent_files/phase-6a/scope-node-003-validation.js against the Cosmos DB emulator (@azure/cosmos v4.5.1, 2026-04-17). 30 orders (~350 B each), partition key /userId, N=10 repeats per variant, target picked deterministically:

Operation avg RU ratio vs point read
container.item(orderId, userId).read() 1.00 1.00×
SELECT * FROM c WHERE c.id = @id with { partitionKey: userId } 2.92 2.92×
SELECT * FROM c WHERE c.id = @id without partitionKey (cross-partition) 2.82 2.82×
SELECT TOP 1 * FROM c WHERE c.id = @id (the exact anti-pattern from P07/R01) 2.82 2.82×

The TOP 1 doesn't save anything — the query engine still does the same routing work and still costs ~3× RU. Matches the rule's existing "1 RU vs ~2.5 RU" headline.

Raw output: _agent_files/phase-6a/scope-node-003-validation.json.

Rule Contradiction Scan

Rule Interaction
query-use-projections (#1 draft) Complementary — point reads supersede projections for id-equality
query-avoid-cross-partition Reinforcing — the TypeScript anti-pattern above violates this too (no partitionKey option)
partition-query-patterns Complementary
monitoring-ru-consumption (#2 draft) Reinforcing — requestCharge is how the 3× delta becomes visible

No contradictions.

Documentation Cross-reference

Recommended cosmosdb-agent-kit Fix

  1. Add a Node.js / TypeScript anti-pattern + correct-pattern block matching the existing C#/Python/Java structure.
  2. Add the batch-lookup note (no readMany in the Node.js SDK v4; recommend bounded parallel reads).
  3. Cross-link to query-avoid-cross-partition for the partition-key-option reminder.

Metadata

Metadata

Assignees

Labels

SCOPEIssues generated by SCOPE toolenhancementNew feature or requestrule:queryCosmos DB query rule enhancement

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions