#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
#55 — Rubric Gap Analysis —
query-use-projectionsenhancement: Add DTO/result-type matching guidancequery-use-projectionsenhancement,SCOPE,agent-kit,rule:querySummary
The
query-use-projectionsrule 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 toSELECT *.Rubric Gap Analysis
During rubric criteria review for query optimization, this was identified as a missing "completeness" check. The existing rule shows
OrderSummaryas 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:
But does not show or warn against the anti-pattern.
Missing Anti-Pattern
Correct Pattern
Recommended Enhancement
Add to
query-use-projectionsafter the existing projection examples:References
query-optimization.md, Criterion 2, check 2