Skip to content

Rubric Gap Analysis — query-use-projections enhancement: Add DTO/result-type matching guidance #55

@jaydestro

Description

@jaydestro

#55 — Rubric Gap Analysis — query-use-projections enhancement: Add DTO/result-type matching guidance

Field Value
Type Rule Enhancement
Target Rule query-use-projections
Severity MEDIUM
Source SCOPE Rubric Criteria — Query Optimization, Criterion 2, check 2
Labels enhancement, SCOPE, agent-kit, rule:query

Summary

The query-use-projections rule demonstrates explicit field projections and LINQ projection but does not address the anti-pattern of deserializing projected query results into the full document model class. When a query projects 4 fields but deserializes into a 20-field model class, the bandwidth savings from projection are preserved but the application still allocates and hydrates unused properties, creating a false sense of optimization and encouraging eventual drift back to SELECT *.

Rubric Gap Analysis

During rubric criteria review for query optimization, this was identified as a missing "completeness" check. The existing rule shows OrderSummary as a projected DTO type in one example, but does not explicitly state the principle or show the anti-pattern of using the full model class with projected results. Agents frequently project fields in the query but deserialize into the parent entity type, partially negating the optimization.

Evidence

Existing Rule Coverage

The rule shows a correct DTO example:

public class OrderSummary  // Dedicated DTO with only projected fields
{
    public string Id { get; set; }
    public DateTime OrderDate { get; set; }
    public decimal Total { get; set; }
    public string Status { get; set; }
}

But does not show or warn against the anti-pattern.

Missing Anti-Pattern

// ❌ Anti-pattern: Projected query with full model deserialization
var query = "SELECT c.id, c.orderDate, c.total, c.status FROM c WHERE c.customerId = @cid";

// Query projects 4 fields — good!
// But deserializes into the full Order class with 20+ properties
var iterator = container.GetItemQueryIterator<Order>(  // ❌ Full Order model
    new QueryDefinition(query).WithParameter("@cid", customerId));

var response = await iterator.ReadNextAsync();
foreach (var order in response)
{
    // order.CustomerName == null (not projected)
    // order.ShippingAddress == null (not projected)
    // order.Items == null (not projected)
    // 16 unused properties allocated and set to default values
    // Developer eventually thinks "these nulls are confusing, let me just use SELECT *"
}
// ❌ Anti-pattern in Spring Data Cosmos
@Query("SELECT c.id, c.playerName, c.score FROM c WHERE c.leaderboardKey = @key")
List<Player> getTopPlayers(@Param("key") String key);
// Deserializes into full Player entity — avatar, email, settings all null
// Encourages drift back to SELECT * to avoid "confusing nulls"

Correct Pattern

// ✅ Dedicated DTO matches projected fields exactly
public class OrderSummary
{
    public string Id { get; set; }
    public DateTime OrderDate { get; set; }
    public decimal Total { get; set; }
    public string Status { get; set; }
    // No unused properties — clean, intentional, and self-documenting
}

var iterator = container.GetItemQueryIterator<OrderSummary>(  // ✅ Matches projection
    new QueryDefinition(query).WithParameter("@cid", customerId));
// ✅ Dedicated projection class
public record PlayerSummary(String id, String playerName, int score) {}

@Query("SELECT c.id, c.playerName, c.score FROM c WHERE c.leaderboardKey = @key")
List<PlayerSummary> getTopPlayers(@Param("key") String key);
// No unused fields, no confusing nulls, no drift back to SELECT *

Recommended Enhancement

Add to query-use-projections after the existing projection examples:

Define a dedicated DTO or result type that matches the projected fields.

Projecting fields in the query but deserializing into the full document model class undermines the optimization:

  • Unused properties are allocated with default/null values
  • Confusing null fields encourage developers to revert to SELECT *
  • The intent of the projection is invisible in the code

Create a dedicated result class (DTO, record, or anonymous type) with only the projected fields. This makes the projection self-documenting and prevents drift.

References

Metadata

Metadata

Labels

SCOPEIssues generated by SCOPE toolagent-kitIssues requiring updates to cosmosdb-best-practices Agent Kit rulesenhancementNew 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