Skip to content

Add support for index field sort direction (ASC/DESC)#941

Open
DZakh wants to merge 3 commits intomainfrom
claude/add-index-sort-direction-KjM0P
Open

Add support for index field sort direction (ASC/DESC)#941
DZakh wants to merge 3 commits intomainfrom
claude/add-index-sort-direction-KjM0P

Conversation

@DZakh
Copy link
Member

@DZakh DZakh commented Feb 6, 2026

Summary

This PR adds support for specifying sort direction (ASC/DESC) on database index fields. Previously, all index fields defaulted to ascending order. Now users can explicitly specify descending order for individual fields in composite indices.

Key Changes

Schema & Parsing (Rust)

  • Added IndexFieldDirection enum with Asc and Desc variants
  • Created IndexField struct to hold field name and direction (replaces plain String)
  • Updated @index directive parsing to support two formats:
    • Simple string: "fieldName" (defaults to ASC)
    • List with direction: ["fieldName", "DESC"] or ["fieldName", "ASC"]
    • Mixed formats allowed in same index: ["field1", ["field2", "DESC"]]
  • Direction parsing is case-insensitive
  • Updated MultiFieldIndex to work with IndexField instead of String
  • Added comprehensive validation and error messages

Code Generation (ReScript)

  • Updated Table.res to use compositeIndexField type with fieldName and direction
  • Modified PgStorage.res to generate SQL with proper direction syntax:
    • ASC is omitted (default PostgreSQL behavior)
    • DESC is explicitly included in SQL
  • Added directionToSql helper function to convert direction to SQL syntax
  • Updated makeCreateCompositeIndexQuery to handle direction in generated SQL

Templates & Output

  • Updated Handlebars template to pass direction information to ReScript code generation
  • Added CompositeIndexFieldTemplate struct for template serialization
  • Modified test schema to demonstrate new syntax with DESC indices

Tests

  • Added 8 new test cases covering:
    • DESC direction parsing
    • Mixed string and direction formats
    • Plain strings defaulting to ASC
    • Invalid direction validation
    • Wrong list length validation
    • Case-insensitive direction parsing
    • Composite index retrieval with direction
  • Added ReScript tests for SQL generation with various direction combinations

Implementation Details

  • Direction defaults to ASC for backward compatibility with plain string syntax
  • SQL generation omits ASC (PostgreSQL default) but includes DESC for clarity
  • All validation occurs during schema parsing to catch errors early
  • Changes are backward compatible - existing indices without direction specifications work as before

https://claude.ai/code/session_01Ujt5CWYiH3vwP2q3A7m1rB

Summary by CodeRabbit

Release Notes

  • New Features

    • Users can now specify sort directions (ASC/DESC) for individual fields within composite database indices, enabling more precise control over query optimization and index performance.
  • Improvements

    • Enhanced database index generation to properly translate per-field sort directions into correct SQL syntax, ensuring consistent behavior across all index creation operations.

Support specifying sort direction (ASC/DESC) on entity-level @index fields
using the syntax: @index(fields: [["field", "DESC"], "otherField"])

Each field in the index can be either a plain string (defaults to ASC) or
a two-element list [fieldName, direction]. Direction is case-insensitive.

Changes across the full pipeline:
- Rust parsing: new IndexField/IndexFieldDirection types in entity_parsing.rs
- Codegen templates: CompositeIndexFieldTemplate carries direction info
- Handlebars template: generates compositeIndexField records with direction
- ReScript Table.res: new compositeIndexField type with direction variant
- PgStorage.res: new makeCreateCompositeIndexQuery emits "field DESC" in SQL
- Tests: 7 new Rust tests + 3 new ReScript tests for direction parsing/SQL

https://claude.ai/code/session_01Ujt5CWYiH3vwP2q3A7m1rB
@DZakh DZakh requested a review from JonoPrest February 6, 2026 12:20
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 6, 2026

📝 Walkthrough

Walkthrough

This PR introduces direction support for composite database indices across the stack. New types (IndexFieldDirection, compositeIndexField/IndexField) represent field sort directions (Asc/Desc). Composite indices change from plain string arrays to structured objects with field names and directions. SQL generation, template conversion, and schema definitions are updated to handle the enhanced index structure.

Changes

Cohort / File(s) Summary
Type Definitions
codegenerator/cli/npm/envio/src/db/Table.res, codegenerator/cli/src/config_parsing/entity_parsing.rs
Added indexFieldDirection (Asc|Desc) and compositeIndexField/IndexField types to represent index fields with explicit sort directions. Updated compositeIndices from array<array<string>> to structured composite field objects, with function signatures adapted accordingly.
SQL Generation
codegenerator/cli/npm/envio/src/PgStorage.res
Introduced directionToSql() helper to convert direction types to SQL syntax ("" for Asc, " DESC" for Desc) and makeCreateCompositeIndexQuery() to generate CREATE INDEX statements with per-field direction support.
Template & Code Generation
codegenerator/cli/src/hbs_templating/codegen_templates.rs, codegenerator/cli/templates/dynamic/codegen/src/db/Entities.res.hbs
Added CompositeIndexFieldTemplate struct to bridge IndexField objects into template context. Updated EntityRecordTypeTemplate to use structured composite index fields, and modified Handlebars template to emit objects with fieldName and direction properties instead of raw strings.
Schema & Tests
scenarios/test_codegen/schema.graphql, scenarios/test_codegen/test/lib_tests/PgStorage_test.res
Added composite index with mixed directions to test schema (tokenId DESC, owner ASC). Added three test cases for makeCreateCompositeIndexQuery() covering ASC (default), DESC, and mixed direction scenarios with SQL assertion validation.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • JonoPrest
  • moose-code

Poem

🐰 With field directions now in sight,
Indices sort both left and right,
Asc and Desc dance through the code,
From schema down the SQL road!
Sort it all in harmony bright! 🌟

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 45.83% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Add support for index field sort direction (ASC/DESC)' clearly and concisely summarizes the main change—enabling explicit sort direction specification for database index fields.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch claude/add-index-sort-direction-KjM0P

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

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
codegenerator/cli/npm/envio/src/db/Table.res (1)

346-369: ⚠️ Potential issue | 🟠 Major

Direction is silently dropped for single-field composite indices.

When a composite index has exactly one field (e.g., @index(fields: [["myField", "DESC"]])), getSingleIndices extracts only the fieldName and discards the direction. These single-field indices are then created via makeCreateIndexQuery which doesn't support direction. The user's explicit DESC directive would be silently ignored.

Two possible fixes:

  1. Keep single-field-with-direction indices in getCompositeIndices so they go through the direction-aware path.
  2. Or, add direction support to makeCreateIndexQuery as well.
Option 1: Don't demote single-field indices that have a non-default direction
 let getSingleIndices = (table): array<string> => {
   let indexFields = table.fields->Array.keepMap(field =>
     switch field {
     | Field(field) if field.isIndex => Some(field->getDbFieldName)
     | _ => None
     }
   )
 
   table
   ->getUnfilteredCompositeIndicesUnsafe
-  ->Array.keepMap(cidx =>
-    switch cidx {
-    | [{fieldName}] => Some([fieldName])
-    | _ => None
-    }
-  )
+  ->Array.keepMap(cidx =>
+    switch cidx {
+    | [{fieldName, direction: Asc}] => Some([fieldName])
+    | _ => None
+    }
+  )
   ->Array.concat([indexFields])
   ->Array.concatMany
   ->Set.String.fromArray
   ->Set.String.toArray
   ->Js.Array2.sortInPlace
 }

-let getCompositeIndices = (table): array<array<compositeIndexField>> => {
+let getCompositeIndices = (table): array<array<compositeIndexField>> => {
   table
   ->getUnfilteredCompositeIndicesUnsafe
-  ->Array.keep(ind => ind->Array.length > 1)
+  ->Array.keep(ind =>
+    ind->Array.length > 1 ||
+    (ind->Array.length == 1 && ind->Belt.Array.getExn(0).direction != Asc)
+  )
 }
codegenerator/cli/src/config_parsing/entity_parsing.rs (1)

556-569: ⚠️ Potential issue | 🔴 Critical

Direction silently lost for single-field @index(fields: [["fieldName", "DESC"]]) definitions.

When a user defines a single-field index with a direction (e.g., @index(fields: [["tokenId", "DESC"]])), the direction is parsed and stored in IndexField, but:

  1. get_single_field_index() (Line 956) returns only Option<String> — extracting just the field name, not direction.
  2. get_composite_indices() (Line 562) filters for len > 1 — excluding single-field indexes entirely.
  3. Downstream, the template renders single-field indexes with only ~isIndex: bool, while multi-field indexes use ~compositeIndices with direction information.

The user would receive no error or warning, but their explicit DESC specification would be ignored in the generated index. Consider either:

  • Routing single-field indexes with non-default direction through get_composite_indices (or a dedicated path that preserves direction), or
  • Emitting a validation error/warning when direction is specified on a single-field index.
🧹 Nitpick comments (3)
codegenerator/cli/npm/envio/src/PgStorage.res (1)

40-42: Index name doesn't encode direction — potential name collision for same-fields-different-directions.

If two composite indices are declared on the same fields but with different sort orders (e.g., [a ASC, b ASC] and [a DESC, b DESC]), the generated index name will be identical (tableName_a_b). CREATE INDEX IF NOT EXISTS will silently skip the second one. Consider encoding the direction in the index name (e.g., appending _desc for DESC fields) to avoid silent collisions.

Suggested approach
 let makeCreateCompositeIndexQuery = (~tableName, ~indexFields: array<Table.compositeIndexField>, ~pgSchema) => {
   let indexName =
-    tableName ++ "_" ++ indexFields->Js.Array2.map(f => f.fieldName)->Js.Array2.joinWith("_")
+    tableName ++ "_" ++ indexFields->Js.Array2.map(f => {
+      let suffix = switch f.direction {
+      | Table.Asc => ""
+      | Table.Desc => "_desc"
+      }
+      f.fieldName ++ suffix
+    })->Js.Array2.joinWith("_")
codegenerator/cli/src/config_parsing/entity_parsing.rs (2)

952-954: Nit: prefer returning &[IndexField] over &Vec<IndexField>.

Returning a slice is more idiomatic in Rust and more flexible for callers.

♻️ Suggested change
-    pub fn get_index_fields(&self) -> &Vec<IndexField> {
+    pub fn get_index_fields(&self) -> &[IndexField] {
         &self.0
     }

2556-2696: Good test coverage for the new direction feature.

Tests cover DESC parsing, mixed formats, default ASC, invalid direction, wrong list length, case insensitivity, and composite index retrieval. Consider adding a test for a single-field index with direction (e.g., @index(fields: [["tokenId", "DESC"]])) to document the current behavior and prevent regressions — especially in light of the direction-loss concern noted above.

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