Skip to content

Conversation

@ymc9
Copy link
Member

@ymc9 ymc9 commented Aug 13, 2025

Summary by CodeRabbit

  • New Features

    • Computed fields can now be used in filters, sorting, and aggregations.
    • Added support for additional aggregations: average (_avg), minimum (_min), and maximum (_max).
    • Improved reliability of group-by and count operations across databases.
  • Refactor

    • Centralized field reference handling to ensure consistent behavior across dialects and queries.
  • Tests

    • Added comprehensive tests for computed fields (filtering, ordering, aggregations, and grouping).

Copilot AI review requested due to automatic review settings August 13, 2025 03:25
@coderabbitai
Copy link

coderabbitai bot commented Aug 13, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Centralizes field reference construction by introducing BaseCrudDialect.fieldRef and replacing buildFieldRef usages across dialects and operations. Refactors group-by to use expressionBuilder and a unified aggregate helper. Expands aggregate operators in query-utils. Adds tests validating computed fields in filtering, ordering, and aggregations.

Changes

Cohort / File(s) Summary
Core dialect abstraction
packages/runtime/src/client/crud/dialects/base.ts
Adds BaseCrudDialect.fieldRef and routes filter, order-by, and selection paths through it; adopts expressionBuilder for non-relational refs.
SQL dialects
.../dialects/postgresql.ts, .../dialects/sqlite.ts
Removes buildFieldRef imports; replaces with this.fieldRef for relation/non-relation field projections and payload selections.
CRUD operations (general)
packages/runtime/src/client/crud/operations/base.ts
Replaces buildFieldRef with this.dialect.fieldRef in distinct emulation and update (increment and scalar list) paths.
CRUD operations (group-by)
packages/runtime/src/client/crud/operations/group-by.ts
Switches to expressionBuilder; introduces local fieldRef for subquery fields; consolidates aggregations via aggregate helper; replaces sql.ref usages.
Query utilities
packages/runtime/src/client/query-utils.ts
Extends aggregate to support _avg, _min, _max; refactors signature and pattern matching with exhaustive handling.
Tests
packages/runtime/test/client-api/computed-fields.test.ts
Adds tests for computed field (upperName) in where, orderBy, aggregate, and groupBy scenarios.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • fix(delegate): filter fixes #112: Also refactors BaseCrudDialect field reference handling, overlapping with this PR’s centralization of fieldRef.
  • implementing groupBy #8: Touches buildOrderBy paths that this PR updates to use fieldRef and expressionBuilder.
  • merge dev to main #154: Modifies runtime query-building and query-utils.aggregate, intersecting with this PR’s aggregate expansion and fieldRef adoption.

Poem

I twitch my nose at fields that hide,
A single ref now guides the tide.
With eb in paw, I hop through rows,
Counting carrots, maxing those.
Upper names gleam—ALEX bright—
Grouped and ordered, nibble-right.
Thump! The queries all take flight.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/computed-fields-agg

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@claude
Copy link

claude bot commented Aug 13, 2025

Claude finished @ymc9's task —— View job


Pull Request Review Complete ✅

Reviewing PR #155: "fix: using computed fields for orderBy and aggregations"

Todo List

  • Read and analyze modified files to understand the changes
  • Review code quality and best practices
  • Check for potential bugs or issues
  • Evaluate performance considerations
  • Assess security concerns
  • Review test coverage
  • Provide comprehensive feedback

Summary

This PR fixes the usage of computed fields in orderBy and aggregation operations by consistently using the fieldRef method from the dialect, which properly handles computed fields by evaluating their expressions rather than treating them as simple column references.

🟢 Positive Aspects

Code Quality & Architecture

  1. Excellent consistency improvement: The changes centralize field reference handling through this.dialect.fieldRef() across all dialects and operations
  2. Clean abstraction: Moving the fieldRef method to the base dialect class provides a unified interface
  3. Proper separation of concerns: Computed field logic is properly encapsulated in the buildFieldRef utility

Bug Fix Quality

  1. Comprehensive fix: The PR addresses computed fields in multiple contexts:
    • orderBy operations
    • Aggregations (_count, _sum, _avg, _min, _max)
    • groupBy operations
    • Field selections
  2. Consistent implementation: All dialects (base, PostgreSQL, SQLite) are updated uniformly

Test Coverage

  1. Solid test additions: The test file now includes comprehensive coverage:
    • Order by computed fields (lines 48-70)
    • Aggregations with computed fields (lines 72-77)
    • Group by with computed fields (lines 79-91)

🟡 Areas for Improvement

Minor Code Issues

  1. Import optimization needed: packages/runtime/src/client/crud/operations/base.ts:32

    • The buildFieldRef import is removed but not used anywhere else - good cleanup
    • However, line 32 in the import list could be cleaned up
  2. Type safety in aggregations: packages/runtime/src/client/crud/dialects/base.ts:728

    • The cast to AGGREGATE_OPERATORS could be more type-safe, but this is existing code

Documentation & Comments

  1. Missing TODO resolution: packages/runtime/src/client/crud/dialects/base.ts:867
    • The comment "TODO: computed field from delegate base?" suggests this case might not be fully handled
    • Consider adding tests for computed fields in delegate/inheritance scenarios

🟢 Performance Considerations

  • No performance regressions: The changes maintain the same query structure
  • Potential improvement: Computed fields are evaluated at query time, which is correct behavior

🟢 Security Assessment

  • No security concerns: The changes don't introduce any new attack vectors
  • SQL injection safety: Uses parameterized queries and expression builders appropriately

🔍 Specific Code Review

packages/runtime/src/client/crud/dialects/base.ts

Lines 725-731: ✅ Good fix for aggregation orderBy

// Before: sql.ref(`${modelAlias}.${k}`)
// After: this.fieldRef(model, k, eb, modelAlias)

This correctly handles computed fields in aggregation-based ordering.

Lines 755-758: ✅ Excellent improvement for regular field ordering

const fieldRef = this.fieldRef(model, field, expressionBuilder(), modelAlias);

Consistent with the aggregation fix and handles both computed and regular fields.

packages/runtime/src/client/crud/operations/group-by.ts

Lines 47-51: ✅ Clean refactoring

const fieldRef = (field: string) => this.dialect.fieldRef(this.model, field, expressionBuilder(), '$sub');
query = query.groupBy(bys.map((by) => fieldRef(by)));

Good use of a helper function for readability.

Line 97: ✅ Using the new aggregate utility function improves consistency

Test Coverage Analysis

The test additions in computed-fields.test.ts are comprehensive and cover the key scenarios:

  • ✅ Filtering by computed fields
  • ✅ Ordering by computed fields
  • ✅ Aggregations on computed fields
  • ✅ GroupBy operations with computed fields

📝 Minor Suggestions

  1. Consider edge case testing: Add tests for computed fields in delegate inheritance scenarios
  2. Error handling: Consider what happens if a computed field implementation is missing during aggregation
  3. Documentation: The PR would benefit from updating any relevant documentation about computed field limitations

🎯 Overall Assessment

This is a high-quality bug fix that:

  • ✅ Addresses a real functional gap in computed field support
  • ✅ Implements the fix consistently across the codebase
  • ✅ Includes appropriate test coverage
  • ✅ Maintains code quality and patterns
  • ✅ Has no security or performance concerns

Recommendation: ✅ APPROVE

The changes are well-implemented, thoroughly tested, and solve the stated problem effectively. The code maintains high quality standards and follows established patterns in the codebase.


Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR fixes support for using computed fields in orderBy clauses and aggregation operations by replacing direct SQL field references with a new fieldRef method that properly handles both regular and computed fields.

  • Introduces a fieldRef method in the base dialect class that delegates to the existing buildFieldRef utility
  • Updates orderBy operations to use computed field expressions instead of raw SQL references
  • Modifies aggregation operations in group-by queries to properly handle computed fields

Reviewed Changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
packages/runtime/test/client-api/computed-fields.test.ts Adds comprehensive test cases for orderBy and aggregation operations with computed fields
packages/runtime/src/client/query-utils.ts Minor formatting change to the aggregate function signature
packages/runtime/src/client/crud/operations/group-by.ts Updates group-by operations to use fieldRef method for proper computed field handling
packages/runtime/src/client/crud/operations/base.ts Replaces buildFieldRef calls with dialect.fieldRef method calls
packages/runtime/src/client/crud/dialects/sqlite.ts Updates SQLite dialect to use fieldRef method instead of buildFieldRef
packages/runtime/src/client/crud/dialects/postgresql.ts Updates PostgreSQL dialect to use fieldRef method instead of buildFieldRef
packages/runtime/src/client/crud/dialects/base.ts Introduces the fieldRef method and updates orderBy logic to handle computed fields

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
You can also share your feedback on Copilot code review for a chance to win a $100 gift card. Take the survey.

@ymc9
Copy link
Member Author

ymc9 commented Aug 13, 2025

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Aug 13, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🔭 Outside diff range comments (1)
packages/runtime/src/client/crud/operations/base.ts (1)

1268-1278: Ensure computed fields cannot be updated via any path
The validator and CRUD layer currently do not reject updates to computed fields. This allows invalid SQL to be generated (e.g. dialect.fieldRef for a computed field). We need to block all update mutations against computed columns as early as possible.

Please add a guard in processScalarFieldUpdateData (in packages/runtime/src/client/crud/operations/base.ts) before any transform:

 private processScalarFieldUpdateData(model: GetModels<Schema>, field: string, data: any): any {
     const fieldDef = this.requireField(model, field);

+    // ✋ Disallow updating computed columns
+    if (fieldDef.computed) {
+      throw new QueryError(`Field "${field}" is computed and cannot be updated`);
+    }
 
     if (this.isNumericIncrementalUpdate(fieldDef, data[field])) {
         return this.transformIncrementalUpdate(model, field, fieldDef, data[field]);

Additionally, exclude computed fields from the Zod update schema in makeUpdateDataSchema (packages/runtime/src/client/crud/validator.ts) by adding at the top of the loop:

         Object.keys(modelDef.fields).forEach((field) => {
-            if (withoutFields.includes(field)) return;
+            const fieldDef = requireField(this.schema, model, field);
+            // ✋ Disallow computed fields entirely
+            if (fieldDef.computed) return;
+            if (withoutFields.includes(field)) return;

             if (fieldDef.relation) {
                 // …

These two changes ensure neither the validator nor the CRUD SQL generator allows writing to computed columns.

♻️ Duplicate comments (1)
packages/runtime/src/client/crud/operations/group-by.ts (1)

47-47: Reuse a single ExpressionBuilder for fieldRef to avoid unnecessary instantiations

Creating a new ExpressionBuilder for each fieldRef call adds overhead without benefit. Reuse one per query scope.

Apply this diff:

-        const fieldRef = (field: string) => this.dialect.fieldRef(this.model, field, expressionBuilder(), '$sub');
+        const sharedEb = expressionBuilder<any, any>();
+        const fieldRef = (field: string) => this.dialect.fieldRef(this.model, field, sharedEb, '$sub');
🧹 Nitpick comments (2)
packages/runtime/src/client/query-utils.ts (1)

321-329: Add explicit return type to aggregate for API stability and chaining clarity

Type inference likely returns a Kysely ExpressionWrapper, but making it explicit avoids accidental type widening/regression and clarifies that the result supports .as().

Apply this diff:

-export function aggregate(eb: ExpressionBuilder<any, any>, expr: Expression<any>, op: AGGREGATE_OPERATORS) {
+export function aggregate(
+    eb: ExpressionBuilder<any, any>,
+    expr: Expression<any>,
+    op: AGGREGATE_OPERATORS,
+): ExpressionWrapper<any, any, unknown> {
     return match(op)
         .with('_count', () => eb.fn.count(expr))
         .with('_sum', () => eb.fn.sum(expr))
         .with('_avg', () => eb.fn.avg(expr))
         .with('_min', () => eb.fn.min(expr))
         .with('_max', () => eb.fn.max(expr))
         .exhaustive();
 }
packages/runtime/src/client/crud/dialects/base.ts (1)

98-103: Consider adding null checks for computed field safety.

While the logic correctly handles field origin, computed fields might have undefined originModel. Consider adding explicit validation for computed fields.

-                const fieldRef = this.fieldRef(
-                    fieldDef.originModel ?? model,
-                    key,
-                    eb,
-                    fieldDef.originModel ?? modelAlias,
-                );
+                const fieldRef = fieldDef.computed
+                    ? this.fieldRef(model, key, eb, modelAlias)
+                    : this.fieldRef(
+                          fieldDef.originModel ?? model,
+                          key,
+                          eb,
+                          fieldDef.originModel ?? modelAlias,
+                      );
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2e95aa5 and 2704643.

📒 Files selected for processing (7)
  • packages/runtime/src/client/crud/dialects/base.ts (8 hunks)
  • packages/runtime/src/client/crud/dialects/postgresql.ts (2 hunks)
  • packages/runtime/src/client/crud/dialects/sqlite.ts (2 hunks)
  • packages/runtime/src/client/crud/operations/base.ts (3 hunks)
  • packages/runtime/src/client/crud/operations/group-by.ts (5 hunks)
  • packages/runtime/src/client/query-utils.ts (1 hunks)
  • packages/runtime/test/client-api/computed-fields.test.ts (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (4)
packages/runtime/src/client/query-utils.ts (2)
packages/sdk/src/schema/expression.ts (1)
  • Expression (1-10)
packages/runtime/src/client/constants.ts (2)
  • AGGREGATE_OPERATORS (29-29)
  • AGGREGATE_OPERATORS (30-30)
packages/runtime/src/client/crud/operations/base.ts (1)
packages/runtime/src/client/crud/dialects/base.ts (1)
  • fieldRef (996-998)
packages/runtime/src/client/crud/dialects/base.ts (2)
packages/runtime/src/client/query-utils.ts (2)
  • aggregate (321-329)
  • buildFieldRef (157-179)
packages/runtime/src/client/constants.ts (2)
  • AGGREGATE_OPERATORS (29-29)
  • AGGREGATE_OPERATORS (30-30)
packages/runtime/src/client/crud/operations/group-by.ts (2)
packages/runtime/src/client/crud/dialects/base.ts (1)
  • fieldRef (996-998)
packages/runtime/src/client/query-utils.ts (1)
  • aggregate (321-329)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build-test (20.x)
🔇 Additional comments (11)
packages/runtime/src/client/crud/dialects/postgresql.ts (1)

229-229: LGTM: Centralizing field references through dialect.fieldRef

Good move replacing buildFieldRef with this.fieldRef. This keeps computed-field logic consistent at the dialect layer and aligns with the PR’s objective.

Also applies to: 252-252

packages/runtime/test/client-api/computed-fields.test.ts (1)

40-91: Great coverage for computed fields across where/orderBy/aggregate/groupBy

These assertions exercise the critical paths introduced by fieldRef and aggregate. They should prevent regressions for computed-field support.

packages/runtime/src/client/crud/operations/base.ts (1)

189-191: LGTM: Distinct fallback now correctly uses dialect.fieldRef

Using the dialect-aware fieldRef here ensures distinct-on emulation works even for computed fields.

packages/runtime/src/client/crud/operations/group-by.ts (1)

91-101: LGTM: Aggregations now use the centralized aggregate helper

This simplifies branching and keeps aggregate operator handling consistent across call sites, aligning with the new AGGREGATE_OPERATORS set.

packages/runtime/src/client/crud/dialects/base.ts (5)

3-3: LGTM! Clean import consolidation.

The addition of expressionBuilder to the imports is well-placed and follows the existing import organization pattern.


755-757: Good use of expressionBuilder for computed field resolution.

The usage of expressionBuilder() here correctly enables computed field resolution in orderBy clauses, which is a key improvement for supporting computed fields in sorting operations.


868-868: LGTM! Consistent field reference handling.

The use of fieldRef for computed fields in SELECT operations ensures proper resolution through the centralized mechanism.


996-998: Excellent centralization of field reference logic.

The new fieldRef method effectively centralizes field reference construction, delegating to buildFieldRef with appropriate schema and options. This abstraction improves maintainability and consistency across the codebase.


728-730: Aggregate operator handling is fully exhaustive and type-safe

The aggregate function in packages/runtime/src/client/crud/dialects/base.ts covers every member of AGGREGATE_OPERATORS via a ts-pattern match(...).exhaustive(). The union is declared as:

// packages/runtime/src/client/constants.ts
export const AGGREGATE_OPERATORS = [
  '_count', '_sum', '_avg', '_min', '_max'
] as const;
export type AGGREGATE_OPERATORS = (typeof AGGREGATE_OPERATORS)[number];

Since the pattern match covers all five operators and ends with .exhaustive(), any invalid string cast to AGGREGATE_OPERATORS would be caught at runtime with an exception. Casting field as AGGREGATE_OPERATORS is safe here provided that field can only be one of the five literals.

No changes required.

packages/runtime/src/client/crud/dialects/sqlite.ts (2)

173-173: LGTM! Consistent migration to centralized fieldRef.

The replacement of buildFieldRef with this.fieldRef correctly maintains the same functionality while using the new centralized field reference method.


202-202: Type casting maintained correctly.

The as ArgsType cast is properly preserved after migrating to this.fieldRef, ensuring type compatibility within the context.

@ymc9 ymc9 merged commit c28fe54 into dev Aug 13, 2025
5 of 7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants