Skip to content

Conversation

@yagodorea
Copy link

@yagodorea yagodorea commented Jul 30, 2025

Pull Request Checklist

  • Have you added new tests to prevent regressions?
  • If a documentation update is necessary, have you opened a PR to the documentation repository? >> docs: query builder website#804
  • Did you update the typescript typings accordingly (if applicable)?
  • Does the description below contain a link to an existing issue (Closes #[issue]) or a description of the issue you are solving?
  • Does the name of your PR follow our conventions?

Description of Changes

Adds a simple query builder to sequelize static models. Basic functionality:

  • Model.select() -> will instantiate the query builder for chaining
  • .attributes(['a', 'b']) -> select attributes
  • .where({c: true, d: 1}) -> specify where conditions
  • .getQuery() -> return plain SQL
  • Add an .execute() method that will run the raw sql instead of returning it
  • Support .limit() and .offset()
  • Support .orderBy([['a', 'DESC'], ['b', 'ASC']])
  • Support .groupBy(['a', 'b', 1, 2])
  • Support .having()
  • Support .andHaving()
  • Support .include() with custom join logic

Future improvements/additions:

  • Support returning the Model objects instead of raw result
  • Support .andWhere using Sequelize.where(col, op, value), Sequelize.literal() and WhereBrackets as per proposal
  • Support .orWhere, block simultaneous usage with .andWhere
  • Allow QueryBuilder on right-hand side of where options as a subquery/literal (DynamicValues?)
  • Support CTEs

Relates to #394

Summary by CodeRabbit

Summary by CodeRabbit

  • New Features
    • Introduced a chainable query builder interface, allowing users to build and execute SQL queries directly from models using a fluent API.
    • Added support for custom SQL join conditions and flexible attribute selection in joined tables.
    • Added a new static select() method on models to initiate query building.
  • Bug Fixes
    • Improved error handling for missing join conditions in custom joins.
  • Tests
    • Added comprehensive integration tests covering query builder features, SQL generation, joins, and execution across multiple database dialects.
  • Enhancements
    • Updated dialect support to explicitly include the iLike operation for PostgreSQL.

* WIP: query builder

* restore changelog files, not sure why they were deleted...

* restore changelog files without pre-commit hook

* fix main changelog

* refactor tests

* add execute method to query builder

* Implement limit + offset, refactor test to use expectsql

* feat: implement remaining features

* fix: prettier

* fix: eslint

* fix: remove js test file

* fix: db2

* fix: db2

* fix: db2
@yagodorea yagodorea requested a review from a team as a code owner July 30, 2025 00:33
@yagodorea yagodorea requested review from WikiRik and sdepold July 30, 2025 00:33
@coderabbitai
Copy link

coderabbitai bot commented Jul 30, 2025

Walkthrough

A new functional, chainable SQL query builder API was implemented for Sequelize models. This includes a QueryBuilder class, a static select() method on models, support for custom JOINs with explicit ON conditions, and comprehensive integration tests. The query builder enables fluent construction of SELECT queries with attributes, where clauses, grouping, ordering, limits, and complex join conditions. Additionally, dialect support for the iLike operation was added for Postgres.

Changes

Cohort / File(s) Change Summary
Query Builder Implementation
packages/core/src/expression-builders/query-builder.ts
Introduces the QueryBuilder class and createQueryBuilder factory function for building SQL SELECT queries with a fluent, immutable API, supporting chaining of attributes, where, group by, having, order, limit, offset, and custom JOINs with explicit ON clauses.
Model Integration
packages/core/src/model.js, packages/core/src/model.d.ts
Adds a static select() method to the Model class, returning a new QueryBuilder instance for the model. Updates TypeScript declarations to reflect the new method and its return type.
Custom Join Support
packages/core/src/abstract-dialect/query-generator.js
Adds generateCustomJoin() to AbstractQueryGenerator for generating SQL JOIN clauses with user-defined ON conditions and attribute selection. Updates generateInclude() to delegate to this method when an include has the _isCustomJoin flag set.
Integration Tests
packages/core/test/integration/query-builder/query-builder.test.ts
Adds comprehensive integration tests for the QueryBuilder, covering query generation, chaining, joins with custom ON conditions, error handling, dialect-specific syntax, and execution against a test database.
Dialect Support Update
packages/core/src/abstract-dialect/dialect.ts, packages/postgres/src/dialect.ts
Extends dialect support metadata by adding an operations.iLike flag set to false in the abstract dialect and overridden to true in the Postgres dialect, indicating explicit support for the iLike operation.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Model
    participant QueryBuilder
    participant QueryGenerator
    participant Database

    User->>Model: Model.select()
    Model->>QueryBuilder: new QueryBuilder(Model)
    User->>QueryBuilder: .attributes().where().includes()...
    User->>QueryBuilder: .getQuery()
    QueryBuilder->>QueryGenerator: generate SQL (with options)
    QueryGenerator-->>QueryBuilder: SQL string
    User->>QueryBuilder: .execute()
    QueryBuilder->>Database: Run SQL
    Database-->>QueryBuilder: Results
    QueryBuilder-->>User: Results
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~40 minutes

Assessment against linked issues

Objective Addressed Explanation
Implement a functional, chainable query builder for SELECT queries (Issue #394)
Support for dynamic WHERE, IN, AND, OR, and flexible query construction (Issue #394)
Allow custom JOINs and complex join conditions in query builder (Issue #394)
Integration with Sequelize models via a static method (Issue #394)

Assessment against linked issues: Out-of-scope changes

No out-of-scope changes found.

Poem

Oh, what a joy for a rabbit to see,
A builder for queries, so fluent and free!
With joins and with where,
And attributes rare,
Now Sequelize sings, “Compose queries with glee!”
🐇✨
SELECTing with style, as easy as pie—
This bunny approves, and so do I!

Note

⚡️ Unit Test Generation is now available in beta!

Learn more here, or try it out under "Finishing Touches" below.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

🪧 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.
    • Explain this complex logic.
    • 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. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • 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 src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

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

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

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.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • 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.

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: 1

🧹 Nitpick comments (7)
packages/core/src/abstract-dialect/query-generator.js (1)

1786-1786: Fix typo in property name.

-        : include.right && this._dialect.supports['RIGHT JOIN']
+        : include.right && this.dialect.supports['RIGHT JOIN']
packages/core/src/expression-builders/query-builder.ts (2)

83-83: Consider deep cloning nested objects in includes.

The current implementation uses shallow copy with spread operator for includes. If include objects contain nested mutable properties, they could be shared between clones.

-    newBuilder._include = this._include.map(include => ({ ...include }));
+    newBuilder._include = this._include.map(include => structuredClone(include));

Note: structuredClone requires Node.js 17+. Alternatively, use a deep clone utility.


302-302: Add missing 'JOIN' to the multiline regex pattern.

The regex pattern for multiline formatting is missing 'JOIN', which would not properly format JOIN clauses.

-      return sql.replaceAll(/FROM|LEFT|INNER|RIGHT|WHERE|GROUP|HAVING|ORDER/g, '\n$&');
+      return sql.replaceAll(/FROM|LEFT|INNER|RIGHT|JOIN|WHERE|GROUP|HAVING|ORDER/g, '\n$&');
packages/core/test/integration/query-builder/query-builder.test.ts (4)

66-69: Remove redundant truncate calls after force sync.

When using sync({ force: true }), the table is already dropped and recreated, making the subsequent truncate() calls unnecessary.

-    await Post.sync({ force: true });
-    await Post.truncate();
-    await User.sync({ force: true });
-    await User.truncate();
+    await Post.sync({ force: true });
+    await User.sync({ force: true });

184-193: Consider tracking the position notation feature.

The commented-out tests for ORDER BY position notation indicate an unimplemented feature. Consider creating an issue to track this if it's a desired functionality.

Would you like me to create an issue to track the implementation of ORDER BY position notation support?


400-492: Consider splitting this complex integration test.

While this test provides valuable end-to-end validation, it tests many features at once (joins, grouping, having, ordering, aggregation). Consider splitting it into smaller, more focused tests for better maintainability and easier debugging when failures occur.

For example, you could have separate tests for:

  • Complex joins with aggregation
  • GROUP BY with HAVING conditions
  • Multi-table queries with WHERE conditions

This would make it easier to identify which specific feature is failing if the test breaks.


1-666: Consider adding tests for additional edge cases.

The test suite is comprehensive, but consider adding tests for:

  • Empty WHERE objects where({})
  • Multiple includes/joins on the same table with different aliases
  • Circular join references (if applicable)
  • Very long attribute lists or deeply nested conditions to test any potential limits
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4136f52 and bad9fd1.

📒 Files selected for processing (5)
  • packages/core/src/abstract-dialect/query-generator.js (2 hunks)
  • packages/core/src/expression-builders/query-builder.ts (1 hunks)
  • packages/core/src/model.d.ts (2 hunks)
  • packages/core/src/model.js (2 hunks)
  • packages/core/test/integration/query-builder/query-builder.test.ts (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (3)
packages/core/src/model.d.ts (2)
packages/core/src/model.js (1)
  • Model (149-4648)
packages/core/src/expression-builders/query-builder.ts (1)
  • QueryBuilder (46-336)
packages/core/src/expression-builders/query-builder.ts (3)
packages/core/src/model.js (1)
  • Model (149-4648)
packages/core/src/model.d.ts (5)
  • ModelStatic (3200-3200)
  • FindAttributeOptions (786-795)
  • GroupOption (149-149)
  • Order (775-775)
  • OrderItem (767-774)
packages/core/test/unit/sql/generateJoin.test.js (1)
  • include (18-18)
packages/core/src/model.js (1)
packages/core/src/expression-builders/query-builder.ts (1)
  • QueryBuilder (46-336)
🪛 Biome (2.1.2)
packages/core/src/model.js

[error] 42-43: Illegal use of an import declaration outside of a module

not allowed inside scripts

(parse)


[error] 43-44: Illegal use of an import declaration outside of a module

not allowed inside scripts

(parse)

⏰ 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). (2)
  • GitHub Check: Upload install and build artifact (Node 20)
  • GitHub Check: Upload install and build artifact (Node 18)
🔇 Additional comments (15)
packages/core/src/model.js (2)

43-43: LGTM: Clean import addition for QueryBuilder functionality.

The import follows the established patterns and is appropriately placed among other expression-builder imports.


4631-4647: Excellent implementation of the query builder entry point.

The select() method provides a clean and intuitive API for accessing the new QueryBuilder functionality. Key strengths:

  • Clean integration with the existing Model class
  • Follows Sequelize's static method conventions
  • Comprehensive JSDoc with practical example
  • Enables fluent/chainable query building as intended
  • Simple implementation that delegates to QueryBuilder appropriately

This serves as an excellent entry point for the new query builder functionality described in the PR objectives.

packages/core/src/model.d.ts (2)

29-29: LGTM! Import statement is correctly positioned and structured.

The type-only import for QueryBuilder is appropriately placed among other expression-builder imports and necessary for the new select() method's return type.


2399-2413: LGTM! Well-structured method declaration with excellent documentation.

The select() method declaration is properly typed with generics to maintain type safety, includes comprehensive JSDoc documentation with a practical example, and aligns perfectly with the implementation in model.js. This provides a clean entry point to the new query builder functionality.

packages/core/src/abstract-dialect/query-generator.js (1)

1593-1594: LGTM!

Clean integration of custom join support by checking the _isCustomJoin flag and delegating to the appropriate handler.

packages/core/src/expression-builders/query-builder.ts (1)

210-217: Remove redundant validation check.

The check for options.on on line 215 will never be reached because line 216 throws an error for missing on condition.

 includes(options: QueryBuilderIncludeOptions<M>) {
   if (!options.model) {
     throw new Error('Model is required for includes');
   }
 
-  if (!options.on) {
-    throw new Error('Custom joins require an "on" condition to be specified');
-  }

Actually, I need to re-examine this - the validation looks correct as-is.

packages/core/test/integration/query-builder/query-builder.test.ts (9)

1-16: LGTM!

The imports are well-organized and appropriate for integration testing the QueryBuilder functionality.


18-30: LGTM!

The TypeScript interfaces are well-defined with appropriate use of Sequelize's type inference utilities.


76-149: LGTM!

The basic QueryBuilder functionality tests are comprehensive and properly handle dialect-specific SQL variations.


196-237: LGTM!

The GROUP BY and HAVING clause tests are well-structured and properly use SQL literals for aggregate functions.


240-286: LGTM!

Excellent tests for validating the immutable/functional behavior of the QueryBuilder. This ensures predictable and safe method chaining.


288-325: LGTM!

The PostgreSQL-specific tests properly validate dialect-specific operators and features.


327-340: LGTM!

Good error handling tests that validate edge cases and provide clear error messages.


495-627: LGTM!

Comprehensive tests for the includes/custom joins functionality with proper error handling for missing parameters.


629-665: LGTM! Consider consistent test data cleanup.

The execute tests properly validate query execution and results. Note that these tests create data but rely on the beforeEach sync({ force: true }) for cleanup, which is fine but differs from other tests that might benefit from explicit cleanup.

Copy link
Member

@WikiRik WikiRik left a comment

Choose a reason for hiding this comment

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

Thanks for working on this! In general it looks great! These are my first remarks, I'll need to do a more in depth review later

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: 1

♻️ Duplicate comments (2)
packages/core/src/expression-builders/query-builder.ts (2)

33-42: Consider renaming for clarity.

As mentioned in previous reviews, IncludeOption should be renamed to CustomIncludeOptions to differentiate from Sequelize's existing IncludeOptions type and avoid confusion.


284-290: Handle undefined values more safely.

The non-null assertions (!) on attributes, order, and group could cause runtime errors if these values are undefined.

 const options: SelectOptions<any> = {
-  attributes: this._attributes!,
+  attributes: this._attributes,
   where: this._where,
   include: processedIncludes,
-  order: this._order!,
+  order: this._order,
   limit: this._limit,
   offset: this._offset,
-  group: this._group!,
+  group: this._group,
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bad9fd1 and 12c7c67.

📒 Files selected for processing (2)
  • packages/core/src/expression-builders/query-builder.ts (1 hunks)
  • packages/core/test/integration/query-builder/query-builder.test.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/core/test/integration/query-builder/query-builder.test.ts
🧰 Additional context used
🪛 GitHub Actions: CI
packages/core/src/expression-builders/query-builder.ts

[warning] 1-1: Prettier formatting check failed. Run 'prettier --write' to fix code style issues.

🔇 Additional comments (6)
packages/core/src/expression-builders/query-builder.ts (6)

47-67: LGTM!

The class structure and constructor are well-designed. The use of private fields, proper inheritance from BaseSqlExpression, and clean initialization provide a solid foundation for the query builder.


75-102: LGTM!

The clone() and select() methods implement proper immutable patterns. The experimental warning in select() appropriately alerts users to the feature's status, and the deep copying in clone() ensures query builders don't interfere with each other.


110-206: LGTM!

All query building methods follow consistent immutable patterns and proper TypeScript typing. The andHaving() method correctly accumulates conditions using spread syntax, and all methods maintain chainability by returning new instances.


214-243: LGTM!

The includes() method has proper validation for required parameters and sound logic for handling different join types. The default attributes extraction and custom join handling are well-implemented.


305-310: LGTM!

The multiline formatting feature using regex replacement provides a useful debugging capability for generated SQL queries.


317-350: LGTM!

The execute() method, getter methods, and createQueryBuilder() utility function are well-implemented. The delegation to Sequelize's queryRaw() method is appropriate for executing generated SQL.

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: 1

♻️ Duplicate comments (4)
packages/core/test/integration/query-builder/query-builder.test.ts (2)

30-30: Remove 'QueryBuilder' prefix from describe block.


379-382: Use dialect.supports consistently instead of environment variables.

The test uses sequelize.dialect.supports.operations.iLike correctly but should also check for a dialect capability instead of SEQ_PG_MINIFY_ALIASES environment variable for consistency.

packages/core/src/expression-builders/query-builder.ts (2)

1-1: Fix Prettier formatting violations.


284-290: Remove unsafe non-null assertions.

The non-null assertions can cause runtime errors if these properties are undefined.

🧹 Nitpick comments (3)
packages/core/src/expression-builders/query-builder.ts (3)

85-85: Consider deep cloning for nested include properties.

The current implementation only creates a shallow copy of include objects. If these objects contain nested references (like where conditions with complex objects), modifications might affect the original builder.

-    newBuilder._include = this._include.map(include => ({ ...include }));
+    newBuilder._include = this._include.map(include => structuredClone(include));

Note: structuredClone requires Node.js 17+ or a polyfill for older versions.


97-97: Consider showing the experimental warning only once.

The warning is logged every time select() is called, which could flood logs in applications that build many queries.

+  private static _hasShownWarning = false;
+
   select(): QueryBuilder<M> {
-    logger.warn('Query Builder is an experimental feature. Results may be incomplete or inaccurate. Please verify all outputs before use.');
+    if (!QueryBuilder._hasShownWarning) {
+      logger.warn('Query Builder is an experimental feature. Results may be incomplete or inaccurate. Please verify all outputs before use.');
+      QueryBuilder._hasShownWarning = true;
+    }

306-306: Consider browser/Node.js compatibility for replaceAll.

The replaceAll method requires ES2021 support. For broader compatibility, consider using replace with a global regex.

-      return sql.replaceAll(/FROM|LEFT|INNER|RIGHT|WHERE|GROUP|HAVING|ORDER/g, '\n$&');
+      return sql.replace(/(FROM|LEFT|INNER|RIGHT|WHERE|GROUP|HAVING|ORDER)/g, '\n$1');
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 12c7c67 and 93c14eb.

📒 Files selected for processing (4)
  • packages/core/src/abstract-dialect/dialect.ts (2 hunks)
  • packages/core/src/expression-builders/query-builder.ts (1 hunks)
  • packages/core/test/integration/query-builder/query-builder.test.ts (1 hunks)
  • packages/postgres/src/dialect.ts (1 hunks)
✅ Files skipped from review due to trivial changes (2)
  • packages/postgres/src/dialect.ts
  • packages/core/src/abstract-dialect/dialect.ts
⏰ 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). (2)
  • GitHub Check: Upload install and build artifact (Node 20)
  • GitHub Check: Upload install and build artifact (Node 18)
🔇 Additional comments (1)
packages/core/test/integration/query-builder/query-builder.test.ts (1)

72-228: Well-structured basic functionality tests!

The tests comprehensively cover all basic QueryBuilder features with proper dialect-specific SQL assertions.

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: 1

♻️ Duplicate comments (1)
packages/core/src/model.js (1)

4638-4654: Implementation looks solid, but consider previous feedback about experimental status.

The method implementation is clean and well-documented. However, as mentioned in the previous review, this being a new experimental feature should be communicated to users.

Consider one of these approaches:

  1. Rename to UNSTABLE_select() to clearly indicate experimental status
  2. Add a console warning when the method is used
  3. Add explicit documentation noting this is an experimental feature

The current implementation provides a clean API, but users should be aware of its experimental nature.

  /**
   * Creates a new QueryBuilder instance for this model.
   * This enables functional/chainable query building.
+  * 
+  * @experimental This feature is experimental and may change in future versions.
   *
   * @returns {QueryBuilder} A new QueryBuilder instance for this model
   * ...
   */
- static select() {
+ static UNSTABLE_select() {
    return new QueryBuilder(this).select();
  }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f97f738 and 81225b2.

📒 Files selected for processing (3)
  • packages/core/src/expression-builders/query-builder.ts (1 hunks)
  • packages/core/src/model.js (2 hunks)
  • packages/core/test/integration/query-builder/query-builder.test.ts (1 hunks)
🧰 Additional context used
🪛 GitHub Actions: CI
packages/core/test/integration/query-builder/query-builder.test.ts

[error] 234-234: Test failure in QueryBuilder: AssertionError due to mismatch in expected and actual SQL query for 'should handle specific operators'.


[error] 247-247: Test failure in QueryBuilder: AssertionError due to mismatch in expected and actual SQL query for 'should handle IN queries'.

🪛 Biome (2.1.2)
packages/core/src/model.js

[error] 42-43: Illegal use of an import declaration outside of a module

not allowed inside scripts

(parse)


[error] 43-44: Illegal use of an import declaration outside of a module

not allowed inside scripts

(parse)

🔇 Additional comments (28)
packages/core/src/model.js (1)

43-43: LGTM! Import addition looks correct.

The import for QueryBuilder is properly formatted and necessary for the new select() method implementation. The static analysis errors about import declarations are false positives in this context.

packages/core/test/integration/query-builder/query-builder.test.ts (11)

1-6: LGTM: Well-structured imports and dependencies

The imports correctly reference the QueryBuilder class and necessary test utilities. The import structure follows Sequelize patterns and includes all required dependencies for comprehensive testing.


7-21: LGTM: Clear and appropriate model interfaces

The TypeScript interfaces for TUser and TPost are well-defined with proper optional properties and extend the correct Sequelize model types. This provides good type safety for the tests.


22-58: LGTM: Proper use of beforeEach2 as recommended

The test setup correctly uses beforeEach2 as suggested in previous review comments. The model definitions are comprehensive with appropriate data types and table configurations. The sync operations ensure clean test state.


65-136: LGTM: Comprehensive basic functionality tests

These tests effectively cover the core QueryBuilder functionality including basic SELECT, attribute selection, aliased attributes, literal attributes, and WHERE clauses with proper dialect-specific assertions.


138-222: LGTM: Thorough coverage of SQL clauses

The tests comprehensively cover LIMIT, OFFSET, ORDER BY, GROUP BY, and HAVING clauses with appropriate dialect-specific expectations. The commented TODO section appropriately flags unimplemented functionality.


225-271: LGTM: Excellent immutability testing

These tests properly verify the functional/immutable behavior of the QueryBuilder, ensuring that method calls return new instances and don't mutate the original builder. This is crucial for the fluent API design.


310-314: LGTM: Appropriate error handling for empty attributes

The test properly validates that empty attributes arrays throw meaningful errors, which is important for preventing invalid SQL generation.


374-377: LGTM: Proper dialect capability checking

The test correctly uses vars.sequelize.dialect.supports.operations.iLike to check for dialect capabilities, which aligns with previous review feedback about using dialect capabilities instead of environment variables.


472-602: LGTM: Comprehensive custom joins testing

The includes/joins tests thoroughly cover custom join functionality including LEFT/INNER joins, custom ON conditions, WHERE clauses on joined tables, custom attributes, and proper error handling for missing required parameters.


604-642: LGTM: Practical execution testing

The execute tests verify that the QueryBuilder can actually run queries against the database and return expected results, including complex scenarios with joins returning multiple rows.


288-300: No issues with IN operator implementation

Verified that the Op.in operator is correctly implemented in where-sql-builder.ts and properly typed in model.d.ts. The integration test in packages/core/test/integration/query-builder/query-builder.test.ts matches the expected SQL output, so no changes are required.

packages/core/src/expression-builders/query-builder.ts (16)

1-18: LGTM: Well-structured imports and dependencies

The imports are comprehensive and correctly reference all necessary Sequelize types and utilities. The import organization follows TypeScript best practices.


19-27: LGTM: Well-defined generic include options type

The QueryBuilderIncludeOptions type properly defines the structure for custom joins with appropriate optional properties and generic constraints.


33-42: LGTM: Appropriate internal type for processed includes

The CustomIncludeOptions type correctly extends the include options with internal properties needed for Sequelize integration, including the _isCustomJoin marker.


47-69: LGTM: Solid class foundation with proper encapsulation

The class properly extends BaseSqlExpression, uses appropriate visibility modifiers, and maintains necessary state for query building. The constructor correctly initializes the model and Sequelize instance.


77-91: LGTM: Efficient immutable cloning implementation

The clone method properly creates new instances and deep-copies mutable properties while avoiding unnecessary cloning of immutable objects like the static model. The comment explaining the rationale for the includes array handling is helpful.


99-111: LGTM: Appropriate experimental feature warning

The select method properly implements the one-time warning system to alert users about the experimental nature of the feature. Creating a new instance rather than modifying the current one maintains immutability.


119-124: LGTM: Clean fluent API implementation

The attributes method follows the immutable builder pattern correctly by cloning before modification and returning the new instance for chaining.


132-137: LGTM: Consistent immutable pattern

The where method maintains the same clean pattern as other builder methods, ensuring immutability and chainability.


145-176: LGTM: Comprehensive GROUP BY and HAVING support

The groupBy, having, and andHaving methods properly handle SQL aggregate query constructs. The andHaving method correctly appends to existing conditions using spread syntax.


184-215: LGTM: Standard query modifier implementations

The orderBy, limit, and offset methods follow the established immutable pattern and properly handle their respective SQL clauses.


223-253: LGTM: Robust custom joins implementation

The includes method properly validates required parameters and constructs appropriate include options with sensible defaults. The error handling for missing model and ON conditions is appropriate.


262-289: LGTM: Proper query generation validation and setup

The getQuery method correctly validates that select() was called and properly processes custom includes with the necessary metadata for Sequelize's query generation system.


291-328: LGTM: Comprehensive query options construction

The method properly constructs SelectOptions with all necessary properties, handles conditional assignments correctly, and provides useful multiline formatting. The HAVING clause construction with Op.and is appropriate.


335-339: LGTM: Simple and effective query execution

The execute method appropriately uses Sequelize's raw query interface to run the generated SQL and return results.


346-357: LGTM: Useful utility getters

The tableName and model getters provide convenient access to important properties needed for query construction and debugging.


366-368: LGTM: Clean factory function

The createQueryBuilder function provides a simple way to instantiate the QueryBuilder with proper generic type preservation.

Comment on lines 273 to 286
it('should handle specific operators', () => {
expectsql(
vars.User.select()
.where({
name: { [Op.iLike]: '%john%' },
age: { [Op.between]: [18, 65] },
})
.getQuery(),
{
default:
"SELECT [User].* FROM [users] AS [User] WHERE [User].[name] ILIKE '%john%' AND ([User].[age] BETWEEN 18 AND 65);",
},
);
});
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Investigate pipeline failure for specific operators test

The test appears correct but pipeline failures indicate SQL query mismatches. The iLike operator usage looks proper, but there might be dialect-specific issues.

Based on the pipeline failure, let me verify the iLike operator support:


🏁 Script executed:

#!/bin/bash
# Check iLike operator support across dialects
rg -A 10 "iLike.*:" --type ts

Length of output: 9751


🏁 Script executed:

#!/bin/bash
# Display lines 250-320 of the integration test around the failing test
sed -n '250,320p' packages/core/test/integration/query-builder/query-builder.test.ts

Length of output: 2481


🏁 Script executed:

#!/bin/bash
# Search for QueryBuilder class definition
rg -n "class QueryBuilder" --type ts packages/core/src

Length of output: 193


🏁 Script executed:

#!/bin/bash
# Locate expectsql definition
rg -n "expectsql" -A5 -B5 --type ts packages/core/test

Length of output: 284039


🏁 Script executed:

#!/bin/bash
# Show the beginning of where-sql-builder to inspect operator handling logic
sed -n '1,200p' packages/core/src/abstract-dialect/where-sql-builder.ts

Length of output: 6676


🏁 Script executed:

#!/bin/bash
# Show the top of the integration test to inspect support and default dialect setup
sed -n '1,80p' packages/core/test/integration/query-builder/query-builder.test.ts

Length of output: 2558


🏁 Script executed:

#!/bin/bash
# Find support.ts or index.ts in integration support folder
fd support -t f packages/core/test/integration

Length of output: 90


🏁 Script executed:

#!/bin/bash
# Show the integration test support.ts to determine default dialect
sed -n '1,200p' packages/core/test/integration/support.ts

Length of output: 6655


Add parentheses around BETWEEN clauses in multi-condition queries

The SQL builder correctly maps [Op.iLike] to ILIKE, so the test failure is due to missing grouping for the BETWEEN clause. The integration test expects:

… WHERE [User].[name] ILIKE '%john%' AND ([User].[age] BETWEEN 18 AND 65);

but the builder currently emits:

… WHERE [User].[name] ILIKE '%john%' AND [User].[age] BETWEEN 18 AND 65;

Please update the WhereSqlBuilder to wrap range operators in parentheses when combined with other predicates:

• File: packages/core/src/abstract-dialect/where-sql-builder.ts

  • In the logic that formats [Op.between] and [Op.notBetween], wrap the generated clause in (...) before passing it to joinWithLogicalOperator.

This will align the output with the integration test’s expectation and prevent pipeline mismatches.

🤖 Prompt for AI Agents
In packages/core/src/abstract-dialect/where-sql-builder.ts around the code
handling [Op.between] and [Op.notBetween], the generated SQL clause for these
range operators is missing parentheses when combined with other conditions. To
fix this, update the logic that formats these operators to wrap the entire
BETWEEN clause in parentheses before it is joined with other predicates using
logical operators. This change will ensure the output matches the expected SQL
in the integration test at
packages/core/test/integration/query-builder/query-builder.test.ts lines
273-286.

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

♻️ Duplicate comments (1)
packages/core/test/integration/query-builder/query-builder.test.ts (1)

22-22: Remove Mocha utils format from describe

Per the coding conventions, the Mocha utils format is no longer needed.

-describe('QueryBuilder', () => {
+describe('QueryBuilder', () => {
🧹 Nitpick comments (2)
packages/core/test/integration/query-builder/query-builder.test.ts (2)

52-55: Remove redundant truncate calls

sync({ force: true }) already drops and recreates the table, making the subsequent truncate() calls unnecessary.

     await Post.sync({ force: true });
-    await Post.truncate();
     await User.sync({ force: true });
-    await User.truncate();

172-181: TODO: ORDER BY position notation support

The commented test indicates a desire to support positional ORDER BY notation (e.g., ORDER BY 2). This is a valid SQL feature that could enhance the QueryBuilder.

Would you like me to help implement ORDER BY position notation support or create an issue to track this enhancement?

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 81225b2 and 1c06fcf.

📒 Files selected for processing (1)
  • packages/core/test/integration/query-builder/query-builder.test.ts (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
packages/core/test/integration/query-builder/query-builder.test.ts (3)
packages/core/src/model.js (1)
  • Model (149-4655)
packages/core/test/support.ts (1)
  • beforeEach2 (553-564)
packages/core/src/expression-builders/query-builder.ts (2)
  • QueryBuilder (47-358)
  • where (132-137)
⏰ 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). (2)
  • GitHub Check: Upload install and build artifact (Node 18)
  • GitHub Check: Upload install and build artifact (Node 20)
🔇 Additional comments (5)
packages/core/test/integration/query-builder/query-builder.test.ts (5)

64-223: Well-structured basic functionality tests

The test suite comprehensively covers all basic QueryBuilder features with proper dialect-specific SQL expectations.


225-271: Excellent immutability tests

These tests properly verify the functional/immutable behavior of the QueryBuilder, ensuring that each method call returns a new instance without mutating the original.


378-381: Correct usage of dialect supports check

Good use of sequelize.dialect.supports for feature detection instead of checking specific dialects.


476-606: Comprehensive custom join tests

Excellent coverage of join functionality including LEFT/INNER joins, custom conditions, attributes selection, and proper error handling for missing required parameters.


608-646: Thorough execution tests

Good coverage of query execution including both simple queries and complex queries with joins, verifying that the QueryBuilder correctly executes and returns expected results.

@sequelize-bot sequelize-bot bot added the conflicted This PR has merge conflicts and will not be present in the list of PRs to review label Nov 13, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

conflicted This PR has merge conflicts and will not be present in the list of PRs to review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants