Skip to content

query-use-projections: refine RU-impact framing and add Node.js example #116

@jaydestro

Description

@jaydestro

#1 — Rubric Gap Analysis — query-use-projections enhancement: Refine RU-impact framing and add Node.js example

Field Value
Type Rule Enhancement
Target Rule query-use-projections
Severity HIGH
Source SCOPE Node.js AI Food Delivery — Query Optimization, Criterion q_2_projections, Phase 3 static + Phase 4 runtime + Phase 6a validation
Labels enhancement, SCOPE, agent-kit, documentation-gap, rule:query

Summary

The query-use-projections rule's "reduces RU and network by 30-80%" headline is correct only for multi-KB / array-heavy / embedding-heavy documents. On small flat documents the RU delta is ~0%. The overstated claim lets agents deprioritize the fix after local-emulator tests on small seed data. Additionally, the rule ships C# and Java/Spring examples but no Node.js (@azure/cosmos v4), which is the SDK where SCOPE measures the worst hit rate on this rule.

Rubric Gap Analysis

Discovered while evaluating 65 Node.js runs against the cosmosdb-best-practices skill.

Cohort Rule hits Rule misses Hit rate
AK (skill installed, 25 runs) 4 21 16%
Control (40 runs) 0 40 0%
All 4 61 6%

The skill provides a +16 pp uplift but does not land in 84% of AK runs. Both cohorts ship SELECT * on partition-scoped parameterized queries. Runtime probes (Phase 4) revealed why agents ignore the rule: on the representative seed data, switching to a projection saves no RU.

Evidence

Existing Rule Coverage

Current front-matter and in-body phrasing:

---
title: Project Only Needed Fields
impact: HIGH
impactDescription: reduces RU and network by 30-80%
tags: query, projection, performance, bandwidth
---
// 70% less data transferred, proportionally lower RU

No Node.js example. All worked examples are C# (GetItemQueryIterator, LINQ projection) or Java (@Query + record DTO).

Missing Anti-Pattern (Node.js / TypeScript — representative of 61/65 runs)

// control run — generic Node.js, @azure/cosmos v4
const ordersQuery = {
  query: 'SELECT * FROM orders o WHERE o.userId = @userId ORDER BY o.createdAt DESC OFFSET 0 LIMIT @limit',
  parameters: [
    { name: '@userId', value: userId },
    { name: '@limit', value: limit },
  ],
};
const { resources } = await ordersContainer.items.query(ordersQuery, { partitionKey: userId }).fetchAll();
// agent-kit run — TypeScript, @azure/cosmos v4
const { resources } = await this.container.items.query<OrderDocument>({
  query: 'SELECT * FROM c WHERE c.userId = @userId ORDER BY c.createdAt DESC',
  parameters: [{ name: '@userId', value: userId }],
}, { partitionKey: userId }).fetchAll();
// agent-kit run — events container
const eventsQuery = {
  query: 'SELECT * FROM c WHERE c.orderId = @orderId ORDER BY c.timestamp DESC',
  parameters: [{ name: '@orderId', value: orderId }],
};

In all three, the caller uses 4–5 fields despite selecting *.

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

// ❌ Anti-pattern: SELECT * pulls every field including future additions
const bad = {
  query: 'SELECT * FROM c WHERE c.userId = @userId ORDER BY c.createdAt DESC',
  parameters: [{ name: '@userId', value: userId }],
};

// ✅ Preferred: project only the fields the caller consumes
const good = {
  query: `
    SELECT c.id, c.userId, c.status, c.total, c.createdAt
    FROM c
    WHERE c.userId = @userId
    ORDER BY c.createdAt DESC
  `,
  parameters: [{ name: '@userId', value: userId }],
};

// TypeScript: dedicated result type matches the projected fields
interface OrderSummary {
  id: string;
  userId: string;
  status: string;
  total: number;
  createdAt: string;
}
const { resources } = await container.items
  .query<OrderSummary>(good, { partitionKey: userId })
  .fetchAll();

// Single-column scalar with SELECT VALUE
const statuses = await container.items
  .query<string>({
    query: 'SELECT VALUE c.status FROM c WHERE c.userId = @u',
    parameters: [{ name: '@u', value: userId }],
  }, { partitionKey: userId })
  .fetchAll();

Proposed front-matter change

 ---
 title: Project Only Needed Fields
 impact: HIGH
-impactDescription: reduces RU and network by 30-80%
+impactDescription: reduces payload size, network bandwidth, and client memory;
+  RU savings scale with document size (negligible on small flat docs,
+  substantial on multi-KB/MB documents and large result sets)
 tags: query, projection, performance, bandwidth
 ---
-// 70% less data transferred, proportionally lower RU
+// Substantial payload-size reduction; RU savings depend on document size
+// (significant on large/nested docs, negligible on small flat docs)

Runtime Validation

Cosmos DB emulator, same filter WHERE c.userId = @u ORDER BY c.createdAt DESC, 10 docs returned per query, N=5 repeats per probe (2026-04-17).

Doc class Size SELECT * RU Projection RU RU Δ Payload Δ
Small flat (id, userId, status, total, createdAt, restaurantId) ~350 B 3.07 3.13 −1.95% 66%
Medium (20-item array + notes) ~5 KB 3.74 3.26 +12.83% 97%
Large (50-item array + grounding blob + 1536-dim embedding) ~30 KB 10.96 3.81 +65.24% 99.79%

The existing rule's "30-80%" band holds for large docs (65.24%). The framing correction is required because the small-doc case is where agents self-test and conclude the rule doesn't apply.

Rule Contradiction Scan

Scanned against all 91 rules + five sibling Phase 5 drafts from this eval.

Rule Interaction
query-point-reads Complementary — point reads supersede projections when id is known
query-avoid-cross-partition Independent axis
query-parameterize / query-order-filters / query-avoid-scans Unrelated
model-nesting-depth Cross-link — nested payloads amplify the projection benefit
model-embed-related Reinforcing — embedding produces larger docs; projections become more important when you embed
monitoring-ru-consumption Complementary — requestCharge is how you verify on your own data shape
index-exclude-unused Separate axis

No contradictions.

Documentation Cross-reference

Recommended cosmosdb-agent-kit Fix

  1. Update front-matter impactDescription as diffed above.
  2. Soften the in-body "70% less data transferred, proportionally lower RU" comment.
  3. Add a Node.js/TypeScript example block matching the other-language examples already present.
  4. Optional: add a one-line cross-link at the bottom of model-embed-related pointing to this rule ("Embedded docs grow; pair this with query-use-projections").

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