@warlock.js/cascade 4.0.47 → 4.0.58
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cjs/context/database-data-source-context.d.ts +29 -0
- package/cjs/context/database-data-source-context.d.ts.map +1 -0
- package/cjs/context/database-data-source-context.js +28 -0
- package/cjs/context/database-data-source-context.js.map +1 -0
- package/cjs/context/database-transaction-context.d.ts +31 -0
- package/cjs/context/database-transaction-context.d.ts.map +1 -0
- package/cjs/context/database-transaction-context.js +34 -0
- package/cjs/context/database-transaction-context.js.map +1 -0
- package/cjs/contracts/database-driver.contract.d.ts +143 -0
- package/cjs/contracts/database-driver.contract.d.ts.map +1 -0
- package/cjs/contracts/database-id-generator.contract.d.ts +109 -0
- package/cjs/contracts/database-id-generator.contract.d.ts.map +1 -0
- package/cjs/contracts/database-remover.contract.d.ts +104 -0
- package/cjs/contracts/database-remover.contract.d.ts.map +1 -0
- package/cjs/contracts/database-restorer.contract.d.ts +143 -0
- package/cjs/contracts/database-restorer.contract.d.ts.map +1 -0
- package/cjs/contracts/database-writer.contract.d.ts +119 -0
- package/cjs/contracts/database-writer.contract.d.ts.map +1 -0
- package/cjs/contracts/driver-blueprint.contract.d.ts +45 -0
- package/cjs/contracts/driver-blueprint.contract.d.ts.map +1 -0
- package/cjs/contracts/index.d.ts +10 -0
- package/cjs/contracts/index.d.ts.map +1 -0
- package/cjs/contracts/migration-driver.contract.d.ts +365 -0
- package/cjs/contracts/migration-driver.contract.d.ts.map +1 -0
- package/cjs/contracts/query-builder.contract.d.ts +1128 -0
- package/cjs/contracts/query-builder.contract.d.ts.map +1 -0
- package/cjs/contracts/sync-adapter.contract.d.ts +58 -0
- package/cjs/contracts/sync-adapter.contract.d.ts.map +1 -0
- package/cjs/data-source/data-source-registry.d.ts +104 -0
- package/cjs/data-source/data-source-registry.d.ts.map +1 -0
- package/cjs/data-source/data-source-registry.js +138 -0
- package/cjs/data-source/data-source-registry.js.map +1 -0
- package/cjs/data-source/data-source.d.ts +106 -0
- package/cjs/data-source/data-source.d.ts.map +1 -0
- package/cjs/data-source/data-source.js +77 -0
- package/cjs/data-source/data-source.js.map +1 -0
- package/cjs/database-dirty-tracker.d.ts +253 -0
- package/cjs/database-dirty-tracker.d.ts.map +1 -0
- package/cjs/database-dirty-tracker.js +389 -0
- package/cjs/database-dirty-tracker.js.map +1 -0
- package/cjs/drivers/mongo/mongo-id-generator.d.ts +116 -0
- package/cjs/drivers/mongo/mongo-id-generator.d.ts.map +1 -0
- package/cjs/drivers/mongo/mongo-id-generator.js +149 -0
- package/cjs/drivers/mongo/mongo-id-generator.js.map +1 -0
- package/cjs/drivers/mongo/mongo-migration-driver.d.ts +228 -0
- package/cjs/drivers/mongo/mongo-migration-driver.d.ts.map +1 -0
- package/cjs/drivers/mongo/mongo-migration-driver.js +524 -0
- package/cjs/drivers/mongo/mongo-migration-driver.js.map +1 -0
- package/cjs/drivers/mongo/mongo-query-builder.d.ts +922 -0
- package/cjs/drivers/mongo/mongo-query-builder.d.ts.map +1 -0
- package/cjs/drivers/mongo/mongo-query-builder.js +1740 -0
- package/cjs/drivers/mongo/mongo-query-builder.js.map +1 -0
- package/cjs/drivers/mongo/mongo-query-operations.d.ts +226 -0
- package/cjs/drivers/mongo/mongo-query-operations.d.ts.map +1 -0
- package/cjs/drivers/mongo/mongo-query-operations.js +270 -0
- package/cjs/drivers/mongo/mongo-query-operations.js.map +1 -0
- package/cjs/drivers/mongo/mongo-query-parser.d.ts +262 -0
- package/cjs/drivers/mongo/mongo-query-parser.d.ts.map +1 -0
- package/cjs/drivers/mongo/mongo-query-parser.js +1351 -0
- package/cjs/drivers/mongo/mongo-query-parser.js.map +1 -0
- package/cjs/drivers/mongo/mongo-sync-adapter.d.ts +79 -0
- package/cjs/drivers/mongo/mongo-sync-adapter.d.ts.map +1 -0
- package/cjs/drivers/mongo/mongo-sync-adapter.js +146 -0
- package/cjs/drivers/mongo/mongo-sync-adapter.js.map +1 -0
- package/cjs/drivers/mongo/mongodb-blueprint.d.ts +30 -0
- package/cjs/drivers/mongo/mongodb-blueprint.d.ts.map +1 -0
- package/cjs/drivers/mongo/mongodb-blueprint.js +51 -0
- package/cjs/drivers/mongo/mongodb-blueprint.js.map +1 -0
- package/cjs/drivers/mongo/mongodb-driver.d.ts +188 -0
- package/cjs/drivers/mongo/mongodb-driver.d.ts.map +1 -0
- package/cjs/drivers/mongo/mongodb-driver.js +411 -0
- package/cjs/drivers/mongo/mongodb-driver.js.map +1 -0
- package/cjs/drivers/mongo/types.d.ts +43 -0
- package/cjs/drivers/mongo/types.d.ts.map +1 -0
- package/cjs/errors/missing-data-source.error.d.ts +22 -0
- package/cjs/errors/missing-data-source.error.d.ts.map +1 -0
- package/cjs/errors/missing-data-source.error.js +29 -0
- package/cjs/errors/missing-data-source.error.js.map +1 -0
- package/cjs/events/model-events.d.ts +231 -0
- package/cjs/events/model-events.d.ts.map +1 -0
- package/cjs/events/model-events.js +259 -0
- package/cjs/events/model-events.js.map +1 -0
- package/cjs/expressions/aggregate-expressions.d.ts +215 -0
- package/cjs/expressions/aggregate-expressions.d.ts.map +1 -0
- package/cjs/expressions/aggregate-expressions.js +221 -0
- package/cjs/expressions/aggregate-expressions.js.map +1 -0
- package/cjs/expressions/index.d.ts +2 -0
- package/cjs/expressions/index.d.ts.map +1 -0
- package/cjs/index.d.ts +41 -0
- package/cjs/index.d.ts.map +1 -0
- package/cjs/index.js +1 -267
- package/cjs/index.js.map +1 -1
- package/cjs/migration/column-builder.d.ts +167 -0
- package/cjs/migration/column-builder.d.ts.map +1 -0
- package/cjs/migration/column-builder.js +217 -0
- package/cjs/migration/column-builder.js.map +1 -0
- package/cjs/migration/foreign-key-builder.d.ts +110 -0
- package/cjs/migration/foreign-key-builder.d.ts.map +1 -0
- package/cjs/migration/foreign-key-builder.js +129 -0
- package/cjs/migration/foreign-key-builder.js.map +1 -0
- package/cjs/migration/index.d.ts +6 -0
- package/cjs/migration/index.d.ts.map +1 -0
- package/cjs/migration/migration-runner.d.ts +231 -0
- package/cjs/migration/migration-runner.d.ts.map +1 -0
- package/cjs/migration/migration-runner.js +443 -0
- package/cjs/migration/migration-runner.js.map +1 -0
- package/cjs/migration/migration.js +1346 -0
- package/cjs/migration/migration.js.map +1 -0
- package/cjs/migration/types.d.ts +132 -0
- package/cjs/migration/types.d.ts.map +1 -0
- package/cjs/model/model.d.ts +1267 -0
- package/cjs/model/model.d.ts.map +1 -0
- package/cjs/model/model.js +1463 -0
- package/cjs/model/model.js.map +1 -0
- package/cjs/model/register-model.d.ts +80 -0
- package/cjs/model/register-model.d.ts.map +1 -0
- package/cjs/model/register-model.js +91 -0
- package/cjs/model/register-model.js.map +1 -0
- package/cjs/remover/database-remover.d.ts +100 -0
- package/cjs/remover/database-remover.d.ts.map +1 -0
- package/cjs/remover/database-remover.js +209 -0
- package/cjs/remover/database-remover.js.map +1 -0
- package/cjs/restorer/database-restorer.d.ts +131 -0
- package/cjs/restorer/database-restorer.d.ts.map +1 -0
- package/cjs/restorer/database-restorer.js +425 -0
- package/cjs/restorer/database-restorer.js.map +1 -0
- package/cjs/sync/index.d.ts +12 -0
- package/cjs/sync/index.d.ts.map +1 -0
- package/cjs/sync/model-events.d.ts +62 -0
- package/cjs/sync/model-events.d.ts.map +1 -0
- package/cjs/sync/model-events.js +49 -0
- package/cjs/sync/model-events.js.map +1 -0
- package/cjs/sync/model-sync-operation.d.ts +163 -0
- package/cjs/sync/model-sync-operation.d.ts.map +1 -0
- package/cjs/sync/model-sync-operation.js +292 -0
- package/cjs/sync/model-sync-operation.js.map +1 -0
- package/cjs/sync/model-sync.d.ts +130 -0
- package/cjs/sync/model-sync.d.ts.map +1 -0
- package/cjs/sync/model-sync.js +178 -0
- package/cjs/sync/model-sync.js.map +1 -0
- package/cjs/sync/sync-context.d.ts +70 -0
- package/cjs/sync/sync-context.d.ts.map +1 -0
- package/cjs/sync/sync-context.js +101 -0
- package/cjs/sync/sync-context.js.map +1 -0
- package/cjs/sync/sync-manager.d.ts +213 -0
- package/cjs/sync/sync-manager.d.ts.map +1 -0
- package/cjs/sync/sync-manager.js +689 -0
- package/cjs/sync/sync-manager.js.map +1 -0
- package/cjs/sync/types.d.ts +289 -0
- package/cjs/sync/types.d.ts.map +1 -0
- package/cjs/types.d.ts +45 -0
- package/cjs/types.d.ts.map +1 -0
- package/cjs/utils/connect-to-database.d.ts +246 -0
- package/cjs/utils/connect-to-database.d.ts.map +1 -0
- package/cjs/utils/connect-to-database.js +92 -0
- package/cjs/utils/connect-to-database.js.map +1 -0
- package/cjs/utils/database-writer.utils.d.ts +15 -0
- package/cjs/utils/database-writer.utils.d.ts.map +1 -0
- package/cjs/utils/database-writer.utils.js +14 -0
- package/cjs/utils/database-writer.utils.js.map +1 -0
- package/cjs/utils/define-model.js +100 -0
- package/cjs/utils/define-model.js.map +1 -0
- package/cjs/utils/once-connected.d.ts +146 -0
- package/cjs/utils/once-connected.d.ts.map +1 -0
- package/cjs/utils/once-connected.js +252 -0
- package/cjs/utils/once-connected.js.map +1 -0
- package/cjs/validation/database-seal-plugins.d.ts +2 -0
- package/cjs/validation/database-seal-plugins.d.ts.map +1 -0
- package/cjs/validation/database-seal-plugins.js +4 -0
- package/cjs/validation/database-seal-plugins.js.map +1 -0
- package/cjs/validation/database-writer-validation-error.d.ts +97 -0
- package/cjs/validation/database-writer-validation-error.d.ts.map +1 -0
- package/cjs/validation/database-writer-validation-error.js +160 -0
- package/cjs/validation/database-writer-validation-error.js.map +1 -0
- package/cjs/validation/index.d.ts +3 -0
- package/cjs/validation/index.d.ts.map +1 -0
- package/cjs/validation/mutators/embed-mutator.d.ts +9 -0
- package/cjs/validation/mutators/embed-mutator.d.ts.map +1 -0
- package/cjs/validation/mutators/embed-mutator.js +33 -0
- package/cjs/validation/mutators/embed-mutator.js.map +1 -0
- package/cjs/validation/plugins/embed-validator-plugin.d.ts +24 -0
- package/cjs/validation/plugins/embed-validator-plugin.d.ts.map +1 -0
- package/cjs/validation/plugins/embed-validator-plugin.js +18 -0
- package/cjs/validation/plugins/embed-validator-plugin.js.map +1 -0
- package/cjs/validation/rules/database-model-rule.d.ts +7 -0
- package/cjs/validation/rules/database-model-rule.d.ts.map +1 -0
- package/cjs/validation/rules/database-model-rule.js +27 -0
- package/cjs/validation/rules/database-model-rule.js.map +1 -0
- package/cjs/validation/transformers/embed-model-transformer.d.ts +3 -0
- package/cjs/validation/transformers/embed-model-transformer.d.ts.map +1 -0
- package/cjs/validation/transformers/embed-model-transformer.js +18 -0
- package/cjs/validation/transformers/embed-model-transformer.js.map +1 -0
- package/cjs/validation/validators/embed-validator.d.ts +21 -0
- package/cjs/validation/validators/embed-validator.d.ts.map +1 -0
- package/cjs/validation/validators/embed-validator.js +42 -0
- package/cjs/validation/validators/embed-validator.js.map +1 -0
- package/cjs/writer/database-writer.d.ts +181 -0
- package/cjs/writer/database-writer.d.ts.map +1 -0
- package/cjs/writer/database-writer.js +402 -0
- package/cjs/writer/database-writer.js.map +1 -0
- package/esm/context/database-data-source-context.d.ts +29 -0
- package/esm/context/database-data-source-context.d.ts.map +1 -0
- package/esm/context/database-data-source-context.js +28 -0
- package/esm/context/database-data-source-context.js.map +1 -0
- package/esm/context/database-transaction-context.d.ts +31 -0
- package/esm/context/database-transaction-context.d.ts.map +1 -0
- package/esm/context/database-transaction-context.js +34 -0
- package/esm/context/database-transaction-context.js.map +1 -0
- package/esm/contracts/database-driver.contract.d.ts +143 -0
- package/esm/contracts/database-driver.contract.d.ts.map +1 -0
- package/esm/contracts/database-id-generator.contract.d.ts +109 -0
- package/esm/contracts/database-id-generator.contract.d.ts.map +1 -0
- package/esm/contracts/database-remover.contract.d.ts +104 -0
- package/esm/contracts/database-remover.contract.d.ts.map +1 -0
- package/esm/contracts/database-restorer.contract.d.ts +143 -0
- package/esm/contracts/database-restorer.contract.d.ts.map +1 -0
- package/esm/contracts/database-writer.contract.d.ts +119 -0
- package/esm/contracts/database-writer.contract.d.ts.map +1 -0
- package/esm/contracts/driver-blueprint.contract.d.ts +45 -0
- package/esm/contracts/driver-blueprint.contract.d.ts.map +1 -0
- package/esm/contracts/index.d.ts +10 -0
- package/esm/contracts/index.d.ts.map +1 -0
- package/esm/contracts/migration-driver.contract.d.ts +365 -0
- package/esm/contracts/migration-driver.contract.d.ts.map +1 -0
- package/esm/contracts/query-builder.contract.d.ts +1128 -0
- package/esm/contracts/query-builder.contract.d.ts.map +1 -0
- package/esm/contracts/sync-adapter.contract.d.ts +58 -0
- package/esm/contracts/sync-adapter.contract.d.ts.map +1 -0
- package/esm/data-source/data-source-registry.d.ts +104 -0
- package/esm/data-source/data-source-registry.d.ts.map +1 -0
- package/esm/data-source/data-source-registry.js +138 -0
- package/esm/data-source/data-source-registry.js.map +1 -0
- package/esm/data-source/data-source.d.ts +106 -0
- package/esm/data-source/data-source.d.ts.map +1 -0
- package/esm/data-source/data-source.js +77 -0
- package/esm/data-source/data-source.js.map +1 -0
- package/esm/database-dirty-tracker.d.ts +253 -0
- package/esm/database-dirty-tracker.d.ts.map +1 -0
- package/esm/database-dirty-tracker.js +389 -0
- package/esm/database-dirty-tracker.js.map +1 -0
- package/esm/drivers/mongo/mongo-id-generator.d.ts +116 -0
- package/esm/drivers/mongo/mongo-id-generator.d.ts.map +1 -0
- package/esm/drivers/mongo/mongo-id-generator.js +149 -0
- package/esm/drivers/mongo/mongo-id-generator.js.map +1 -0
- package/esm/drivers/mongo/mongo-migration-driver.d.ts +228 -0
- package/esm/drivers/mongo/mongo-migration-driver.d.ts.map +1 -0
- package/esm/drivers/mongo/mongo-migration-driver.js +524 -0
- package/esm/drivers/mongo/mongo-migration-driver.js.map +1 -0
- package/esm/drivers/mongo/mongo-query-builder.d.ts +922 -0
- package/esm/drivers/mongo/mongo-query-builder.d.ts.map +1 -0
- package/esm/drivers/mongo/mongo-query-builder.js +1740 -0
- package/esm/drivers/mongo/mongo-query-builder.js.map +1 -0
- package/esm/drivers/mongo/mongo-query-operations.d.ts +226 -0
- package/esm/drivers/mongo/mongo-query-operations.d.ts.map +1 -0
- package/esm/drivers/mongo/mongo-query-operations.js +270 -0
- package/esm/drivers/mongo/mongo-query-operations.js.map +1 -0
- package/esm/drivers/mongo/mongo-query-parser.d.ts +262 -0
- package/esm/drivers/mongo/mongo-query-parser.d.ts.map +1 -0
- package/esm/drivers/mongo/mongo-query-parser.js +1351 -0
- package/esm/drivers/mongo/mongo-query-parser.js.map +1 -0
- package/esm/drivers/mongo/mongo-sync-adapter.d.ts +79 -0
- package/esm/drivers/mongo/mongo-sync-adapter.d.ts.map +1 -0
- package/esm/drivers/mongo/mongo-sync-adapter.js +146 -0
- package/esm/drivers/mongo/mongo-sync-adapter.js.map +1 -0
- package/esm/drivers/mongo/mongodb-blueprint.d.ts +30 -0
- package/esm/drivers/mongo/mongodb-blueprint.d.ts.map +1 -0
- package/esm/drivers/mongo/mongodb-blueprint.js +51 -0
- package/esm/drivers/mongo/mongodb-blueprint.js.map +1 -0
- package/esm/drivers/mongo/mongodb-driver.d.ts +188 -0
- package/esm/drivers/mongo/mongodb-driver.d.ts.map +1 -0
- package/esm/drivers/mongo/mongodb-driver.js +411 -0
- package/esm/drivers/mongo/mongodb-driver.js.map +1 -0
- package/esm/drivers/mongo/types.d.ts +43 -0
- package/esm/drivers/mongo/types.d.ts.map +1 -0
- package/esm/errors/missing-data-source.error.d.ts +22 -0
- package/esm/errors/missing-data-source.error.d.ts.map +1 -0
- package/esm/errors/missing-data-source.error.js +29 -0
- package/esm/errors/missing-data-source.error.js.map +1 -0
- package/esm/events/model-events.d.ts +231 -0
- package/esm/events/model-events.d.ts.map +1 -0
- package/esm/events/model-events.js +259 -0
- package/esm/events/model-events.js.map +1 -0
- package/esm/expressions/aggregate-expressions.d.ts +215 -0
- package/esm/expressions/aggregate-expressions.d.ts.map +1 -0
- package/esm/expressions/aggregate-expressions.js +221 -0
- package/esm/expressions/aggregate-expressions.js.map +1 -0
- package/esm/expressions/index.d.ts +2 -0
- package/esm/expressions/index.d.ts.map +1 -0
- package/esm/index.d.ts +41 -0
- package/esm/index.d.ts.map +1 -0
- package/esm/index.js +1 -40
- package/esm/index.js.map +1 -1
- package/esm/migration/column-builder.d.ts +167 -0
- package/esm/migration/column-builder.d.ts.map +1 -0
- package/esm/migration/column-builder.js +217 -0
- package/esm/migration/column-builder.js.map +1 -0
- package/esm/migration/foreign-key-builder.d.ts +110 -0
- package/esm/migration/foreign-key-builder.d.ts.map +1 -0
- package/esm/migration/foreign-key-builder.js +129 -0
- package/esm/migration/foreign-key-builder.js.map +1 -0
- package/esm/migration/index.d.ts +6 -0
- package/esm/migration/index.d.ts.map +1 -0
- package/esm/migration/migration-runner.d.ts +231 -0
- package/esm/migration/migration-runner.d.ts.map +1 -0
- package/esm/migration/migration-runner.js +443 -0
- package/esm/migration/migration-runner.js.map +1 -0
- package/esm/migration/migration.js +1346 -0
- package/esm/migration/migration.js.map +1 -0
- package/esm/migration/types.d.ts +132 -0
- package/esm/migration/types.d.ts.map +1 -0
- package/esm/model/model.d.ts +1267 -0
- package/esm/model/model.d.ts.map +1 -0
- package/esm/model/model.js +1463 -0
- package/esm/model/model.js.map +1 -0
- package/esm/model/register-model.d.ts +80 -0
- package/esm/model/register-model.d.ts.map +1 -0
- package/esm/model/register-model.js +91 -0
- package/esm/model/register-model.js.map +1 -0
- package/esm/remover/database-remover.d.ts +100 -0
- package/esm/remover/database-remover.d.ts.map +1 -0
- package/esm/remover/database-remover.js +209 -0
- package/esm/remover/database-remover.js.map +1 -0
- package/esm/restorer/database-restorer.d.ts +131 -0
- package/esm/restorer/database-restorer.d.ts.map +1 -0
- package/esm/restorer/database-restorer.js +425 -0
- package/esm/restorer/database-restorer.js.map +1 -0
- package/esm/sync/index.d.ts +12 -0
- package/esm/sync/index.d.ts.map +1 -0
- package/esm/sync/model-events.d.ts +62 -0
- package/esm/sync/model-events.d.ts.map +1 -0
- package/esm/sync/model-events.js +49 -0
- package/esm/sync/model-events.js.map +1 -0
- package/esm/sync/model-sync-operation.d.ts +163 -0
- package/esm/sync/model-sync-operation.d.ts.map +1 -0
- package/esm/sync/model-sync-operation.js +292 -0
- package/esm/sync/model-sync-operation.js.map +1 -0
- package/esm/sync/model-sync.d.ts +130 -0
- package/esm/sync/model-sync.d.ts.map +1 -0
- package/esm/sync/model-sync.js +178 -0
- package/esm/sync/model-sync.js.map +1 -0
- package/esm/sync/sync-context.d.ts +70 -0
- package/esm/sync/sync-context.d.ts.map +1 -0
- package/esm/sync/sync-context.js +101 -0
- package/esm/sync/sync-context.js.map +1 -0
- package/esm/sync/sync-manager.d.ts +213 -0
- package/esm/sync/sync-manager.d.ts.map +1 -0
- package/esm/sync/sync-manager.js +689 -0
- package/esm/sync/sync-manager.js.map +1 -0
- package/esm/sync/types.d.ts +289 -0
- package/esm/sync/types.d.ts.map +1 -0
- package/esm/types.d.ts +45 -0
- package/esm/types.d.ts.map +1 -0
- package/esm/utils/connect-to-database.d.ts +246 -0
- package/esm/utils/connect-to-database.d.ts.map +1 -0
- package/esm/utils/connect-to-database.js +92 -0
- package/esm/utils/connect-to-database.js.map +1 -0
- package/esm/utils/database-writer.utils.d.ts +15 -0
- package/esm/utils/database-writer.utils.d.ts.map +1 -0
- package/esm/utils/database-writer.utils.js +14 -0
- package/esm/utils/database-writer.utils.js.map +1 -0
- package/esm/utils/define-model.js +100 -0
- package/esm/utils/define-model.js.map +1 -0
- package/esm/utils/once-connected.d.ts +146 -0
- package/esm/utils/once-connected.d.ts.map +1 -0
- package/esm/utils/once-connected.js +252 -0
- package/esm/utils/once-connected.js.map +1 -0
- package/esm/validation/database-seal-plugins.d.ts +2 -0
- package/esm/validation/database-seal-plugins.d.ts.map +1 -0
- package/esm/validation/database-seal-plugins.js +4 -0
- package/esm/validation/database-seal-plugins.js.map +1 -0
- package/esm/validation/database-writer-validation-error.d.ts +97 -0
- package/esm/validation/database-writer-validation-error.d.ts.map +1 -0
- package/esm/validation/database-writer-validation-error.js +160 -0
- package/esm/validation/database-writer-validation-error.js.map +1 -0
- package/esm/validation/index.d.ts +3 -0
- package/esm/validation/index.d.ts.map +1 -0
- package/esm/validation/mutators/embed-mutator.d.ts +9 -0
- package/esm/validation/mutators/embed-mutator.d.ts.map +1 -0
- package/esm/validation/mutators/embed-mutator.js +33 -0
- package/esm/validation/mutators/embed-mutator.js.map +1 -0
- package/esm/validation/plugins/embed-validator-plugin.d.ts +24 -0
- package/esm/validation/plugins/embed-validator-plugin.d.ts.map +1 -0
- package/esm/validation/plugins/embed-validator-plugin.js +18 -0
- package/esm/validation/plugins/embed-validator-plugin.js.map +1 -0
- package/esm/validation/rules/database-model-rule.d.ts +7 -0
- package/esm/validation/rules/database-model-rule.d.ts.map +1 -0
- package/esm/validation/rules/database-model-rule.js +27 -0
- package/esm/validation/rules/database-model-rule.js.map +1 -0
- package/esm/validation/transformers/embed-model-transformer.d.ts +3 -0
- package/esm/validation/transformers/embed-model-transformer.d.ts.map +1 -0
- package/esm/validation/transformers/embed-model-transformer.js +18 -0
- package/esm/validation/transformers/embed-model-transformer.js.map +1 -0
- package/esm/validation/validators/embed-validator.d.ts +21 -0
- package/esm/validation/validators/embed-validator.d.ts.map +1 -0
- package/esm/validation/validators/embed-validator.js +42 -0
- package/esm/validation/validators/embed-validator.js.map +1 -0
- package/esm/writer/database-writer.d.ts +181 -0
- package/esm/writer/database-writer.d.ts.map +1 -0
- package/esm/writer/database-writer.js +402 -0
- package/esm/writer/database-writer.js.map +1 -0
- package/package.json +61 -52
|
@@ -0,0 +1,1740 @@
|
|
|
1
|
+
import {get}from'@mongez/reinforcements';import {databaseTransactionContext}from'../../context/database-transaction-context.js';import {dataSourceRegistry}from'../../data-source/data-source-registry.js';import {MongoQueryOperations}from'./mongo-query-operations.js';import {MongoQueryParser}from'./mongo-query-parser.js';/* eslint-disable no-case-declarations */
|
|
2
|
+
/**
|
|
3
|
+
* MongoDB-specific query builder implementation using aggregation pipeline.
|
|
4
|
+
*/
|
|
5
|
+
class MongoQueryBuilder {
|
|
6
|
+
table;
|
|
7
|
+
/**
|
|
8
|
+
* Ordered list of operations to be converted to MongoDB aggregation pipeline.
|
|
9
|
+
* Public to allow parser access.
|
|
10
|
+
*/
|
|
11
|
+
operations = [];
|
|
12
|
+
/**
|
|
13
|
+
* Data source instance
|
|
14
|
+
*/
|
|
15
|
+
dataSource;
|
|
16
|
+
/**
|
|
17
|
+
* Lazy-loaded operations helper for constructing pipeline operations.
|
|
18
|
+
*/
|
|
19
|
+
_operationsHelper;
|
|
20
|
+
hydrateCallback;
|
|
21
|
+
fetchingCallback;
|
|
22
|
+
hydratingCallback;
|
|
23
|
+
fetchedCallback;
|
|
24
|
+
// Scope properties
|
|
25
|
+
pendingGlobalScopes;
|
|
26
|
+
availableLocalScopes;
|
|
27
|
+
disabledGlobalScopes = new Set();
|
|
28
|
+
scopesApplied = false;
|
|
29
|
+
/**
|
|
30
|
+
* Create a new query builder for the given collection.
|
|
31
|
+
* @param collection - The MongoDB collection to query
|
|
32
|
+
*/
|
|
33
|
+
constructor(table, dataSource) {
|
|
34
|
+
this.table = table;
|
|
35
|
+
this.dataSource = dataSource || dataSourceRegistry.get();
|
|
36
|
+
// TODO: Trigger the fetching event
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Gets the operations helper instance, creating it if needed.
|
|
40
|
+
* @returns The operations helper instance
|
|
41
|
+
*/
|
|
42
|
+
get operationsHelper() {
|
|
43
|
+
if (!this._operationsHelper) {
|
|
44
|
+
this._operationsHelper = new MongoQueryOperations(this.operations);
|
|
45
|
+
}
|
|
46
|
+
return this._operationsHelper;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Get collection instance
|
|
50
|
+
*/
|
|
51
|
+
get collection() {
|
|
52
|
+
const driver = this.dataSource.driver;
|
|
53
|
+
return driver.database.collection(this.table);
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Add hydrate callback function
|
|
57
|
+
*/
|
|
58
|
+
hydrate(callback) {
|
|
59
|
+
this.hydrateCallback = callback;
|
|
60
|
+
return this;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Register a callback to be invoked before query execution
|
|
64
|
+
* @returns Unsubscribe function to remove the callback
|
|
65
|
+
*/
|
|
66
|
+
onFetching(callback) {
|
|
67
|
+
this.fetchingCallback = callback;
|
|
68
|
+
return () => {
|
|
69
|
+
this.fetchingCallback = undefined;
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Register a callback to be invoked after records are fetched but before hydration
|
|
74
|
+
* @returns Unsubscribe function to remove the callback
|
|
75
|
+
*/
|
|
76
|
+
onHydrating(callback) {
|
|
77
|
+
this.hydratingCallback = callback;
|
|
78
|
+
return () => {
|
|
79
|
+
this.hydratingCallback = undefined;
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Register a callback to be invoked after records are fetched and hydrated
|
|
84
|
+
* @returns Unsubscribe function to remove the callback
|
|
85
|
+
*/
|
|
86
|
+
onFetched(callback) {
|
|
87
|
+
this.fetchedCallback = callback;
|
|
88
|
+
return () => {
|
|
89
|
+
this.fetchedCallback = undefined;
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Disable one or more global scopes for this query
|
|
94
|
+
*/
|
|
95
|
+
withoutGlobalScope(...scopeNames) {
|
|
96
|
+
scopeNames.forEach((name) => this.disabledGlobalScopes.add(name));
|
|
97
|
+
return this;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Disable all global scopes for this query
|
|
101
|
+
*/
|
|
102
|
+
withoutGlobalScopes() {
|
|
103
|
+
if (this.pendingGlobalScopes) {
|
|
104
|
+
this.pendingGlobalScopes.forEach((_, name) => {
|
|
105
|
+
this.disabledGlobalScopes.add(name);
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
return this;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Apply a local scope to this query
|
|
112
|
+
*/
|
|
113
|
+
scope(scopeName) {
|
|
114
|
+
if (!this.availableLocalScopes) {
|
|
115
|
+
throw new Error(`No local scopes available`);
|
|
116
|
+
}
|
|
117
|
+
const scopeCallback = this.availableLocalScopes.get(scopeName);
|
|
118
|
+
if (!scopeCallback) {
|
|
119
|
+
throw new Error(`Local scope "${scopeName}" not found`);
|
|
120
|
+
}
|
|
121
|
+
// Apply scope immediately (not deferred)
|
|
122
|
+
scopeCallback(this);
|
|
123
|
+
return this;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Apply pending global scopes before query execution
|
|
127
|
+
*/
|
|
128
|
+
applyPendingScopes() {
|
|
129
|
+
if (!this.pendingGlobalScopes || this.scopesApplied) {
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
const beforeOps = [];
|
|
133
|
+
const afterOps = [];
|
|
134
|
+
for (const [name, { callback, timing }] of this.pendingGlobalScopes) {
|
|
135
|
+
// Skip disabled scopes
|
|
136
|
+
if (this.disabledGlobalScopes.has(name)) {
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
// Create temporary query builder to capture operations
|
|
140
|
+
const tempBuilder = new MongoQueryBuilder(this.table, this.dataSource);
|
|
141
|
+
callback(tempBuilder);
|
|
142
|
+
// Collect operations based on timing
|
|
143
|
+
if (timing === "before") {
|
|
144
|
+
beforeOps.push(...tempBuilder.operations);
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
afterOps.push(...tempBuilder.operations);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
// Apply: before scopes + user operations + after scopes
|
|
151
|
+
this.operations = [...beforeOps, ...this.operations, ...afterOps];
|
|
152
|
+
this.scopesApplied = true;
|
|
153
|
+
}
|
|
154
|
+
where(...args) {
|
|
155
|
+
this.addWhereClause("where", args);
|
|
156
|
+
return this;
|
|
157
|
+
}
|
|
158
|
+
orWhere(...args) {
|
|
159
|
+
this.addWhereClause("orWhere", args);
|
|
160
|
+
return this;
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Adds a raw WHERE clause using MongoDB's native query syntax.
|
|
164
|
+
* @param expression - Raw MongoDB expression
|
|
165
|
+
* @param bindings - Optional parameter bindings for string expressions
|
|
166
|
+
*/
|
|
167
|
+
whereRaw(expression, bindings) {
|
|
168
|
+
return this.addRawWhere("whereRaw", expression, bindings);
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Adds a raw OR WHERE clause using MongoDB's native query syntax.
|
|
172
|
+
* @param expression - Raw MongoDB expression
|
|
173
|
+
* @param bindings - Optional parameter bindings
|
|
174
|
+
*/
|
|
175
|
+
orWhereRaw(expression, bindings) {
|
|
176
|
+
return this.addRawWhere("orWhereRaw", expression, bindings);
|
|
177
|
+
}
|
|
178
|
+
// ============================================================================
|
|
179
|
+
// WHERE CLAUSES - COLUMN COMPARISONS
|
|
180
|
+
// ============================================================================
|
|
181
|
+
/**
|
|
182
|
+
* Adds a WHERE clause comparing two columns/fields directly.
|
|
183
|
+
* @param first - The first field name
|
|
184
|
+
* @param operator - The comparison operator
|
|
185
|
+
* @param second - The second field name
|
|
186
|
+
*/
|
|
187
|
+
whereColumn(first, operator, second) {
|
|
188
|
+
this.operationsHelper.addMatchOperation("whereColumn", {
|
|
189
|
+
first,
|
|
190
|
+
operator,
|
|
191
|
+
second,
|
|
192
|
+
});
|
|
193
|
+
return this;
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Adds an OR WHERE clause comparing two columns/fields directly.
|
|
197
|
+
* @param first - The first field name
|
|
198
|
+
* @param operator - The comparison operator
|
|
199
|
+
* @param second - The second field name
|
|
200
|
+
*/
|
|
201
|
+
orWhereColumn(first, operator, second) {
|
|
202
|
+
this.operationsHelper.addMatchOperation("orWhereColumn", {
|
|
203
|
+
first,
|
|
204
|
+
operator,
|
|
205
|
+
second,
|
|
206
|
+
});
|
|
207
|
+
return this;
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Adds multiple column comparison clauses at once.
|
|
211
|
+
* @param comparisons - Array of tuples [leftField, operator, rightField]
|
|
212
|
+
*/
|
|
213
|
+
whereColumns(comparisons) {
|
|
214
|
+
for (const [left, operator, right] of comparisons) {
|
|
215
|
+
this.whereColumn(left, operator, right);
|
|
216
|
+
}
|
|
217
|
+
return this;
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Filters documents where a field's value falls between two other fields.
|
|
221
|
+
* @param field - The field to check
|
|
222
|
+
* @param lowerColumn - The field defining the lower bound
|
|
223
|
+
* @param upperColumn - The field defining the upper bound
|
|
224
|
+
*/
|
|
225
|
+
whereBetweenColumns(field, lowerColumn, upperColumn) {
|
|
226
|
+
this.operationsHelper.addMatchOperation("whereBetweenColumns", {
|
|
227
|
+
field,
|
|
228
|
+
lowerColumn,
|
|
229
|
+
upperColumn,
|
|
230
|
+
});
|
|
231
|
+
return this;
|
|
232
|
+
}
|
|
233
|
+
// ============================================================================
|
|
234
|
+
// WHERE CLAUSES - DATE OPERATIONS
|
|
235
|
+
// ============================================================================
|
|
236
|
+
/**
|
|
237
|
+
* Filters documents where a date field matches the given date (ignoring time).
|
|
238
|
+
* @param field - The date field name
|
|
239
|
+
* @param value - The date to match
|
|
240
|
+
*/
|
|
241
|
+
whereDate(field, value) {
|
|
242
|
+
this.operationsHelper.addMatchOperation("whereDate", { field, value });
|
|
243
|
+
return this;
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Alias for `whereDate()`. Filters by exact date match (ignoring time).
|
|
247
|
+
* @param field - The date field name
|
|
248
|
+
* @param value - The date to match
|
|
249
|
+
*/
|
|
250
|
+
whereDateEquals(field, value) {
|
|
251
|
+
this.operationsHelper.addMatchOperation("whereDateEquals", {
|
|
252
|
+
field,
|
|
253
|
+
value,
|
|
254
|
+
});
|
|
255
|
+
return this;
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Filters documents where a date field is before the given date.
|
|
259
|
+
* @param field - The date field name
|
|
260
|
+
* @param value - The cutoff date
|
|
261
|
+
*/
|
|
262
|
+
whereDateBefore(field, value) {
|
|
263
|
+
this.operationsHelper.addMatchOperation("whereDateBefore", {
|
|
264
|
+
field,
|
|
265
|
+
value,
|
|
266
|
+
});
|
|
267
|
+
return this;
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Filters documents where a date field is after the given date.
|
|
271
|
+
* @param field - The date field name
|
|
272
|
+
* @param value - The cutoff date
|
|
273
|
+
*/
|
|
274
|
+
whereDateAfter(field, value) {
|
|
275
|
+
this.operationsHelper.addMatchOperation("whereDateAfter", { field, value });
|
|
276
|
+
return this;
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Filters documents where a time field matches the given time (HH:MM:SS format).
|
|
280
|
+
* @param field - The time/datetime field name
|
|
281
|
+
* @param value - The time string in HH:MM:SS format
|
|
282
|
+
*/
|
|
283
|
+
whereTime(field, value) {
|
|
284
|
+
this.operationsHelper.addMatchOperation("whereTime", { field, value });
|
|
285
|
+
return this;
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* Filters documents where the day of the month matches the given value (1-31).
|
|
289
|
+
* @param field - The date field name
|
|
290
|
+
* @param value - The day of the month
|
|
291
|
+
*/
|
|
292
|
+
whereDay(field, value) {
|
|
293
|
+
this.operationsHelper.addMatchOperation("whereDay", { field, value });
|
|
294
|
+
return this;
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Filters documents where the month matches the given value (1-12).
|
|
298
|
+
* @param field - The date field name
|
|
299
|
+
* @param value - The month number
|
|
300
|
+
*/
|
|
301
|
+
whereMonth(field, value) {
|
|
302
|
+
this.operationsHelper.addMatchOperation("whereMonth", { field, value });
|
|
303
|
+
return this;
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Filters documents where the year matches the given value.
|
|
307
|
+
* @param field - The date field name
|
|
308
|
+
* @param value - The year
|
|
309
|
+
*/
|
|
310
|
+
whereYear(field, value) {
|
|
311
|
+
this.operationsHelper.addMatchOperation("whereYear", { field, value });
|
|
312
|
+
return this;
|
|
313
|
+
}
|
|
314
|
+
// ============================================================================
|
|
315
|
+
// WHERE CLAUSES - JSON OPERATIONS
|
|
316
|
+
// ============================================================================
|
|
317
|
+
/**
|
|
318
|
+
* Filters documents where a JSON field contains the specified value.
|
|
319
|
+
* @param path - The JSON path to check
|
|
320
|
+
* @param value - The value to search for
|
|
321
|
+
*/
|
|
322
|
+
whereJsonContains(path, value) {
|
|
323
|
+
this.operationsHelper.addMatchOperation("whereJsonContains", {
|
|
324
|
+
path,
|
|
325
|
+
value,
|
|
326
|
+
});
|
|
327
|
+
return this;
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* Filters documents where a JSON field does NOT contain the specified value.
|
|
331
|
+
* @param path - The JSON path to check
|
|
332
|
+
* @param value - The value to exclude
|
|
333
|
+
*/
|
|
334
|
+
whereJsonDoesntContain(path, value) {
|
|
335
|
+
this.operationsHelper.addMatchOperation("whereJsonDoesntContain", {
|
|
336
|
+
path,
|
|
337
|
+
value,
|
|
338
|
+
});
|
|
339
|
+
return this;
|
|
340
|
+
}
|
|
341
|
+
/**
|
|
342
|
+
* Filters documents where a JSON field contains a specific key.
|
|
343
|
+
* @param path - The JSON path to check for key existence
|
|
344
|
+
*/
|
|
345
|
+
whereJsonContainsKey(path) {
|
|
346
|
+
this.operationsHelper.addMatchOperation("whereJsonContainsKey", { path });
|
|
347
|
+
return this;
|
|
348
|
+
}
|
|
349
|
+
/**
|
|
350
|
+
* Filters documents where a JSON array or string has a specific length.
|
|
351
|
+
* @param path - The JSON path to check
|
|
352
|
+
* @param operator - The comparison operator
|
|
353
|
+
* @param value - The length value to compare against
|
|
354
|
+
*/
|
|
355
|
+
whereJsonLength(path, operator, value) {
|
|
356
|
+
this.operationsHelper.addMatchOperation("whereJsonLength", {
|
|
357
|
+
path,
|
|
358
|
+
operator,
|
|
359
|
+
value,
|
|
360
|
+
});
|
|
361
|
+
return this;
|
|
362
|
+
}
|
|
363
|
+
/**
|
|
364
|
+
* Filters documents where a JSON field is an array.
|
|
365
|
+
* @param path - The JSON path to check
|
|
366
|
+
*/
|
|
367
|
+
whereJsonIsArray(path) {
|
|
368
|
+
this.operationsHelper.addMatchOperation("whereJsonIsArray", { path });
|
|
369
|
+
return this;
|
|
370
|
+
}
|
|
371
|
+
/**
|
|
372
|
+
* Filters documents where a JSON field is an object.
|
|
373
|
+
* @param path - The JSON path to check
|
|
374
|
+
*/
|
|
375
|
+
whereJsonIsObject(path) {
|
|
376
|
+
this.operationsHelper.addMatchOperation("whereJsonIsObject", { path });
|
|
377
|
+
return this;
|
|
378
|
+
}
|
|
379
|
+
/**
|
|
380
|
+
* Filters documents where an array field has a specific length.
|
|
381
|
+
* @param field - The array field name
|
|
382
|
+
* @param operator - The comparison operator
|
|
383
|
+
* @param value - The length value to compare against
|
|
384
|
+
*/
|
|
385
|
+
whereArrayLength(field, operator, value) {
|
|
386
|
+
this.operationsHelper.addMatchOperation("whereArrayLength", {
|
|
387
|
+
field,
|
|
388
|
+
operator,
|
|
389
|
+
value,
|
|
390
|
+
});
|
|
391
|
+
return this;
|
|
392
|
+
}
|
|
393
|
+
// ============================================================================
|
|
394
|
+
// WHERE CLAUSES - CONVENIENCE METHODS
|
|
395
|
+
// ============================================================================
|
|
396
|
+
/**
|
|
397
|
+
* Filters documents by ID (convenience method for `where("id", value)`).
|
|
398
|
+
* @param value - The ID value to match
|
|
399
|
+
*/
|
|
400
|
+
whereId(value) {
|
|
401
|
+
return this.where("id", value);
|
|
402
|
+
}
|
|
403
|
+
/**
|
|
404
|
+
* Filters documents by multiple IDs (convenience method for `whereIn("id", values)`).
|
|
405
|
+
* @param values - Array of ID values to match
|
|
406
|
+
*/
|
|
407
|
+
whereIds(values) {
|
|
408
|
+
return this.whereIn("id", values);
|
|
409
|
+
}
|
|
410
|
+
/**
|
|
411
|
+
* Filters documents by UUID (convenience method for `where("uuid", value)`).
|
|
412
|
+
* @param value - The UUID string to match
|
|
413
|
+
*/
|
|
414
|
+
whereUuid(value) {
|
|
415
|
+
return this.where("uuid", value);
|
|
416
|
+
}
|
|
417
|
+
/**
|
|
418
|
+
* Filters documents by ULID (convenience method for `where("ulid", value)`).
|
|
419
|
+
* @param value - The ULID string to match
|
|
420
|
+
*/
|
|
421
|
+
whereUlid(value) {
|
|
422
|
+
return this.where("ulid", value);
|
|
423
|
+
}
|
|
424
|
+
/**
|
|
425
|
+
* Performs full-text search on one or more fields.
|
|
426
|
+
* @param fields - Field name or array of field names to search
|
|
427
|
+
* @param query - The search query string
|
|
428
|
+
*/
|
|
429
|
+
whereFullText(fields, query) {
|
|
430
|
+
const filters = typeof fields === "string" ? { fields: [fields] } : { fields: fields ?? [] };
|
|
431
|
+
this.operationsHelper.addMatchOperation("whereFullText", {
|
|
432
|
+
fields: filters.fields,
|
|
433
|
+
query,
|
|
434
|
+
});
|
|
435
|
+
return this;
|
|
436
|
+
}
|
|
437
|
+
/**
|
|
438
|
+
* Performs full-text search with OR logic.
|
|
439
|
+
* @param fields - Field name or array of field names to search
|
|
440
|
+
* @param query - The search query string
|
|
441
|
+
*/
|
|
442
|
+
orWhereFullText(fields, query) {
|
|
443
|
+
const filters = typeof fields === "string" ? { fields: [fields] } : { fields: fields ?? [] };
|
|
444
|
+
this.operationsHelper.addMatchOperation("orWhereFullText", {
|
|
445
|
+
fields: filters.fields,
|
|
446
|
+
query,
|
|
447
|
+
});
|
|
448
|
+
return this;
|
|
449
|
+
}
|
|
450
|
+
/**
|
|
451
|
+
* Alias for `whereFullText()` with a single field.
|
|
452
|
+
* @param field - The field name to search
|
|
453
|
+
* @param query - The search query string
|
|
454
|
+
*/
|
|
455
|
+
whereSearch(field, query) {
|
|
456
|
+
return this.whereFullText(field, query);
|
|
457
|
+
}
|
|
458
|
+
/**
|
|
459
|
+
* Negates a set of conditions using a callback.
|
|
460
|
+
* @param callback - Callback function defining conditions to negate
|
|
461
|
+
*/
|
|
462
|
+
whereNot(callback) {
|
|
463
|
+
this.operationsHelper.addMatchOperation("where:not", { callback });
|
|
464
|
+
return this;
|
|
465
|
+
}
|
|
466
|
+
/**
|
|
467
|
+
* Negates a set of conditions with OR logic.
|
|
468
|
+
* @param callback - Callback function defining conditions to negate
|
|
469
|
+
*/
|
|
470
|
+
orWhereNot(callback) {
|
|
471
|
+
this.operationsHelper.addMatchOperation("orWhere:not", { callback });
|
|
472
|
+
return this;
|
|
473
|
+
}
|
|
474
|
+
// ============================================================================
|
|
475
|
+
// WHERE CLAUSES - COMPARISON OPERATORS
|
|
476
|
+
// ============================================================================
|
|
477
|
+
/**
|
|
478
|
+
* Filters documents where a field's value matches any value in the given array.
|
|
479
|
+
* @param field - The field name to check
|
|
480
|
+
* @param values - Array of values to match against
|
|
481
|
+
*/
|
|
482
|
+
whereIn(field, values) {
|
|
483
|
+
this.operationsHelper.addMatchOperation("whereIn", { field, values });
|
|
484
|
+
return this;
|
|
485
|
+
}
|
|
486
|
+
/**
|
|
487
|
+
* Filters documents where a field's value does NOT match any value in the array.
|
|
488
|
+
* @param field - The field name to check
|
|
489
|
+
* @param values - Array of values to exclude
|
|
490
|
+
*/
|
|
491
|
+
whereNotIn(field, values) {
|
|
492
|
+
this.operationsHelper.addMatchOperation("whereNotIn", { field, values });
|
|
493
|
+
return this;
|
|
494
|
+
}
|
|
495
|
+
/**
|
|
496
|
+
* Filters documents where a field's value is null or undefined.
|
|
497
|
+
* @param field - The field name to check
|
|
498
|
+
*/
|
|
499
|
+
whereNull(field) {
|
|
500
|
+
this.operationsHelper.addMatchOperation("whereNull", { field });
|
|
501
|
+
return this;
|
|
502
|
+
}
|
|
503
|
+
/**
|
|
504
|
+
* Filters documents where a field's value is NOT null or undefined.
|
|
505
|
+
* @param field - The field name to check
|
|
506
|
+
*/
|
|
507
|
+
whereNotNull(field) {
|
|
508
|
+
this.operationsHelper.addMatchOperation("whereNotNull", { field });
|
|
509
|
+
return this;
|
|
510
|
+
}
|
|
511
|
+
/**
|
|
512
|
+
* Filters documents where a field's value falls within the given range (inclusive).
|
|
513
|
+
* @param field - The field name to check
|
|
514
|
+
* @param range - Tuple of [min, max] values
|
|
515
|
+
*/
|
|
516
|
+
whereBetween(field, range) {
|
|
517
|
+
this.operationsHelper.addMatchOperation("whereBetween", { field, range });
|
|
518
|
+
return this;
|
|
519
|
+
}
|
|
520
|
+
/**
|
|
521
|
+
* Filters documents where a field's value is NOT within the given range.
|
|
522
|
+
* @param field - The field name to check
|
|
523
|
+
* @param range - Tuple of [min, max] values to exclude
|
|
524
|
+
*/
|
|
525
|
+
whereNotBetween(field, range) {
|
|
526
|
+
this.operationsHelper.addMatchOperation("whereNotBetween", {
|
|
527
|
+
field,
|
|
528
|
+
range,
|
|
529
|
+
});
|
|
530
|
+
return this;
|
|
531
|
+
}
|
|
532
|
+
// ============================================================================
|
|
533
|
+
// WHERE CLAUSES - PATTERN MATCHING
|
|
534
|
+
// ============================================================================
|
|
535
|
+
/**
|
|
536
|
+
* Filters documents where a field matches the given pattern (case-insensitive).
|
|
537
|
+
* @param field - The field name to search
|
|
538
|
+
* @param pattern - The pattern to match
|
|
539
|
+
*/
|
|
540
|
+
whereLike(field, pattern) {
|
|
541
|
+
this.operationsHelper.addMatchOperation("whereLike", { field, pattern });
|
|
542
|
+
return this;
|
|
543
|
+
}
|
|
544
|
+
/**
|
|
545
|
+
* Filters documents where a field does NOT match the given pattern.
|
|
546
|
+
* @param field - The field name to search
|
|
547
|
+
* @param pattern - The pattern to exclude
|
|
548
|
+
*/
|
|
549
|
+
whereNotLike(field, pattern) {
|
|
550
|
+
this.operationsHelper.addMatchOperation("whereNotLike", { field, pattern });
|
|
551
|
+
return this;
|
|
552
|
+
}
|
|
553
|
+
/**
|
|
554
|
+
* Filters documents where a field's value starts with the given prefix.
|
|
555
|
+
* @param field - The field name to check
|
|
556
|
+
* @param value - The prefix to match
|
|
557
|
+
*/
|
|
558
|
+
whereStartsWith(field, value) {
|
|
559
|
+
this.operationsHelper.addMatchOperation("whereStartsWith", {
|
|
560
|
+
field,
|
|
561
|
+
value,
|
|
562
|
+
});
|
|
563
|
+
return this;
|
|
564
|
+
}
|
|
565
|
+
/**
|
|
566
|
+
* Filters documents where a field's value does NOT start with the given prefix.
|
|
567
|
+
* @param field - The field name to check
|
|
568
|
+
* @param value - The prefix to exclude
|
|
569
|
+
*/
|
|
570
|
+
whereNotStartsWith(field, value) {
|
|
571
|
+
this.operationsHelper.addMatchOperation("whereNotStartsWith", {
|
|
572
|
+
field,
|
|
573
|
+
value,
|
|
574
|
+
});
|
|
575
|
+
return this;
|
|
576
|
+
}
|
|
577
|
+
/**
|
|
578
|
+
* Filters documents where a field's value ends with the given suffix.
|
|
579
|
+
* @param field - The field name to check
|
|
580
|
+
* @param value - The suffix to match
|
|
581
|
+
*/
|
|
582
|
+
whereEndsWith(field, value) {
|
|
583
|
+
this.operationsHelper.addMatchOperation("whereEndsWith", { field, value });
|
|
584
|
+
return this;
|
|
585
|
+
}
|
|
586
|
+
/**
|
|
587
|
+
* Filters documents where a field's value does NOT end with the given suffix.
|
|
588
|
+
* @param field - The field name to check
|
|
589
|
+
* @param value - The suffix to exclude
|
|
590
|
+
*/
|
|
591
|
+
whereNotEndsWith(field, value) {
|
|
592
|
+
this.operationsHelper.addMatchOperation("whereNotEndsWith", {
|
|
593
|
+
field,
|
|
594
|
+
value,
|
|
595
|
+
});
|
|
596
|
+
return this;
|
|
597
|
+
}
|
|
598
|
+
/**
|
|
599
|
+
* Filters documents where a date field falls within the given date range.
|
|
600
|
+
* @param field - The date field name
|
|
601
|
+
* @param range - Tuple of [startDate, endDate]
|
|
602
|
+
*/
|
|
603
|
+
whereDateBetween(field, range) {
|
|
604
|
+
this.operationsHelper.addMatchOperation("whereDateBetween", {
|
|
605
|
+
field,
|
|
606
|
+
range,
|
|
607
|
+
});
|
|
608
|
+
return this;
|
|
609
|
+
}
|
|
610
|
+
/**
|
|
611
|
+
* Filters documents where a date field is NOT within the given date range.
|
|
612
|
+
* @param field - The date field name
|
|
613
|
+
* @param range - Tuple of [startDate, endDate] to exclude
|
|
614
|
+
*/
|
|
615
|
+
whereDateNotBetween(field, range) {
|
|
616
|
+
this.operationsHelper.addMatchOperation("whereDateNotBetween", {
|
|
617
|
+
field,
|
|
618
|
+
range,
|
|
619
|
+
});
|
|
620
|
+
return this;
|
|
621
|
+
}
|
|
622
|
+
whereExists(param) {
|
|
623
|
+
if (typeof param === "function") {
|
|
624
|
+
this.operationsHelper.addMatchOperation("where:exists", {
|
|
625
|
+
callback: param,
|
|
626
|
+
});
|
|
627
|
+
return this;
|
|
628
|
+
}
|
|
629
|
+
this.operationsHelper.addMatchOperation("whereExists", { field: param });
|
|
630
|
+
return this;
|
|
631
|
+
}
|
|
632
|
+
whereNotExists(param) {
|
|
633
|
+
if (typeof param === "function") {
|
|
634
|
+
this.operationsHelper.addMatchOperation("where:notExists", {
|
|
635
|
+
callback: param,
|
|
636
|
+
});
|
|
637
|
+
return this;
|
|
638
|
+
}
|
|
639
|
+
this.operationsHelper.addMatchOperation("whereNotExists", {
|
|
640
|
+
field: param,
|
|
641
|
+
});
|
|
642
|
+
return this;
|
|
643
|
+
}
|
|
644
|
+
whereSize(field, ...args) {
|
|
645
|
+
if (args.length === 1) {
|
|
646
|
+
this.operationsHelper.addMatchOperation("whereSize", {
|
|
647
|
+
field,
|
|
648
|
+
operator: "=",
|
|
649
|
+
size: args[0],
|
|
650
|
+
});
|
|
651
|
+
}
|
|
652
|
+
else {
|
|
653
|
+
this.operationsHelper.addMatchOperation("whereSize", {
|
|
654
|
+
field,
|
|
655
|
+
operator: args[0],
|
|
656
|
+
size: args[1],
|
|
657
|
+
});
|
|
658
|
+
}
|
|
659
|
+
return this;
|
|
660
|
+
}
|
|
661
|
+
// ============================================================================
|
|
662
|
+
// WHERE CLAUSES - FULL-TEXT SEARCH
|
|
663
|
+
// ============================================================================
|
|
664
|
+
/**
|
|
665
|
+
* Performs a full-text search on the specified fields.
|
|
666
|
+
* @param query - The search query string
|
|
667
|
+
* @param filters - Optional additional filter conditions
|
|
668
|
+
*/
|
|
669
|
+
textSearch(query, filters) {
|
|
670
|
+
this.operationsHelper.addMatchOperation("textSearch", { query, filters });
|
|
671
|
+
return this;
|
|
672
|
+
}
|
|
673
|
+
// ============================================================================
|
|
674
|
+
// WHERE CLAUSES - ARRAY OPERATIONS
|
|
675
|
+
// ============================================================================
|
|
676
|
+
/**
|
|
677
|
+
* Filters documents where an array field contains the given value.
|
|
678
|
+
* @param field - The array field name
|
|
679
|
+
* @param value - The value to search for in the array
|
|
680
|
+
* @param key - Optional key to check within array objects
|
|
681
|
+
*/
|
|
682
|
+
whereArrayContains(field, value, key) {
|
|
683
|
+
this.operationsHelper.addMatchOperation("whereArrayContains", {
|
|
684
|
+
field,
|
|
685
|
+
value,
|
|
686
|
+
key,
|
|
687
|
+
});
|
|
688
|
+
return this;
|
|
689
|
+
}
|
|
690
|
+
/**
|
|
691
|
+
* Filters documents where an array field does NOT contain the given value.
|
|
692
|
+
* @param field - The array field name
|
|
693
|
+
* @param value - The value to exclude from the array
|
|
694
|
+
* @param key - Optional key to check within array objects
|
|
695
|
+
*/
|
|
696
|
+
whereArrayNotContains(field, value, key) {
|
|
697
|
+
this.operationsHelper.addMatchOperation("whereArrayNotContains", {
|
|
698
|
+
field,
|
|
699
|
+
value,
|
|
700
|
+
key,
|
|
701
|
+
});
|
|
702
|
+
return this;
|
|
703
|
+
}
|
|
704
|
+
/**
|
|
705
|
+
* Filters documents where an array field contains the value OR is empty.
|
|
706
|
+
* @param field - The array field name
|
|
707
|
+
* @param value - The value to search for
|
|
708
|
+
* @param key - Optional key to check within array objects
|
|
709
|
+
*/
|
|
710
|
+
whereArrayHasOrEmpty(field, value, key) {
|
|
711
|
+
this.operationsHelper.addMatchOperation("whereArrayHasOrEmpty", {
|
|
712
|
+
field,
|
|
713
|
+
value,
|
|
714
|
+
key,
|
|
715
|
+
});
|
|
716
|
+
return this;
|
|
717
|
+
}
|
|
718
|
+
/**
|
|
719
|
+
* Filters documents where an array field does NOT contain the value AND is not empty.
|
|
720
|
+
* @param field - The array field name
|
|
721
|
+
* @param value - The value to exclude
|
|
722
|
+
* @param key - Optional key to check within array objects
|
|
723
|
+
*/
|
|
724
|
+
whereArrayNotHaveOrEmpty(field, value, key) {
|
|
725
|
+
this.operationsHelper.addMatchOperation("whereArrayNotHaveOrEmpty", {
|
|
726
|
+
field,
|
|
727
|
+
value,
|
|
728
|
+
key,
|
|
729
|
+
});
|
|
730
|
+
return this;
|
|
731
|
+
}
|
|
732
|
+
/**
|
|
733
|
+
* Internal helper for processing where clause arguments.
|
|
734
|
+
* @param prefix - The operation prefix
|
|
735
|
+
* @param args - The arguments passed to where/orWhere
|
|
736
|
+
*/
|
|
737
|
+
addWhereClause(prefix, args) {
|
|
738
|
+
if (args.length === 1) {
|
|
739
|
+
if (typeof args[0] === "function") {
|
|
740
|
+
// Callback-based where
|
|
741
|
+
this.operationsHelper.addMatchOperation(`${prefix}:callback`, args[0]);
|
|
742
|
+
}
|
|
743
|
+
else {
|
|
744
|
+
// Object-based where
|
|
745
|
+
this.operationsHelper.addMatchOperation(`${prefix}:object`, args[0]);
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
else if (args.length === 2) {
|
|
749
|
+
// Simple equality: where(field, value)
|
|
750
|
+
this.operationsHelper.addMatchOperation(prefix, {
|
|
751
|
+
field: args[0],
|
|
752
|
+
operator: "=",
|
|
753
|
+
value: args[1],
|
|
754
|
+
});
|
|
755
|
+
}
|
|
756
|
+
else if (args.length === 3) {
|
|
757
|
+
// With operator: where(field, operator, value)
|
|
758
|
+
this.operationsHelper.addMatchOperation(prefix, {
|
|
759
|
+
field: args[0],
|
|
760
|
+
operator: args[1],
|
|
761
|
+
value: args[2],
|
|
762
|
+
});
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
/**
|
|
766
|
+
* Internal helper for adding raw where clauses.
|
|
767
|
+
* @param type - The operation type
|
|
768
|
+
* @param expression - The raw expression in MongoDB query language
|
|
769
|
+
* @param bindings - Optional bindings for the expression
|
|
770
|
+
*/
|
|
771
|
+
addRawWhere(type, expression, bindings) {
|
|
772
|
+
this.operationsHelper.addMatchOperation(type, { expression, bindings });
|
|
773
|
+
return this;
|
|
774
|
+
}
|
|
775
|
+
/**
|
|
776
|
+
* Normalizes select field arguments into a structured format.
|
|
777
|
+
* @param args - The arguments to normalize
|
|
778
|
+
* @returns Normalized selection object with fields and aliases
|
|
779
|
+
*/
|
|
780
|
+
normalizeSelectFields(args) {
|
|
781
|
+
// Single argument cases
|
|
782
|
+
if (args.length === 1) {
|
|
783
|
+
const arg = args[0];
|
|
784
|
+
// Object format: { field: 1, field2: 0, field3: "alias", field4: true }
|
|
785
|
+
if (typeof arg === "object" && !Array.isArray(arg)) {
|
|
786
|
+
return { projection: arg };
|
|
787
|
+
}
|
|
788
|
+
// Array format: ["field1", "field2"]
|
|
789
|
+
if (Array.isArray(arg)) {
|
|
790
|
+
return { fields: arg };
|
|
791
|
+
}
|
|
792
|
+
// Single string: "field"
|
|
793
|
+
if (typeof arg === "string") {
|
|
794
|
+
return { fields: [arg] };
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
// Multiple string arguments: select("field1", "field2", "field3")
|
|
798
|
+
return { fields: args.filter((arg) => typeof arg === "string") };
|
|
799
|
+
}
|
|
800
|
+
select(...args) {
|
|
801
|
+
const normalized = this.normalizeSelectFields(args);
|
|
802
|
+
this.operationsHelper.addProjectOperation("select", normalized);
|
|
803
|
+
return this;
|
|
804
|
+
}
|
|
805
|
+
/**
|
|
806
|
+
* Selects a field with an alias.
|
|
807
|
+
* @param field - The field to select
|
|
808
|
+
* @param alias - The alias name for the field
|
|
809
|
+
* @returns The query builder instance
|
|
810
|
+
*/
|
|
811
|
+
selectAs(field, alias) {
|
|
812
|
+
return this.select({ [field]: alias });
|
|
813
|
+
}
|
|
814
|
+
/**
|
|
815
|
+
* Adds a computed field using a raw MongoDB expression.
|
|
816
|
+
* @param expression - The raw MongoDB expression
|
|
817
|
+
* @param bindings - Optional parameter bindings for string expressions
|
|
818
|
+
*/
|
|
819
|
+
selectRaw(expression, bindings) {
|
|
820
|
+
this.operationsHelper.addProjectOperation("selectRaw", {
|
|
821
|
+
expression,
|
|
822
|
+
bindings,
|
|
823
|
+
});
|
|
824
|
+
return this;
|
|
825
|
+
}
|
|
826
|
+
/**
|
|
827
|
+
* Adds multiple computed fields using raw MongoDB expressions.
|
|
828
|
+
* @param definitions - Array of field definitions with alias, expression, and optional bindings
|
|
829
|
+
*/
|
|
830
|
+
selectRawMany(definitions) {
|
|
831
|
+
for (const definition of definitions) {
|
|
832
|
+
this.selectRaw({ [definition.alias]: definition.expression }, definition.bindings);
|
|
833
|
+
}
|
|
834
|
+
return this;
|
|
835
|
+
}
|
|
836
|
+
/**
|
|
837
|
+
* Adds a subquery as a computed field.
|
|
838
|
+
* @param expression - The subquery expression
|
|
839
|
+
* @param alias - The alias for the computed field
|
|
840
|
+
*/
|
|
841
|
+
selectSub(expression, alias) {
|
|
842
|
+
this.operationsHelper.addProjectOperation("selectSub", {
|
|
843
|
+
expression,
|
|
844
|
+
alias,
|
|
845
|
+
});
|
|
846
|
+
return this;
|
|
847
|
+
}
|
|
848
|
+
/**
|
|
849
|
+
* Adds an additional subquery field to existing selections.
|
|
850
|
+
* @param expression - The subquery expression
|
|
851
|
+
* @param alias - The alias for the computed field
|
|
852
|
+
*/
|
|
853
|
+
addSelectSub(expression, alias) {
|
|
854
|
+
this.operationsHelper.addProjectOperation("addSelectSub", {
|
|
855
|
+
expression,
|
|
856
|
+
alias,
|
|
857
|
+
});
|
|
858
|
+
return this;
|
|
859
|
+
}
|
|
860
|
+
/**
|
|
861
|
+
* Adds an aggregate value as a computed field.
|
|
862
|
+
* @param field - The field to aggregate
|
|
863
|
+
* @param aggregate - The aggregate function to apply
|
|
864
|
+
* @param alias - The alias for the computed field
|
|
865
|
+
*/
|
|
866
|
+
selectAggregate(field, aggregate, alias) {
|
|
867
|
+
this.operationsHelper.addProjectOperation("selectAggregate", {
|
|
868
|
+
field,
|
|
869
|
+
aggregate,
|
|
870
|
+
alias,
|
|
871
|
+
});
|
|
872
|
+
return this;
|
|
873
|
+
}
|
|
874
|
+
/**
|
|
875
|
+
* Adds a boolean field indicating whether a related document exists.
|
|
876
|
+
* @param field - The field to check for existence
|
|
877
|
+
* @param alias - The alias for the boolean field
|
|
878
|
+
*/
|
|
879
|
+
selectExists(field, alias) {
|
|
880
|
+
this.operationsHelper.addProjectOperation("selectExists", {
|
|
881
|
+
field,
|
|
882
|
+
alias,
|
|
883
|
+
});
|
|
884
|
+
return this;
|
|
885
|
+
}
|
|
886
|
+
/**
|
|
887
|
+
* Adds a count field for a related collection.
|
|
888
|
+
* @param field - The field to count
|
|
889
|
+
* @param alias - The alias for the count field
|
|
890
|
+
*/
|
|
891
|
+
selectCount(field, alias) {
|
|
892
|
+
this.operationsHelper.addProjectOperation("selectCount", { field, alias });
|
|
893
|
+
return this;
|
|
894
|
+
}
|
|
895
|
+
/**
|
|
896
|
+
* Adds a CASE-like conditional field using multiple conditions.
|
|
897
|
+
* @param cases - Array of when/then pairs
|
|
898
|
+
* @param otherwise - Default value if no conditions match
|
|
899
|
+
* @param alias - The alias for the computed field
|
|
900
|
+
*/
|
|
901
|
+
selectCase(cases, otherwise, alias) {
|
|
902
|
+
this.operationsHelper.addProjectOperation("selectCase", {
|
|
903
|
+
cases,
|
|
904
|
+
otherwise,
|
|
905
|
+
alias,
|
|
906
|
+
});
|
|
907
|
+
return this;
|
|
908
|
+
}
|
|
909
|
+
/**
|
|
910
|
+
* Adds a simple conditional field (if/else).
|
|
911
|
+
* @param condition - The condition to evaluate
|
|
912
|
+
* @param thenValue - Value if condition is true
|
|
913
|
+
* @param elseValue - Value if condition is false
|
|
914
|
+
* @param alias - The alias for the computed field
|
|
915
|
+
*/
|
|
916
|
+
selectWhen(condition, thenValue, elseValue, alias) {
|
|
917
|
+
this.operationsHelper.addProjectOperation("selectWhen", {
|
|
918
|
+
condition,
|
|
919
|
+
thenValue,
|
|
920
|
+
elseValue,
|
|
921
|
+
alias,
|
|
922
|
+
});
|
|
923
|
+
return this;
|
|
924
|
+
}
|
|
925
|
+
/**
|
|
926
|
+
* Allows direct manipulation of the MongoDB projection object.
|
|
927
|
+
* @param callback - Function that receives and modifies the projection object
|
|
928
|
+
*/
|
|
929
|
+
selectDriverProjection(callback) {
|
|
930
|
+
this.operationsHelper.addProjectOperation("selectDriverProjection", {
|
|
931
|
+
callback,
|
|
932
|
+
});
|
|
933
|
+
return this;
|
|
934
|
+
}
|
|
935
|
+
/**
|
|
936
|
+
* Extracts a JSON field from a document.
|
|
937
|
+
* @param path - The JSON path to extract
|
|
938
|
+
* @param alias - Optional alias for the extracted field
|
|
939
|
+
*/
|
|
940
|
+
selectJson(path, alias) {
|
|
941
|
+
this.operationsHelper.addProjectOperation("selectJson", { path, alias });
|
|
942
|
+
return this;
|
|
943
|
+
}
|
|
944
|
+
/**
|
|
945
|
+
* Extracts a JSON field using a raw MongoDB expression.
|
|
946
|
+
* @param path - The JSON path
|
|
947
|
+
* @param expression - The raw expression for extraction
|
|
948
|
+
* @param alias - The alias for the extracted field
|
|
949
|
+
*/
|
|
950
|
+
selectJsonRaw(path, expression, alias) {
|
|
951
|
+
this.operationsHelper.addProjectOperation("selectJsonRaw", {
|
|
952
|
+
path,
|
|
953
|
+
expression,
|
|
954
|
+
alias,
|
|
955
|
+
});
|
|
956
|
+
return this;
|
|
957
|
+
}
|
|
958
|
+
/**
|
|
959
|
+
* Excludes a JSON path from the results.
|
|
960
|
+
* @param path - The JSON path to exclude
|
|
961
|
+
*/
|
|
962
|
+
deselectJson(path) {
|
|
963
|
+
this.operationsHelper.addProjectOperation("deselectJson", { path });
|
|
964
|
+
return this;
|
|
965
|
+
}
|
|
966
|
+
/**
|
|
967
|
+
* Concatenates multiple fields into a single string field.
|
|
968
|
+
* @param fields - Array of fields or expressions to concatenate
|
|
969
|
+
* @param alias - The alias for the concatenated field
|
|
970
|
+
*/
|
|
971
|
+
selectConcat(fields, alias) {
|
|
972
|
+
this.operationsHelper.addProjectOperation("selectConcat", {
|
|
973
|
+
fields,
|
|
974
|
+
alias,
|
|
975
|
+
});
|
|
976
|
+
return this;
|
|
977
|
+
}
|
|
978
|
+
/**
|
|
979
|
+
* Returns the first non-null value from a list of fields.
|
|
980
|
+
* @param fields - Array of fields to check
|
|
981
|
+
* @param alias - The alias for the coalesced field
|
|
982
|
+
*/
|
|
983
|
+
selectCoalesce(fields, alias) {
|
|
984
|
+
this.operationsHelper.addProjectOperation("selectCoalesce", {
|
|
985
|
+
fields,
|
|
986
|
+
alias,
|
|
987
|
+
});
|
|
988
|
+
return this;
|
|
989
|
+
}
|
|
990
|
+
/**
|
|
991
|
+
* Adds window function operations to the query.
|
|
992
|
+
* @param spec - The window function specification
|
|
993
|
+
*/
|
|
994
|
+
selectWindow(spec) {
|
|
995
|
+
this.operationsHelper.addOperation("$setWindowFields", "selectWindow", { spec }, false);
|
|
996
|
+
return this;
|
|
997
|
+
}
|
|
998
|
+
deselect(...args) {
|
|
999
|
+
const fields = this.normalizeSelectFields(args);
|
|
1000
|
+
this.operationsHelper.addProjectOperation("deselect", { fields });
|
|
1001
|
+
return this;
|
|
1002
|
+
}
|
|
1003
|
+
/**
|
|
1004
|
+
* Returns only distinct values for the specified fields.
|
|
1005
|
+
* @param fields - Optional field names to use for distinctness
|
|
1006
|
+
*/
|
|
1007
|
+
distinctValues(fields) {
|
|
1008
|
+
this.operationsHelper.addGroupOperation("distinct", { fields }, false);
|
|
1009
|
+
return this;
|
|
1010
|
+
}
|
|
1011
|
+
addSelect(...args) {
|
|
1012
|
+
const fields = this.normalizeSelectFields(args);
|
|
1013
|
+
this.operationsHelper.addProjectOperation("addSelect", { fields });
|
|
1014
|
+
return this;
|
|
1015
|
+
}
|
|
1016
|
+
/**
|
|
1017
|
+
* Removes all field selection restrictions.
|
|
1018
|
+
*/
|
|
1019
|
+
clearSelect() {
|
|
1020
|
+
this.operations = this.operations.filter((op) => op.stage !== "$project");
|
|
1021
|
+
return this;
|
|
1022
|
+
}
|
|
1023
|
+
/**
|
|
1024
|
+
* Alias for `clearSelect()`. Removes all field restrictions.
|
|
1025
|
+
*/
|
|
1026
|
+
selectAll() {
|
|
1027
|
+
return this.clearSelect();
|
|
1028
|
+
}
|
|
1029
|
+
/**
|
|
1030
|
+
* Alias for `clearSelect()`. Resets to default field selection.
|
|
1031
|
+
*/
|
|
1032
|
+
selectDefault() {
|
|
1033
|
+
return this.clearSelect();
|
|
1034
|
+
}
|
|
1035
|
+
orderBy(fieldOrFields, direction = "asc") {
|
|
1036
|
+
if (typeof fieldOrFields === "string") {
|
|
1037
|
+
// Single field
|
|
1038
|
+
this.operationsHelper.addSortOperation("orderBy", {
|
|
1039
|
+
field: fieldOrFields,
|
|
1040
|
+
direction,
|
|
1041
|
+
});
|
|
1042
|
+
}
|
|
1043
|
+
else {
|
|
1044
|
+
// Multiple fields - add each as a separate operation (they'll be merged)
|
|
1045
|
+
for (const [field, dir] of Object.entries(fieldOrFields)) {
|
|
1046
|
+
this.operationsHelper.addSortOperation("orderBy", {
|
|
1047
|
+
field,
|
|
1048
|
+
direction: dir,
|
|
1049
|
+
});
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
1052
|
+
return this;
|
|
1053
|
+
}
|
|
1054
|
+
/**
|
|
1055
|
+
* Orders the query results by a field in descending order.
|
|
1056
|
+
* @param field - The field name to sort by
|
|
1057
|
+
*/
|
|
1058
|
+
orderByDesc(field) {
|
|
1059
|
+
return this.orderBy(field, "desc");
|
|
1060
|
+
}
|
|
1061
|
+
/**
|
|
1062
|
+
* Orders the query results using a raw MongoDB sort expression.
|
|
1063
|
+
* @param expression - The raw MongoDB sort expression
|
|
1064
|
+
* @param bindings - Optional parameter bindings
|
|
1065
|
+
*/
|
|
1066
|
+
orderByRaw(expression, bindings) {
|
|
1067
|
+
this.operationsHelper.addSortOperation("orderByRaw", {
|
|
1068
|
+
expression,
|
|
1069
|
+
bindings,
|
|
1070
|
+
});
|
|
1071
|
+
return this;
|
|
1072
|
+
}
|
|
1073
|
+
/**
|
|
1074
|
+
* Orders the query results randomly.
|
|
1075
|
+
*/
|
|
1076
|
+
orderByRandom(limit = 1000) {
|
|
1077
|
+
this.operationsHelper.addSortOperation("orderByRandom", { limit }, false);
|
|
1078
|
+
return this;
|
|
1079
|
+
}
|
|
1080
|
+
/**
|
|
1081
|
+
* Orders results by a date field in descending order (newest first).
|
|
1082
|
+
* @param column - The date column to sort by
|
|
1083
|
+
*/
|
|
1084
|
+
latest(column = "createdAt") {
|
|
1085
|
+
return this.orderBy(column, "desc").get();
|
|
1086
|
+
}
|
|
1087
|
+
/**
|
|
1088
|
+
* Orders results by a date field in ascending order (oldest first).
|
|
1089
|
+
* @param column - The date column to sort by
|
|
1090
|
+
*/
|
|
1091
|
+
oldest(column = "createdAt") {
|
|
1092
|
+
return this.orderBy(column, "asc");
|
|
1093
|
+
}
|
|
1094
|
+
// ============================================================================
|
|
1095
|
+
// LIMITING / PAGINATION
|
|
1096
|
+
// ============================================================================
|
|
1097
|
+
/**
|
|
1098
|
+
* Limits the number of documents returned by the query.
|
|
1099
|
+
* @param value - The maximum number of documents to return
|
|
1100
|
+
*/
|
|
1101
|
+
limit(value) {
|
|
1102
|
+
this.operationsHelper.addOperation("$limit", "limit", { value }, false);
|
|
1103
|
+
return this;
|
|
1104
|
+
}
|
|
1105
|
+
/**
|
|
1106
|
+
* Skips a specified number of documents in the query results.
|
|
1107
|
+
* @param value - The number of documents to skip
|
|
1108
|
+
*/
|
|
1109
|
+
skip(value) {
|
|
1110
|
+
this.operationsHelper.addOperation("$skip", "skip", { value }, false);
|
|
1111
|
+
return this;
|
|
1112
|
+
}
|
|
1113
|
+
/**
|
|
1114
|
+
* Alias for `skip()`. Skips a specified number of documents.
|
|
1115
|
+
* @param value - The number of documents to skip
|
|
1116
|
+
*/
|
|
1117
|
+
offset(value) {
|
|
1118
|
+
return this.skip(value);
|
|
1119
|
+
}
|
|
1120
|
+
/**
|
|
1121
|
+
* Alias for `limit()`. Limits the number of documents returned.
|
|
1122
|
+
* @param value - The maximum number of documents to return
|
|
1123
|
+
*/
|
|
1124
|
+
take(value) {
|
|
1125
|
+
return this.limit(value);
|
|
1126
|
+
}
|
|
1127
|
+
/**
|
|
1128
|
+
* Applies cursor-based filtering for pagination.
|
|
1129
|
+
* @param after - Cursor value for forward pagination
|
|
1130
|
+
* @param before - Cursor value for backward pagination
|
|
1131
|
+
*/
|
|
1132
|
+
cursor(after, before) {
|
|
1133
|
+
this.operationsHelper.addMatchOperation("cursor", { after, before });
|
|
1134
|
+
return this;
|
|
1135
|
+
}
|
|
1136
|
+
groupBy(fields, aggregates) {
|
|
1137
|
+
if (aggregates) {
|
|
1138
|
+
this.operationsHelper.addGroupOperation("groupByWithAggregates", { fields, aggregates }, false);
|
|
1139
|
+
}
|
|
1140
|
+
else {
|
|
1141
|
+
this.operationsHelper.addGroupOperation("groupBy", { fields }, false);
|
|
1142
|
+
}
|
|
1143
|
+
return this;
|
|
1144
|
+
}
|
|
1145
|
+
/**
|
|
1146
|
+
* Groups documents using a raw MongoDB expression.
|
|
1147
|
+
* @param expression - The raw grouping expression
|
|
1148
|
+
* @param bindings - Optional parameter bindings
|
|
1149
|
+
*/
|
|
1150
|
+
groupByRaw(expression, bindings) {
|
|
1151
|
+
this.operationsHelper.addGroupOperation("groupByRaw", { expression, bindings }, false);
|
|
1152
|
+
return this;
|
|
1153
|
+
}
|
|
1154
|
+
having(...args) {
|
|
1155
|
+
if (args.length === 1) {
|
|
1156
|
+
this.operationsHelper.addMatchOperation("having:condition", args[0], false);
|
|
1157
|
+
}
|
|
1158
|
+
else if (args.length === 2) {
|
|
1159
|
+
this.operationsHelper.addMatchOperation("having", { field: args[0], operator: "=", value: args[1] }, false);
|
|
1160
|
+
}
|
|
1161
|
+
else {
|
|
1162
|
+
this.operationsHelper.addMatchOperation("having", { field: args[0], operator: args[1], value: args[2] }, false);
|
|
1163
|
+
}
|
|
1164
|
+
return this;
|
|
1165
|
+
}
|
|
1166
|
+
/**
|
|
1167
|
+
* Filters grouped results using a raw MongoDB expression.
|
|
1168
|
+
* @param expression - The raw having expression
|
|
1169
|
+
* @param bindings - Optional parameter bindings
|
|
1170
|
+
*/
|
|
1171
|
+
havingRaw(expression, bindings) {
|
|
1172
|
+
this.operationsHelper.addMatchOperation("havingRaw", { expression, bindings }, false);
|
|
1173
|
+
return this;
|
|
1174
|
+
}
|
|
1175
|
+
// ============================================================================
|
|
1176
|
+
// JOINS
|
|
1177
|
+
// ============================================================================
|
|
1178
|
+
/**
|
|
1179
|
+
* Performs a left outer join with another collection.
|
|
1180
|
+
* @param options - Join configuration including table, fields, and optional pipeline
|
|
1181
|
+
*/
|
|
1182
|
+
join(options) {
|
|
1183
|
+
this.operationsHelper.addLookupOperation("join", options);
|
|
1184
|
+
return this;
|
|
1185
|
+
}
|
|
1186
|
+
// ============================================================================
|
|
1187
|
+
// UTILITY / EXTENSIONS
|
|
1188
|
+
// ============================================================================
|
|
1189
|
+
/**
|
|
1190
|
+
* Allows direct manipulation of the native MongoDB query.
|
|
1191
|
+
* @param builder - Function that receives and modifies the native query
|
|
1192
|
+
*/
|
|
1193
|
+
raw(builder) {
|
|
1194
|
+
this.operationsHelper.addMatchOperation("raw", { builder }, false);
|
|
1195
|
+
return this;
|
|
1196
|
+
}
|
|
1197
|
+
/**
|
|
1198
|
+
* Extends the query builder with driver-specific functionality.
|
|
1199
|
+
* @param extension - The extension name
|
|
1200
|
+
* @param _args - Extension-specific arguments
|
|
1201
|
+
* @returns The extension's return value
|
|
1202
|
+
*/
|
|
1203
|
+
extend(extension, ..._args) {
|
|
1204
|
+
// Driver-specific extensions can be added here
|
|
1205
|
+
throw new Error(`Extension '${extension}' is not supported by MongoQueryBuilder`);
|
|
1206
|
+
}
|
|
1207
|
+
/**
|
|
1208
|
+
* Creates a deep copy of the query builder.
|
|
1209
|
+
* @returns A new query builder instance with copied operations
|
|
1210
|
+
*/
|
|
1211
|
+
clone() {
|
|
1212
|
+
const cloned = new MongoQueryBuilder(this.table, this.dataSource);
|
|
1213
|
+
cloned.operations = [...this.operations];
|
|
1214
|
+
cloned.hydrateCallback = this.hydrateCallback?.bind(cloned);
|
|
1215
|
+
cloned.fetchingCallback = this.fetchingCallback?.bind(cloned);
|
|
1216
|
+
cloned.hydratingCallback = this.hydratingCallback?.bind(cloned);
|
|
1217
|
+
cloned.fetchedCallback = this.fetchedCallback?.bind(cloned);
|
|
1218
|
+
// Copy scope state
|
|
1219
|
+
cloned.pendingGlobalScopes = this.pendingGlobalScopes;
|
|
1220
|
+
cloned.availableLocalScopes = this.availableLocalScopes;
|
|
1221
|
+
cloned.disabledGlobalScopes = new Set(this.disabledGlobalScopes);
|
|
1222
|
+
cloned.scopesApplied = this.scopesApplied;
|
|
1223
|
+
cloned.__operationsHelper = this.__operationsHelper;
|
|
1224
|
+
return cloned;
|
|
1225
|
+
}
|
|
1226
|
+
/**
|
|
1227
|
+
* Executes a callback with the query builder without breaking the chain.
|
|
1228
|
+
* @param callback - Function to execute with the builder
|
|
1229
|
+
*/
|
|
1230
|
+
tap(callback) {
|
|
1231
|
+
callback(this);
|
|
1232
|
+
return this;
|
|
1233
|
+
}
|
|
1234
|
+
/**
|
|
1235
|
+
* Conditionally applies query modifications based on a condition.
|
|
1236
|
+
* @param condition - The condition to evaluate
|
|
1237
|
+
* @param callback - Function to execute if condition is true
|
|
1238
|
+
* @param otherwise - Optional function to execute if condition is false
|
|
1239
|
+
*
|
|
1240
|
+
* @example
|
|
1241
|
+
* query.when(searchTerm, (q, term) => q.whereLike('name', term))
|
|
1242
|
+
*/
|
|
1243
|
+
when(condition, callback, otherwise) {
|
|
1244
|
+
if (condition) {
|
|
1245
|
+
callback(this, condition);
|
|
1246
|
+
}
|
|
1247
|
+
else if (otherwise) {
|
|
1248
|
+
otherwise(this);
|
|
1249
|
+
}
|
|
1250
|
+
return this;
|
|
1251
|
+
}
|
|
1252
|
+
// ============================================================================
|
|
1253
|
+
// EXECUTION METHODS
|
|
1254
|
+
// ============================================================================
|
|
1255
|
+
/**
|
|
1256
|
+
* Executes the query and returns all matching documents.
|
|
1257
|
+
* @returns an array of matching documents
|
|
1258
|
+
*/
|
|
1259
|
+
async get() {
|
|
1260
|
+
const startTime = Date.now();
|
|
1261
|
+
// Emit onFetching event
|
|
1262
|
+
if (this.fetchingCallback) {
|
|
1263
|
+
await this.fetchingCallback(this);
|
|
1264
|
+
}
|
|
1265
|
+
// Execute query and get raw records
|
|
1266
|
+
const rawRecords = await this.execute();
|
|
1267
|
+
// Emit onHydrating event
|
|
1268
|
+
if (this.hydratingCallback) {
|
|
1269
|
+
await this.hydratingCallback(rawRecords, {
|
|
1270
|
+
query: this,
|
|
1271
|
+
hydrateCallback: this.hydrateCallback,
|
|
1272
|
+
});
|
|
1273
|
+
}
|
|
1274
|
+
// Hydrate records
|
|
1275
|
+
const hydratedRecords = this.hydrateCallback
|
|
1276
|
+
? rawRecords.map(this.hydrateCallback)
|
|
1277
|
+
: rawRecords;
|
|
1278
|
+
// Emit onFetched event
|
|
1279
|
+
if (this.fetchedCallback) {
|
|
1280
|
+
await this.fetchedCallback(hydratedRecords, {
|
|
1281
|
+
query: this,
|
|
1282
|
+
rawRecords,
|
|
1283
|
+
duration: Date.now() - startTime,
|
|
1284
|
+
});
|
|
1285
|
+
}
|
|
1286
|
+
return hydratedRecords;
|
|
1287
|
+
}
|
|
1288
|
+
/**
|
|
1289
|
+
* Execute the query and get first result
|
|
1290
|
+
* This is different than `first` as first adds a `limit = 1` to the pipeline
|
|
1291
|
+
*/
|
|
1292
|
+
async getFirst() {
|
|
1293
|
+
return (await this.get())?.[0] ?? null;
|
|
1294
|
+
}
|
|
1295
|
+
/**
|
|
1296
|
+
* Executes the query and returns the first matching document.
|
|
1297
|
+
* @returns the first document or null
|
|
1298
|
+
*/
|
|
1299
|
+
async first() {
|
|
1300
|
+
const results = await this.limit(1).get();
|
|
1301
|
+
return results.length > 0 ? results[0] : null;
|
|
1302
|
+
}
|
|
1303
|
+
/**
|
|
1304
|
+
* Executes the query and returns the first matching document, throwing if none found.
|
|
1305
|
+
* @returns the first document
|
|
1306
|
+
*/
|
|
1307
|
+
async firstOrFail() {
|
|
1308
|
+
const result = await this.first();
|
|
1309
|
+
if (!result) {
|
|
1310
|
+
throw new Error("No records found matching the query");
|
|
1311
|
+
}
|
|
1312
|
+
return result;
|
|
1313
|
+
}
|
|
1314
|
+
/**
|
|
1315
|
+
* Configures the query to retrieve the last matching document.
|
|
1316
|
+
*/
|
|
1317
|
+
last(field = "createdAt") {
|
|
1318
|
+
this.orderBy(field, "desc");
|
|
1319
|
+
return this.first();
|
|
1320
|
+
}
|
|
1321
|
+
/**
|
|
1322
|
+
* Counts the number of documents matching the query.
|
|
1323
|
+
* @returns the count of matching documents
|
|
1324
|
+
*/
|
|
1325
|
+
async count() {
|
|
1326
|
+
const pipeline = this.buildPipeline();
|
|
1327
|
+
pipeline.push({ $count: "total" });
|
|
1328
|
+
const results = await this.execute(pipeline);
|
|
1329
|
+
return results.length > 0 ? results[0].total : 0;
|
|
1330
|
+
}
|
|
1331
|
+
/**
|
|
1332
|
+
* Calculates the sum of a numeric field across matching documents.
|
|
1333
|
+
* @param field - The numeric field to sum
|
|
1334
|
+
* @returns the sum value
|
|
1335
|
+
*/
|
|
1336
|
+
async sum(field) {
|
|
1337
|
+
this.groupByRaw({
|
|
1338
|
+
_id: null,
|
|
1339
|
+
total: { $sum: `$${field}` },
|
|
1340
|
+
});
|
|
1341
|
+
// make sure to clear the data map callback
|
|
1342
|
+
this.hydrateCallback = undefined;
|
|
1343
|
+
const result = await this.getFirst();
|
|
1344
|
+
return result?.total ?? 0;
|
|
1345
|
+
}
|
|
1346
|
+
/**
|
|
1347
|
+
* Calculates the average value of a numeric field across matching documents.
|
|
1348
|
+
* @param field - The numeric field to average
|
|
1349
|
+
* @returns the average value
|
|
1350
|
+
*/
|
|
1351
|
+
async avg(field) {
|
|
1352
|
+
this.groupByRaw({
|
|
1353
|
+
_id: null,
|
|
1354
|
+
average: { $avg: `$${field}` },
|
|
1355
|
+
});
|
|
1356
|
+
// make sure to clear the data map callback
|
|
1357
|
+
this.hydrateCallback = undefined;
|
|
1358
|
+
return (await this.getFirst())?.average ?? 0;
|
|
1359
|
+
}
|
|
1360
|
+
/**
|
|
1361
|
+
* Finds the minimum value of a field across matching documents.
|
|
1362
|
+
* @param field - The field to find the minimum of
|
|
1363
|
+
* @returns the minimum value
|
|
1364
|
+
*/
|
|
1365
|
+
async min(field) {
|
|
1366
|
+
this.groupByRaw({
|
|
1367
|
+
_id: null,
|
|
1368
|
+
minimum: { $min: `$${field}` },
|
|
1369
|
+
});
|
|
1370
|
+
// make sure to clear the data map callback
|
|
1371
|
+
this.hydrateCallback = undefined;
|
|
1372
|
+
return (await this.getFirst())?.minimum ?? 0;
|
|
1373
|
+
}
|
|
1374
|
+
/**
|
|
1375
|
+
* Finds the maximum value of a field across matching documents.
|
|
1376
|
+
* @param field - The field to find the maximum of
|
|
1377
|
+
* @returns the maximum value
|
|
1378
|
+
*/
|
|
1379
|
+
async max(field) {
|
|
1380
|
+
this.groupByRaw({
|
|
1381
|
+
_id: null,
|
|
1382
|
+
maximum: { $max: `$${field}` },
|
|
1383
|
+
});
|
|
1384
|
+
// make sure to clear the data map callback
|
|
1385
|
+
this.hydrateCallback = undefined;
|
|
1386
|
+
return (await this.getFirst())?.maximum ?? 0;
|
|
1387
|
+
}
|
|
1388
|
+
/**
|
|
1389
|
+
* Returns an array of distinct values for a field, respecting query filters.
|
|
1390
|
+
* @param field - The field to get distinct values from
|
|
1391
|
+
* @returns an array of distinct values
|
|
1392
|
+
*/
|
|
1393
|
+
async distinct(field, ignoreNull = true) {
|
|
1394
|
+
if (ignoreNull) {
|
|
1395
|
+
this.whereNotNull(field);
|
|
1396
|
+
}
|
|
1397
|
+
this.groupBy(field);
|
|
1398
|
+
// make sure to clear the data map callback
|
|
1399
|
+
this.hydrateCallback = undefined;
|
|
1400
|
+
const results = await this.get();
|
|
1401
|
+
return results.map((doc) => doc._id);
|
|
1402
|
+
}
|
|
1403
|
+
/**
|
|
1404
|
+
* Count distinct values for a field, respecting query filters.
|
|
1405
|
+
* @param field - The field to count distinct values for
|
|
1406
|
+
* @returns the count of distinct values
|
|
1407
|
+
*/
|
|
1408
|
+
async countDistinct(field, ignoreNull = true) {
|
|
1409
|
+
if (ignoreNull) {
|
|
1410
|
+
this.whereNotNull(field);
|
|
1411
|
+
}
|
|
1412
|
+
return await this.groupBy(field).count();
|
|
1413
|
+
}
|
|
1414
|
+
/**
|
|
1415
|
+
* Extracts a single field value from each matching document.
|
|
1416
|
+
* @param field - The field to extract
|
|
1417
|
+
* @returns an array of field values
|
|
1418
|
+
*/
|
|
1419
|
+
async pluck(field) {
|
|
1420
|
+
// make sure to clear the data map callback
|
|
1421
|
+
this.hydrateCallback = undefined;
|
|
1422
|
+
// Use object aliasing: { [field]: "value" } to project field as "value"
|
|
1423
|
+
const results = await this.selectAs(field, "value").get();
|
|
1424
|
+
return results.map((doc) => doc.value).filter((value) => value !== undefined);
|
|
1425
|
+
}
|
|
1426
|
+
/**
|
|
1427
|
+
* Gets the value of a single field from the first matching document.
|
|
1428
|
+
* @param field - The field to extract
|
|
1429
|
+
* @returns the field value or null
|
|
1430
|
+
*/
|
|
1431
|
+
async value(field) {
|
|
1432
|
+
// make sure to clear the data map callback
|
|
1433
|
+
this.hydrateCallback = undefined;
|
|
1434
|
+
const result = await this.selectAs(field, "value").first();
|
|
1435
|
+
return result?.value ?? null;
|
|
1436
|
+
}
|
|
1437
|
+
/**
|
|
1438
|
+
* Checks if any documents match the query.
|
|
1439
|
+
* @param filter - Optional filter to apply to the query
|
|
1440
|
+
* @returns true if documents exist, false otherwise
|
|
1441
|
+
*/
|
|
1442
|
+
async exists(filter) {
|
|
1443
|
+
if (filter) {
|
|
1444
|
+
this.where(filter);
|
|
1445
|
+
}
|
|
1446
|
+
const count = await this.limit(1).count();
|
|
1447
|
+
return count > 0;
|
|
1448
|
+
}
|
|
1449
|
+
/**
|
|
1450
|
+
* Checks if no documents match the query.
|
|
1451
|
+
* @param filter - Optional filter to apply to the query
|
|
1452
|
+
* @returns true if no documents exist, false otherwise
|
|
1453
|
+
*/
|
|
1454
|
+
async notExists(filter) {
|
|
1455
|
+
return !(await this.exists(filter));
|
|
1456
|
+
}
|
|
1457
|
+
/**
|
|
1458
|
+
* Increments a numeric field by the specified amount for first matching document.
|
|
1459
|
+
* @param field - The field to increment
|
|
1460
|
+
* @param amount - The amount to increment by (default: 1)
|
|
1461
|
+
* @returns the new value
|
|
1462
|
+
*/
|
|
1463
|
+
async increment(field, amount = 1) {
|
|
1464
|
+
const filter = this.buildFilter();
|
|
1465
|
+
const result = await this.collection.findOneAndUpdate(filter, {
|
|
1466
|
+
$inc: { [field]: amount },
|
|
1467
|
+
}, {
|
|
1468
|
+
returnDocument: "after",
|
|
1469
|
+
});
|
|
1470
|
+
return get(result, field, 0);
|
|
1471
|
+
}
|
|
1472
|
+
/**
|
|
1473
|
+
* Decrements a numeric field by the specified amount.
|
|
1474
|
+
* @param field - The field to decrement
|
|
1475
|
+
* @param amount - The amount to decrement by
|
|
1476
|
+
* @returns the new value
|
|
1477
|
+
*/
|
|
1478
|
+
async decrement(field, amount = 1) {
|
|
1479
|
+
return this.increment(field, -amount);
|
|
1480
|
+
}
|
|
1481
|
+
/**
|
|
1482
|
+
* Increments a numeric field by the specified amount for all matching documents.
|
|
1483
|
+
* @param field - The field to increment
|
|
1484
|
+
* @param amount - The amount to increment by (default: 1)
|
|
1485
|
+
* @returns the number of documents modified
|
|
1486
|
+
*/
|
|
1487
|
+
async incrementMany(field, amount = 1) {
|
|
1488
|
+
const filter = this.buildFilter();
|
|
1489
|
+
const result = await this.dataSource.driver.updateMany(this.table, filter, {
|
|
1490
|
+
$inc: { [field]: amount },
|
|
1491
|
+
});
|
|
1492
|
+
return result.modifiedCount;
|
|
1493
|
+
}
|
|
1494
|
+
/**
|
|
1495
|
+
* Decrements a numeric field by the specified amount for all matching documents.
|
|
1496
|
+
* @param field - The field to decrement
|
|
1497
|
+
* @param amount - The amount to decrement by (default: 1)
|
|
1498
|
+
* @returns the number of documents modified
|
|
1499
|
+
*/
|
|
1500
|
+
async decrementMany(field, amount = 1) {
|
|
1501
|
+
return this.incrementMany(field, -amount);
|
|
1502
|
+
}
|
|
1503
|
+
/**
|
|
1504
|
+
* Delete all documents matching the query.
|
|
1505
|
+
*/
|
|
1506
|
+
async delete() {
|
|
1507
|
+
const filter = this.buildFilter();
|
|
1508
|
+
return await this.dataSource.driver.deleteMany(this.table, filter);
|
|
1509
|
+
}
|
|
1510
|
+
/**
|
|
1511
|
+
* Delete a single document matching the query.
|
|
1512
|
+
*/
|
|
1513
|
+
async deleteOne() {
|
|
1514
|
+
const filter = this.buildFilter();
|
|
1515
|
+
return await this.dataSource.driver.delete(this.table, filter);
|
|
1516
|
+
}
|
|
1517
|
+
/**
|
|
1518
|
+
* Update the given fields for all documents matching the query.
|
|
1519
|
+
*/
|
|
1520
|
+
async update(fields) {
|
|
1521
|
+
const filter = this.buildFilter();
|
|
1522
|
+
const result = await this.dataSource.driver.updateMany(this.table, filter, {
|
|
1523
|
+
$set: fields,
|
|
1524
|
+
});
|
|
1525
|
+
return result.modifiedCount;
|
|
1526
|
+
}
|
|
1527
|
+
/**
|
|
1528
|
+
* Unset the given fields from all documents matching the query.
|
|
1529
|
+
*/
|
|
1530
|
+
async unset(...fields) {
|
|
1531
|
+
const filter = this.buildFilter();
|
|
1532
|
+
const result = await this.dataSource.driver.updateMany(this.table, filter, {
|
|
1533
|
+
$unset: fields.reduce((acc, field) => {
|
|
1534
|
+
acc[field] = 1;
|
|
1535
|
+
return acc;
|
|
1536
|
+
}, {}),
|
|
1537
|
+
});
|
|
1538
|
+
return result.modifiedCount;
|
|
1539
|
+
}
|
|
1540
|
+
// ============================================================================
|
|
1541
|
+
// CHUNKING / PAGINATION
|
|
1542
|
+
// ============================================================================
|
|
1543
|
+
/**
|
|
1544
|
+
* Processes query results in chunks, executing a callback for each chunk.
|
|
1545
|
+
* @param size - The number of documents per chunk
|
|
1546
|
+
* @param callback - Function to execute for each chunk
|
|
1547
|
+
* @returns void
|
|
1548
|
+
*/
|
|
1549
|
+
async chunk(size, callback) {
|
|
1550
|
+
let chunkIndex = 0;
|
|
1551
|
+
let hasMore = true;
|
|
1552
|
+
while (hasMore) {
|
|
1553
|
+
const chunk = await this.clone()
|
|
1554
|
+
.skip(chunkIndex * size)
|
|
1555
|
+
.limit(size)
|
|
1556
|
+
.get();
|
|
1557
|
+
if (chunk.length === 0) {
|
|
1558
|
+
break;
|
|
1559
|
+
}
|
|
1560
|
+
const shouldContinue = await callback(chunk, chunkIndex);
|
|
1561
|
+
if (shouldContinue === false) {
|
|
1562
|
+
break;
|
|
1563
|
+
}
|
|
1564
|
+
hasMore = chunk.length === size;
|
|
1565
|
+
chunkIndex++;
|
|
1566
|
+
}
|
|
1567
|
+
}
|
|
1568
|
+
/**
|
|
1569
|
+
* Executes the query with traditional page-based pagination.
|
|
1570
|
+
* @param options - Pagination options
|
|
1571
|
+
* @returns pagination result with data and metadata
|
|
1572
|
+
*/
|
|
1573
|
+
async paginate(options) {
|
|
1574
|
+
const page = options.page ?? 1;
|
|
1575
|
+
const limit = options.limit ?? 10;
|
|
1576
|
+
const skip = (page - 1) * limit;
|
|
1577
|
+
const [data, total] = await Promise.all([
|
|
1578
|
+
this.clone().skip(skip).limit(limit).get(),
|
|
1579
|
+
this.count(),
|
|
1580
|
+
]);
|
|
1581
|
+
return {
|
|
1582
|
+
data,
|
|
1583
|
+
pagination: {
|
|
1584
|
+
total,
|
|
1585
|
+
page,
|
|
1586
|
+
limit,
|
|
1587
|
+
pages: Math.ceil(total / limit),
|
|
1588
|
+
},
|
|
1589
|
+
};
|
|
1590
|
+
}
|
|
1591
|
+
/**
|
|
1592
|
+
* Executes the query with cursor-based pagination supporting both directions.
|
|
1593
|
+
* @param options - Cursor pagination options
|
|
1594
|
+
* @returns cursor pagination result with data and cursor info
|
|
1595
|
+
*/
|
|
1596
|
+
async cursorPaginate(options) {
|
|
1597
|
+
const limit = options.limit;
|
|
1598
|
+
const cursor = options.cursor;
|
|
1599
|
+
const column = options.column ?? "_id";
|
|
1600
|
+
const direction = options.direction ?? "next";
|
|
1601
|
+
// Apply cursor filter
|
|
1602
|
+
if (cursor) {
|
|
1603
|
+
const operator = direction === "next" ? ">" : "<";
|
|
1604
|
+
this.where(column, operator, cursor);
|
|
1605
|
+
}
|
|
1606
|
+
// Apply sorting
|
|
1607
|
+
const sortOrder = direction === "next" ? "asc" : "desc";
|
|
1608
|
+
this.orderBy(column, sortOrder);
|
|
1609
|
+
this.orderBy("_id", sortOrder); // Always sort by _id to ensure consistent order
|
|
1610
|
+
// Fetch one extra to detect if there are more results
|
|
1611
|
+
const results = await this.limit(limit + 1).get();
|
|
1612
|
+
const hasMore = results.length > limit;
|
|
1613
|
+
let data = hasMore ? results.slice(0, limit) : results;
|
|
1614
|
+
// Reverse results if fetching previous page to keep natural order
|
|
1615
|
+
if (direction === "prev") {
|
|
1616
|
+
data = data.reverse();
|
|
1617
|
+
}
|
|
1618
|
+
// Determine cursors based on actual data
|
|
1619
|
+
let nextCursor;
|
|
1620
|
+
let prevCursor;
|
|
1621
|
+
let hasPrev = false;
|
|
1622
|
+
if (data.length > 0) {
|
|
1623
|
+
const firstItem = data[0][column];
|
|
1624
|
+
const lastItem = data[data.length - 1][column];
|
|
1625
|
+
if (direction === "next") {
|
|
1626
|
+
// Forward pagination
|
|
1627
|
+
nextCursor = hasMore ? lastItem : undefined;
|
|
1628
|
+
// Check if there's a previous page
|
|
1629
|
+
if (cursor) {
|
|
1630
|
+
hasPrev = true;
|
|
1631
|
+
prevCursor = firstItem;
|
|
1632
|
+
}
|
|
1633
|
+
}
|
|
1634
|
+
else {
|
|
1635
|
+
// Backward pagination
|
|
1636
|
+
prevCursor = hasMore ? firstItem : undefined;
|
|
1637
|
+
hasPrev = hasMore;
|
|
1638
|
+
// Check if there's a next page
|
|
1639
|
+
if (cursor) {
|
|
1640
|
+
nextCursor = lastItem;
|
|
1641
|
+
}
|
|
1642
|
+
}
|
|
1643
|
+
}
|
|
1644
|
+
return {
|
|
1645
|
+
data,
|
|
1646
|
+
pagination: {
|
|
1647
|
+
hasMore,
|
|
1648
|
+
hasPrev,
|
|
1649
|
+
nextCursor,
|
|
1650
|
+
prevCursor,
|
|
1651
|
+
},
|
|
1652
|
+
};
|
|
1653
|
+
}
|
|
1654
|
+
// ============================================================================
|
|
1655
|
+
// INSPECTION / DEBUGGING
|
|
1656
|
+
// ============================================================================
|
|
1657
|
+
/**
|
|
1658
|
+
* Returns the MongoDB aggregation pipeline that will be executed.
|
|
1659
|
+
* @returns The MongoDB aggregation pipeline array
|
|
1660
|
+
*/
|
|
1661
|
+
parse() {
|
|
1662
|
+
return this.buildPipeline();
|
|
1663
|
+
}
|
|
1664
|
+
/**
|
|
1665
|
+
* Returns a formatted string representation of the query pipeline.
|
|
1666
|
+
* @returns A formatted string representation of the pipeline
|
|
1667
|
+
*/
|
|
1668
|
+
pretty() {
|
|
1669
|
+
return this.getParser().toPrettyString();
|
|
1670
|
+
}
|
|
1671
|
+
/**
|
|
1672
|
+
* Returns the MongoDB query execution plan.
|
|
1673
|
+
* @returns MongoDB's explain output
|
|
1674
|
+
*/
|
|
1675
|
+
async explain() {
|
|
1676
|
+
// TODO: Trigger the explaining event
|
|
1677
|
+
const pipeline = this.buildPipeline();
|
|
1678
|
+
const session = databaseTransactionContext.getSession();
|
|
1679
|
+
const options = session ? { session, explain: true } : { explain: true };
|
|
1680
|
+
return this.collection.aggregate(pipeline, options).toArray();
|
|
1681
|
+
}
|
|
1682
|
+
// ============================================================================
|
|
1683
|
+
// INTERNAL PIPELINE BUILDING
|
|
1684
|
+
// ============================================================================
|
|
1685
|
+
/**
|
|
1686
|
+
* Get query parser instance
|
|
1687
|
+
*/
|
|
1688
|
+
getParser() {
|
|
1689
|
+
this.applyPendingScopes();
|
|
1690
|
+
return new MongoQueryParser({
|
|
1691
|
+
collection: this.collection,
|
|
1692
|
+
operations: this.operations,
|
|
1693
|
+
createSubBuilder: () => new MongoQueryBuilder(this.table, this.dataSource),
|
|
1694
|
+
});
|
|
1695
|
+
}
|
|
1696
|
+
/**
|
|
1697
|
+
* Build the MongoDB aggregation pipeline from the operations list.
|
|
1698
|
+
* @returns The MongoDB aggregation pipeline
|
|
1699
|
+
*/
|
|
1700
|
+
buildPipeline() {
|
|
1701
|
+
const parser = this.getParser();
|
|
1702
|
+
return parser.parse();
|
|
1703
|
+
}
|
|
1704
|
+
/**
|
|
1705
|
+
* Build a MongoDB filter object from the query's where clauses.
|
|
1706
|
+
* Used for update operations like increment/decrement.
|
|
1707
|
+
* @returns The MongoDB filter object
|
|
1708
|
+
*/
|
|
1709
|
+
buildFilter() {
|
|
1710
|
+
// Get all match operations
|
|
1711
|
+
const matchOperations = this.operations.filter((op) => op.stage === "$match");
|
|
1712
|
+
if (matchOperations.length === 0) {
|
|
1713
|
+
return {}; // No filters, match all documents
|
|
1714
|
+
}
|
|
1715
|
+
// Build the pipeline and extract the first $match stage
|
|
1716
|
+
const pipeline = this.buildPipeline();
|
|
1717
|
+
const matchStage = pipeline.find((stage) => stage.$match);
|
|
1718
|
+
if (matchStage && matchStage.$match) {
|
|
1719
|
+
return matchStage.$match;
|
|
1720
|
+
}
|
|
1721
|
+
return {};
|
|
1722
|
+
}
|
|
1723
|
+
/**
|
|
1724
|
+
* Execute the aggregate command
|
|
1725
|
+
*/
|
|
1726
|
+
async execute(pipeline) {
|
|
1727
|
+
const aggregationPipeline = pipeline || this.buildPipeline();
|
|
1728
|
+
const session = databaseTransactionContext.getSession();
|
|
1729
|
+
const options = { session };
|
|
1730
|
+
// TODO: Trigger the executing event
|
|
1731
|
+
const results = (await this.collection
|
|
1732
|
+
.aggregate(aggregationPipeline, options)
|
|
1733
|
+
.toArray());
|
|
1734
|
+
// TODO: Trigger the fetched event
|
|
1735
|
+
// we need to cleanup the operations list
|
|
1736
|
+
this.operations = [];
|
|
1737
|
+
this.operationsHelper.setOperations(this.operations);
|
|
1738
|
+
return results;
|
|
1739
|
+
}
|
|
1740
|
+
}export{MongoQueryBuilder};//# sourceMappingURL=mongo-query-builder.js.map
|