@mikro-orm/knex 7.0.0-dev.6 → 7.0.0-dev.61

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.
Files changed (50) hide show
  1. package/AbstractSqlConnection.d.ts +11 -5
  2. package/AbstractSqlConnection.js +79 -32
  3. package/AbstractSqlDriver.d.ts +14 -10
  4. package/AbstractSqlDriver.js +273 -222
  5. package/AbstractSqlPlatform.js +3 -3
  6. package/PivotCollectionPersister.d.ts +3 -2
  7. package/PivotCollectionPersister.js +6 -2
  8. package/README.md +3 -2
  9. package/SqlEntityManager.d.ts +9 -2
  10. package/SqlEntityManager.js +2 -2
  11. package/dialects/mssql/MsSqlNativeQueryBuilder.d.ts +2 -0
  12. package/dialects/mssql/MsSqlNativeQueryBuilder.js +43 -2
  13. package/dialects/mysql/MySqlPlatform.js +2 -1
  14. package/dialects/postgresql/PostgreSqlTableCompiler.d.ts +1 -0
  15. package/dialects/postgresql/PostgreSqlTableCompiler.js +1 -0
  16. package/dialects/sqlite/BaseSqliteConnection.d.ts +4 -2
  17. package/dialects/sqlite/BaseSqliteConnection.js +8 -5
  18. package/dialects/sqlite/SqliteSchemaHelper.js +1 -1
  19. package/index.d.ts +1 -1
  20. package/index.js +1 -1
  21. package/package.json +5 -5
  22. package/query/ArrayCriteriaNode.d.ts +1 -0
  23. package/query/ArrayCriteriaNode.js +3 -0
  24. package/query/CriteriaNode.d.ts +4 -2
  25. package/query/CriteriaNode.js +11 -6
  26. package/query/CriteriaNodeFactory.js +11 -6
  27. package/query/NativeQueryBuilder.js +1 -1
  28. package/query/ObjectCriteriaNode.d.ts +1 -0
  29. package/query/ObjectCriteriaNode.js +38 -7
  30. package/query/QueryBuilder.d.ts +67 -6
  31. package/query/QueryBuilder.js +195 -43
  32. package/query/QueryBuilderHelper.d.ts +1 -1
  33. package/query/QueryBuilderHelper.js +13 -6
  34. package/query/ScalarCriteriaNode.d.ts +3 -3
  35. package/query/ScalarCriteriaNode.js +7 -5
  36. package/query/index.d.ts +1 -0
  37. package/query/index.js +1 -0
  38. package/query/raw.d.ts +59 -0
  39. package/query/raw.js +68 -0
  40. package/query/rawKnex.d.ts +58 -0
  41. package/query/rawKnex.js +72 -0
  42. package/schema/DatabaseSchema.js +25 -4
  43. package/schema/DatabaseTable.d.ts +5 -4
  44. package/schema/DatabaseTable.js +80 -34
  45. package/schema/SchemaComparator.js +2 -2
  46. package/schema/SchemaHelper.d.ts +2 -0
  47. package/schema/SchemaHelper.js +8 -4
  48. package/schema/SqlSchemaGenerator.d.ts +9 -2
  49. package/schema/SqlSchemaGenerator.js +31 -11
  50. package/typings.d.ts +86 -3
@@ -43,13 +43,13 @@ export class AbstractSqlPlatform extends Platform {
43
43
  return 'rollback';
44
44
  }
45
45
  getSavepointSQL(savepointName) {
46
- return `savepoint ${savepointName}`;
46
+ return `savepoint ${this.quoteIdentifier(savepointName)}`;
47
47
  }
48
48
  getRollbackToSavepointSQL(savepointName) {
49
- return `rollback to savepoint ${savepointName}`;
49
+ return `rollback to savepoint ${this.quoteIdentifier(savepointName)}`;
50
50
  }
51
51
  getReleaseSavepointSQL(savepointName) {
52
- return `release savepoint ${savepointName}`;
52
+ return `release savepoint ${this.quoteIdentifier(savepointName)}`;
53
53
  }
54
54
  quoteValue(value) {
55
55
  if (isRaw(value)) {
@@ -1,16 +1,17 @@
1
- import { type EntityMetadata, type EntityProperty, type Primary, type Transaction } from '@mikro-orm/core';
1
+ import { type Dictionary, type EntityMetadata, type EntityProperty, type Primary, type Transaction } from '@mikro-orm/core';
2
2
  import { type AbstractSqlDriver } from './AbstractSqlDriver.js';
3
3
  export declare class PivotCollectionPersister<Entity extends object> {
4
4
  private readonly meta;
5
5
  private readonly driver;
6
6
  private readonly ctx?;
7
7
  private readonly schema?;
8
+ private readonly loggerContext?;
8
9
  private readonly platform;
9
10
  private readonly inserts;
10
11
  private readonly deletes;
11
12
  private readonly batchSize;
12
13
  private order;
13
- constructor(meta: EntityMetadata<Entity>, driver: AbstractSqlDriver, ctx?: Transaction | undefined, schema?: string | undefined);
14
+ constructor(meta: EntityMetadata<Entity>, driver: AbstractSqlDriver, ctx?: Transaction | undefined, schema?: string | undefined, loggerContext?: Dictionary | undefined);
14
15
  enqueueUpdate(prop: EntityProperty<Entity>, insertDiff: Primary<Entity>[][], deleteDiff: Primary<Entity>[][] | boolean, pks: Primary<Entity>[]): void;
15
16
  private enqueueInsert;
16
17
  private enqueueDelete;
@@ -38,16 +38,18 @@ export class PivotCollectionPersister {
38
38
  driver;
39
39
  ctx;
40
40
  schema;
41
+ loggerContext;
41
42
  platform;
42
43
  inserts = new Map();
43
44
  deletes = new Map();
44
45
  batchSize;
45
46
  order = 0;
46
- constructor(meta, driver, ctx, schema) {
47
+ constructor(meta, driver, ctx, schema, loggerContext) {
47
48
  this.meta = meta;
48
49
  this.driver = driver;
49
50
  this.ctx = ctx;
50
51
  this.schema = schema;
52
+ this.loggerContext = loggerContext;
51
53
  this.platform = this.driver.getPlatform();
52
54
  this.batchSize = this.driver.config.get('batchSize');
53
55
  }
@@ -99,6 +101,7 @@ export class PivotCollectionPersister {
99
101
  await this.driver.nativeDelete(this.meta.className, cond, {
100
102
  ctx: this.ctx,
101
103
  schema: this.schema,
104
+ loggerContext: this.loggerContext,
102
105
  });
103
106
  }
104
107
  }
@@ -118,13 +121,14 @@ export class PivotCollectionPersister {
118
121
  schema: this.schema,
119
122
  convertCustomTypes: false,
120
123
  processCollections: false,
124
+ loggerContext: this.loggerContext,
121
125
  });
122
126
  }
123
127
  /* v8 ignore start */
124
128
  }
125
129
  else {
126
130
  await Utils.runSerial(items, item => {
127
- return this.driver.createQueryBuilder(this.meta.className, this.ctx, 'write')
131
+ return this.driver.createQueryBuilder(this.meta.className, this.ctx, 'write', false, this.loggerContext)
128
132
  .withSchema(this.schema)
129
133
  .insert(item)
130
134
  .execute('run', false);
package/README.md CHANGED
@@ -11,7 +11,6 @@ TypeScript ORM for Node.js based on Data Mapper, [Unit of Work](https://mikro-or
11
11
  [![Chat on discord](https://img.shields.io/discord/1214904142443839538?label=discord&color=blue)](https://discord.gg/w8bjxFHS7X)
12
12
  [![Downloads](https://img.shields.io/npm/dm/@mikro-orm/core.svg)](https://www.npmjs.com/package/@mikro-orm/core)
13
13
  [![Coverage Status](https://img.shields.io/coveralls/mikro-orm/mikro-orm.svg)](https://coveralls.io/r/mikro-orm/mikro-orm?branch=master)
14
- [![Maintainability](https://api.codeclimate.com/v1/badges/27999651d3adc47cfa40/maintainability)](https://codeclimate.com/github/mikro-orm/mikro-orm/maintainability)
15
14
  [![Build Status](https://github.com/mikro-orm/mikro-orm/workflows/tests/badge.svg?branch=master)](https://github.com/mikro-orm/mikro-orm/actions?workflow=tests)
16
15
 
17
16
  ## 🤔 Unit of What?
@@ -141,7 +140,7 @@ There is also auto-generated [CHANGELOG.md](CHANGELOG.md) file based on commit m
141
140
  - [Composite and Foreign Keys as Primary Key](https://mikro-orm.io/docs/composite-keys)
142
141
  - [Filters](https://mikro-orm.io/docs/filters)
143
142
  - [Using `QueryBuilder`](https://mikro-orm.io/docs/query-builder)
144
- - [Preloading Deeply Nested Structures via populate](https://mikro-orm.io/docs/nested-populate)
143
+ - [Populating relations](https://mikro-orm.io/docs/populating-relations)
145
144
  - [Property Validation](https://mikro-orm.io/docs/property-validation)
146
145
  - [Lifecycle Hooks](https://mikro-orm.io/docs/events#hooks)
147
146
  - [Vanilla JS Support](https://mikro-orm.io/docs/usage-with-js)
@@ -382,6 +381,8 @@ See also the list of contributors who [participated](https://github.com/mikro-or
382
381
 
383
382
  Please ⭐️ this repository if this project helped you!
384
383
 
384
+ > If you'd like to support my open-source work, consider sponsoring me directly at [github.com/sponsors/b4nan](https://github.com/sponsors/b4nan).
385
+
385
386
  ## 📝 License
386
387
 
387
388
  Copyright © 2018 [Martin Adámek](https://github.com/b4nan).
@@ -1,8 +1,13 @@
1
- import { EntityManager, type AnyEntity, type ConnectionType, type EntityData, type EntityName, type EntityRepository, type GetRepository, type QueryResult, type FilterQuery, type LoggingOptions, type RawQueryFragment } from '@mikro-orm/core';
1
+ import { type EntitySchemaWithMeta, EntityManager, type AnyEntity, type ConnectionType, type EntityData, type EntityName, type EntityRepository, type GetRepository, type QueryResult, type FilterQuery, type LoggingOptions, type RawQueryFragment } from '@mikro-orm/core';
2
2
  import type { AbstractSqlDriver } from './AbstractSqlDriver.js';
3
3
  import type { NativeQueryBuilder } from './query/NativeQueryBuilder.js';
4
4
  import type { QueryBuilder } from './query/QueryBuilder.js';
5
5
  import type { SqlEntityRepository } from './SqlEntityRepository.js';
6
+ import type { Kysely } from 'kysely';
7
+ import type { InferKyselyDB } from './typings.js';
8
+ export interface GetKyselyOptions {
9
+ type?: ConnectionType;
10
+ }
6
11
  /**
7
12
  * @inheritDoc
8
13
  */
@@ -18,8 +23,10 @@ export declare class SqlEntityManager<Driver extends AbstractSqlDriver = Abstrac
18
23
  /**
19
24
  * Returns configured Kysely instance.
20
25
  */
21
- getKysely(type?: ConnectionType): import("kysely").Kysely<any>;
26
+ getKysely<TDB = undefined, TOptions extends GetKyselyOptions = GetKyselyOptions>(options?: TOptions): Kysely<TDB extends undefined ? InferKyselyDB<EntitiesFromManager<this>, TOptions> : TDB>;
22
27
  execute<T extends QueryResult | EntityData<AnyEntity> | EntityData<AnyEntity>[] = EntityData<AnyEntity>[]>(query: string | NativeQueryBuilder | RawQueryFragment, params?: any[], method?: 'all' | 'get' | 'run', loggerContext?: LoggingOptions): Promise<T>;
23
28
  getRepository<T extends object, U extends EntityRepository<T> = SqlEntityRepository<T>>(entityName: EntityName<T>): GetRepository<T, U>;
24
29
  protected applyDiscriminatorCondition<Entity extends object>(entityName: string, where: FilterQuery<Entity>): FilterQuery<Entity>;
25
30
  }
31
+ type EntitiesFromManager<TEntityManager extends EntityManager<any>> = NonNullable<TEntityManager['~entities']> extends any[] ? (Extract<NonNullable<TEntityManager['~entities']>[number], EntitySchemaWithMeta>) : never;
32
+ export {};
@@ -19,8 +19,8 @@ export class SqlEntityManager extends EntityManager {
19
19
  /**
20
20
  * Returns configured Kysely instance.
21
21
  */
22
- getKysely(type) {
23
- return this.getConnection(type).getClient();
22
+ getKysely(options = {}) {
23
+ return this.getConnection(options.type).getClient();
24
24
  }
25
25
  async execute(query, params = [], method = 'all', loggerContext) {
26
26
  return this.getDriver().execute(query, params, method, this.getContext(false).getTransactionContext(), loggerContext);
@@ -5,6 +5,8 @@ export declare class MsSqlNativeQueryBuilder extends NativeQueryBuilder {
5
5
  sql: string;
6
6
  params: unknown[];
7
7
  };
8
+ protected compileInsert(): void;
9
+ private appendOutputTable;
8
10
  private compileUpsert;
9
11
  protected compileSelect(): void;
10
12
  protected addLockClause(): void;
@@ -12,6 +12,10 @@ export class MsSqlNativeQueryBuilder extends NativeQueryBuilder {
12
12
  if (this.options.flags?.has(QueryFlag.IDENTITY_INSERT)) {
13
13
  this.parts.push(`set identity_insert ${this.getTableName()} on;`);
14
14
  }
15
+ const { prefix, suffix } = this.appendOutputTable();
16
+ if (prefix) {
17
+ this.parts.push(prefix);
18
+ }
15
19
  if (this.options.comment) {
16
20
  this.parts.push(...this.options.comment.map(comment => `/* ${comment} */`));
17
21
  }
@@ -37,7 +41,11 @@ export class MsSqlNativeQueryBuilder extends NativeQueryBuilder {
37
41
  this.compileTruncate();
38
42
  break;
39
43
  }
40
- if ([QueryType.INSERT, QueryType.UPDATE, QueryType.DELETE].includes(this.type)) {
44
+ if (suffix) {
45
+ this.parts[this.parts.length - 1] += ';';
46
+ this.parts.push(suffix);
47
+ }
48
+ else if ([QueryType.INSERT, QueryType.UPDATE, QueryType.DELETE].includes(this.type)) {
41
49
  this.parts[this.parts.length - 1] += '; select @@rowcount;';
42
50
  }
43
51
  }
@@ -46,6 +54,37 @@ export class MsSqlNativeQueryBuilder extends NativeQueryBuilder {
46
54
  }
47
55
  return this.combineParts();
48
56
  }
57
+ compileInsert() {
58
+ if (!this.options.data) {
59
+ throw new Error('No data provided');
60
+ }
61
+ this.parts.push('insert');
62
+ this.addHintComment();
63
+ this.parts.push(`into ${this.getTableName()}`);
64
+ if (Object.keys(this.options.data).length === 0) {
65
+ this.addOutputClause('inserted');
66
+ this.parts.push('default values');
67
+ return;
68
+ }
69
+ const parts = this.processInsertData();
70
+ if (this.options.flags?.has(QueryFlag.OUTPUT_TABLE)) {
71
+ this.parts[this.parts.length - 2] += ' into #out ';
72
+ }
73
+ this.parts.push(parts.join(', '));
74
+ }
75
+ appendOutputTable() {
76
+ if (!this.options.flags?.has(QueryFlag.OUTPUT_TABLE)) {
77
+ return { prefix: '', suffix: '' };
78
+ }
79
+ const returningFields = this.options.returning;
80
+ const selections = returningFields
81
+ .map(field => `[t].${this.platform.quoteIdentifier(field)}`)
82
+ .join(',');
83
+ return {
84
+ prefix: `select top(0) ${selections} into #out from ${this.getTableName()} as t left join ${this.getTableName()} on 0 = 1;`,
85
+ suffix: `select ${selections} from #out as t; drop table #out`,
86
+ };
87
+ }
49
88
  compileUpsert() {
50
89
  const clause = this.options.onConflict;
51
90
  const dataAsArray = Utils.asArray(this.options.data);
@@ -82,7 +121,9 @@ export class MsSqlNativeQueryBuilder extends NativeQueryBuilder {
82
121
  }
83
122
  this.parts.push('then update set');
84
123
  if (!clause.merge || Array.isArray(clause.merge)) {
85
- const parts = keys.map((column) => `${this.quote(column)} = tsource.${this.quote(column)}`);
124
+ const parts = (clause.merge || keys)
125
+ .filter(field => !Array.isArray(clause.fields) || !clause.fields.includes(field))
126
+ .map((column) => `${this.quote(column)} = tsource.${this.quote(column)}`);
86
127
  this.parts.push(parts.join(', '));
87
128
  }
88
129
  else if (typeof clause.merge === 'object') {
@@ -81,7 +81,8 @@ export class MySqlPlatform extends AbstractSqlPlatform {
81
81
  }
82
82
  const indexName = super.getIndexName(tableName, columns, type);
83
83
  if (indexName.length > 64) {
84
- return `${indexName.substring(0, 56 - type.length)}_${Utils.hash(indexName, 5)}_${type}`;
84
+ const hashAlgorithm = this.config.get('hashAlgorithm');
85
+ return `${indexName.substring(0, 56 - type.length)}_${Utils.hash(indexName, 5, hashAlgorithm)}_${type}`;
85
86
  }
86
87
  return indexName;
87
88
  }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -1,5 +1,7 @@
1
1
  import { AbstractSqlConnection } from '../../AbstractSqlConnection.js';
2
2
  export declare abstract class BaseSqliteConnection extends AbstractSqlConnection {
3
- connect(): Promise<void>;
4
- getClientUrl(): string;
3
+ connect(options?: {
4
+ skipOnConnect?: boolean;
5
+ simple?: boolean;
6
+ }): Promise<void>;
5
7
  }
@@ -3,12 +3,15 @@ import { CompiledQuery } from 'kysely';
3
3
  import { Utils } from '@mikro-orm/core';
4
4
  import { AbstractSqlConnection } from '../../AbstractSqlConnection.js';
5
5
  export class BaseSqliteConnection extends AbstractSqlConnection {
6
- async connect() {
6
+ async connect(options) {
7
7
  await super.connect();
8
- Utils.ensureDir(dirname(this.config.get('dbName')));
8
+ if (options?.simple) {
9
+ return;
10
+ }
11
+ const dbName = this.config.get('dbName');
12
+ if (dbName && dbName !== ':memory:') {
13
+ Utils.ensureDir(dirname(this.config.get('dbName')));
14
+ }
9
15
  await this.client.executeQuery(CompiledQuery.raw('pragma foreign_keys = on'));
10
16
  }
11
- getClientUrl() {
12
- return '';
13
- }
14
17
  }
@@ -167,7 +167,7 @@ export class SqliteSchemaHelper extends SchemaHelper {
167
167
  if (col.hidden > 1) {
168
168
  /* v8 ignore next */
169
169
  const storage = col.hidden === 2 ? 'virtual' : 'stored';
170
- const re = `(generated always)? as \\((.*)\\)( ${storage})?$`;
170
+ const re = new RegExp(`(generated always)? as \\((.*)\\)( ${storage})?$`, 'i');
171
171
  const match = columnDefinitions[col.name].definition.match(re);
172
172
  if (match) {
173
173
  generated = `${match[2]} ${storage}`;
package/index.d.ts CHANGED
@@ -2,7 +2,6 @@
2
2
  * @packageDocumentation
3
3
  * @module knex
4
4
  */
5
- /** @ignore */
6
5
  export { Kysely } from 'kysely';
7
6
  export * from '@mikro-orm/core';
8
7
  export * from './AbstractSqlConnection.js';
@@ -11,6 +10,7 @@ export * from './AbstractSqlPlatform.js';
11
10
  export * from './SqlEntityManager.js';
12
11
  export * from './SqlEntityRepository.js';
13
12
  export * from './query/index.js';
13
+ export { raw } from './query/index.js';
14
14
  export * from './schema/index.js';
15
15
  export * from './dialects/index.js';
16
16
  export * from './typings.js';
package/index.js CHANGED
@@ -2,7 +2,6 @@
2
2
  * @packageDocumentation
3
3
  * @module knex
4
4
  */
5
- /** @ignore */
6
5
  export { Kysely } from 'kysely';
7
6
  export * from '@mikro-orm/core';
8
7
  export * from './AbstractSqlConnection.js';
@@ -11,6 +10,7 @@ export * from './AbstractSqlPlatform.js';
11
10
  export * from './SqlEntityManager.js';
12
11
  export * from './SqlEntityRepository.js';
13
12
  export * from './query/index.js';
13
+ export { raw } from './query/index.js';
14
14
  export * from './schema/index.js';
15
15
  export * from './dialects/index.js';
16
16
  export * from './typings.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mikro-orm/knex",
3
- "version": "7.0.0-dev.6",
3
+ "version": "7.0.0-dev.61",
4
4
  "description": "TypeScript ORM for Node.js based on Data Mapper, Unit of Work and Identity Map patterns. Supports MongoDB, MySQL, PostgreSQL and SQLite databases as well as usage with vanilla JavaScript.",
5
5
  "type": "module",
6
6
  "exports": {
@@ -38,7 +38,7 @@
38
38
  },
39
39
  "homepage": "https://mikro-orm.io",
40
40
  "engines": {
41
- "node": ">= 22.11.0"
41
+ "node": ">= 22.17.0"
42
42
  },
43
43
  "scripts": {
44
44
  "build": "yarn clean && yarn compile && yarn copy",
@@ -50,13 +50,13 @@
50
50
  "access": "public"
51
51
  },
52
52
  "dependencies": {
53
- "kysely": "https://pkg.pr.new/kysely-org/kysely/kysely@2b7007e",
53
+ "kysely": "0.28.8",
54
54
  "sqlstring": "2.3.3"
55
55
  },
56
56
  "devDependencies": {
57
- "@mikro-orm/core": "^6.4.7"
57
+ "@mikro-orm/core": "^6.6.1"
58
58
  },
59
59
  "peerDependencies": {
60
- "@mikro-orm/core": "7.0.0-dev.6"
60
+ "@mikro-orm/core": "7.0.0-dev.61"
61
61
  }
62
62
  }
@@ -7,4 +7,5 @@ export declare class ArrayCriteriaNode<T extends object> extends CriteriaNode<T>
7
7
  process(qb: IQueryBuilder<T>, options?: ICriteriaNodeProcessOptions): any;
8
8
  unwrap(): any;
9
9
  willAutoJoin(qb: IQueryBuilder<T>, alias?: string, options?: ICriteriaNodeProcessOptions): any;
10
+ isStrict(): boolean;
10
11
  }
@@ -18,4 +18,7 @@ export class ArrayCriteriaNode extends CriteriaNode {
18
18
  return node.willAutoJoin(qb, alias, options);
19
19
  });
20
20
  }
21
+ isStrict() {
22
+ return this.strict || this.payload.some((node) => node.isStrict());
23
+ }
21
24
  }
@@ -11,20 +11,22 @@ export declare class CriteriaNode<T extends object> implements ICriteriaNode<T>
11
11
  readonly entityName: string;
12
12
  readonly parent?: ICriteriaNode<T> | undefined;
13
13
  readonly key?: EntityKey<T> | undefined;
14
+ readonly strict: boolean;
14
15
  payload: any;
15
16
  prop?: EntityProperty<T>;
16
17
  index?: number;
17
- constructor(metadata: MetadataStorage, entityName: string, parent?: ICriteriaNode<T> | undefined, key?: EntityKey<T> | undefined, validate?: boolean);
18
+ constructor(metadata: MetadataStorage, entityName: string, parent?: ICriteriaNode<T> | undefined, key?: EntityKey<T> | undefined, validate?: boolean, strict?: boolean);
18
19
  process(qb: IQueryBuilder<T>, options?: ICriteriaNodeProcessOptions): any;
19
20
  unwrap(): any;
20
21
  shouldInline(payload: any): boolean;
21
22
  willAutoJoin(qb: IQueryBuilder<T>, alias?: string, options?: ICriteriaNodeProcessOptions): boolean;
22
23
  shouldRename(payload: any): boolean;
23
- renameFieldToPK<T>(qb: IQueryBuilder<T>): string;
24
+ renameFieldToPK<T>(qb: IQueryBuilder<T>, ownerAlias?: string): string;
24
25
  getPath(addIndex?: boolean): string;
25
26
  private isPivotJoin;
26
27
  getPivotPath(path: string): string;
27
28
  aliased(field: string, alias?: string): string;
29
+ isStrict(): boolean;
28
30
  /** @ignore */
29
31
  [inspect.custom](): string;
30
32
  }
@@ -10,14 +10,16 @@ export class CriteriaNode {
10
10
  entityName;
11
11
  parent;
12
12
  key;
13
+ strict;
13
14
  payload;
14
15
  prop;
15
16
  index;
16
- constructor(metadata, entityName, parent, key, validate = true) {
17
+ constructor(metadata, entityName, parent, key, validate = true, strict = false) {
17
18
  this.metadata = metadata;
18
19
  this.entityName = entityName;
19
20
  this.parent = parent;
20
21
  this.key = key;
22
+ this.strict = strict;
21
23
  const meta = parent && metadata.find(parent.entityName);
22
24
  if (meta && key) {
23
25
  const pks = Utils.splitPrimaryKeys(key);
@@ -65,13 +67,13 @@ export class CriteriaNode {
65
67
  default: return false;
66
68
  }
67
69
  }
68
- renameFieldToPK(qb) {
69
- let joinAlias = qb.getAliasForJoinPath(this.getPath());
70
+ renameFieldToPK(qb, ownerAlias) {
71
+ const joinAlias = qb.getAliasForJoinPath(this.getPath(), { matchPopulateJoins: true });
70
72
  if (!joinAlias && this.parent && [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(this.prop.kind) && this.prop.owner) {
71
- joinAlias = qb.getAliasForJoinPath(this.parent.getPath());
72
- return Utils.getPrimaryKeyHash(this.prop.ownColumns.map(col => `${joinAlias ?? qb.alias}.${col}`));
73
+ const alias = qb.getAliasForJoinPath(this.parent.getPath()) ?? ownerAlias ?? qb.alias;
74
+ return Utils.getPrimaryKeyHash(this.prop.joinColumns.map(col => `${alias}.${col}`));
73
75
  }
74
- const alias = joinAlias ?? qb.alias;
76
+ const alias = joinAlias ?? ownerAlias ?? qb.alias;
75
77
  if (this.prop.kind === ReferenceKind.MANY_TO_MANY) {
76
78
  return Utils.getPrimaryKeyHash(this.prop.inverseJoinColumns.map(col => `${alias}.${col}`));
77
79
  }
@@ -106,6 +108,9 @@ export class CriteriaNode {
106
108
  aliased(field, alias) {
107
109
  return alias ? `${alias}.${field}` : field;
108
110
  }
111
+ isStrict() {
112
+ return this.strict;
113
+ }
109
114
  /** @ignore */
110
115
  [inspect.custom]() {
111
116
  const o = {};
@@ -36,7 +36,7 @@ export class CriteriaNodeFactory {
36
36
  }
37
37
  static createObjectNode(metadata, entityName, payload, parent, key) {
38
38
  const meta = metadata.find(entityName);
39
- const node = new ObjectCriteriaNode(metadata, entityName, parent, key);
39
+ const node = new ObjectCriteriaNode(metadata, entityName, parent, key, true, payload.__strict);
40
40
  node.payload = {};
41
41
  for (const key of Object.keys(payload)) {
42
42
  node.payload[key] = this.createObjectItemNode(metadata, entityName, node, payload, key, meta);
@@ -46,10 +46,14 @@ export class CriteriaNodeFactory {
46
46
  static createObjectItemNode(metadata, entityName, node, payload, key, meta) {
47
47
  const prop = meta?.properties[key];
48
48
  const childEntity = prop && prop.kind !== ReferenceKind.SCALAR ? prop.type : entityName;
49
- if (prop?.customType instanceof JsonType) {
49
+ const isNotEmbedded = prop?.kind !== ReferenceKind.EMBEDDED;
50
+ if (isNotEmbedded && prop?.customType instanceof JsonType) {
50
51
  return this.createScalarNode(metadata, childEntity, payload[key], node, key);
51
52
  }
52
- if (prop?.kind !== ReferenceKind.EMBEDDED) {
53
+ if (prop?.kind === ReferenceKind.SCALAR && payload[key] != null && Object.keys(payload[key]).some(f => Utils.isGroupOperator(f))) {
54
+ throw ValidationError.cannotUseGroupOperatorsInsideScalars(entityName, prop.name, payload);
55
+ }
56
+ if (isNotEmbedded) {
53
57
  return this.createNode(metadata, childEntity, payload[key], node, key);
54
58
  }
55
59
  if (payload[key] == null) {
@@ -66,11 +70,12 @@ export class CriteriaNodeFactory {
66
70
  throw ValidationError.cannotUseOperatorsInsideEmbeddables(entityName, prop.name, payload);
67
71
  }
68
72
  const map = Object.keys(payload[key]).reduce((oo, k) => {
69
- if (!prop.embeddedProps[k] && !allowedOperators.includes(k)) {
73
+ const embeddedProp = prop.embeddedProps[k] ?? Object.values(prop.embeddedProps).find(p => p.name === k);
74
+ if (!embeddedProp && !allowedOperators.includes(k)) {
70
75
  throw ValidationError.invalidEmbeddableQuery(entityName, k, prop.type);
71
76
  }
72
- if (prop.embeddedProps[k]) {
73
- oo[prop.embeddedProps[k].name] = payload[key][k];
77
+ if (embeddedProp) {
78
+ oo[embeddedProp.name] = payload[key][k];
74
79
  }
75
80
  else if (typeof payload[key][k] === 'object') {
76
81
  oo[k] = JSON.stringify(payload[key][k]);
@@ -261,7 +261,7 @@ export class NativeQueryBuilder {
261
261
  }
262
262
  if (this.options.where?.sql.trim()) {
263
263
  this.parts.push(`where ${this.options.where.sql}`);
264
- this.params.push(...this.options.where.params);
264
+ this.options.where.params.forEach(p => this.params.push(p));
265
265
  }
266
266
  if (this.options.groupBy) {
267
267
  const fields = this.options.groupBy.map(field => this.quote(field));
@@ -5,6 +5,7 @@ import type { ICriteriaNodeProcessOptions, IQueryBuilder } from '../typings.js';
5
5
  */
6
6
  export declare class ObjectCriteriaNode<T extends object> extends CriteriaNode<T> {
7
7
  process(qb: IQueryBuilder<T>, options?: ICriteriaNodeProcessOptions): any;
8
+ isStrict(): boolean;
8
9
  unwrap(): any;
9
10
  willAutoJoin(qb: IQueryBuilder<T>, alias?: string, options?: ICriteriaNodeProcessOptions): boolean;
10
11
  shouldInline(payload: any): boolean;
@@ -6,7 +6,8 @@ import { JoinType, QueryType } from './enums.js';
6
6
  */
7
7
  export class ObjectCriteriaNode extends CriteriaNode {
8
8
  process(qb, options) {
9
- const nestedAlias = qb.getAliasForJoinPath(this.getPath(), options);
9
+ const matchPopulateJoins = options?.matchPopulateJoins || (this.prop && [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(this.prop.kind));
10
+ const nestedAlias = qb.getAliasForJoinPath(this.getPath(), { ...options, matchPopulateJoins });
10
11
  const ownerAlias = options?.alias || qb.alias;
11
12
  const keys = Object.keys(this.payload);
12
13
  let alias = options?.alias;
@@ -55,7 +56,23 @@ export class ObjectCriteriaNode extends CriteriaNode {
55
56
  }
56
57
  return { $and };
57
58
  }
58
- alias = this.autoJoin(qb, ownerAlias);
59
+ alias = this.autoJoin(qb, ownerAlias, options);
60
+ }
61
+ if (this.prop && nestedAlias) {
62
+ const toOneProperty = [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(this.prop.kind);
63
+ // if the property is nullable and the filter is strict, we need to use left join, so we mimic the inner join behaviour
64
+ // with an exclusive condition on the join columns:
65
+ // - if the owning column is null, the row is missing, we don't apply the filter
66
+ // - if the target column is not null, the row is matched, we apply the filter
67
+ if (toOneProperty && this.prop.nullable && this.isStrict()) {
68
+ const key = this.prop.owner ? this.prop.name : this.prop.referencedPKs;
69
+ qb.andWhere({
70
+ $or: [
71
+ { [ownerAlias + '.' + key]: null },
72
+ { [nestedAlias + '.' + Utils.getPrimaryKeyHash(this.prop.referencedPKs)]: { $ne: null } },
73
+ ],
74
+ });
75
+ }
59
76
  }
60
77
  return keys.reduce((o, field) => {
61
78
  const childNode = this.payload[field];
@@ -66,13 +83,14 @@ export class ObjectCriteriaNode extends CriteriaNode {
66
83
  const virtual = childNode.prop?.persist === false && !childNode.prop?.formula;
67
84
  // if key is missing, we are inside group operator and we need to prefix with alias
68
85
  const primaryKey = this.key && this.metadata.find(this.entityName)?.primaryKeys.includes(field);
86
+ const isToOne = childNode.prop && [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(childNode.prop.kind);
69
87
  if (childNode.shouldInline(payload)) {
70
- const childAlias = qb.getAliasForJoinPath(childNode.getPath(), options);
88
+ const childAlias = qb.getAliasForJoinPath(childNode.getPath(), { preferNoBranch: isToOne, ...options });
71
89
  const a = qb.helper.isTableNameAliasRequired(qb.type) ? alias : undefined;
72
90
  this.inlineChildPayload(o, payload, field, a, childAlias);
73
91
  }
74
92
  else if (childNode.shouldRename(payload)) {
75
- this.inlineCondition(childNode.renameFieldToPK(qb), o, payload);
93
+ this.inlineCondition(childNode.renameFieldToPK(qb, alias), o, payload);
76
94
  }
77
95
  else if (isRawField) {
78
96
  const rawField = RawQueryFragment.getKnownFragment(field);
@@ -87,6 +105,11 @@ export class ObjectCriteriaNode extends CriteriaNode {
87
105
  return o;
88
106
  }, {});
89
107
  }
108
+ isStrict() {
109
+ return this.strict || Object.keys(this.payload).some(key => {
110
+ return this.payload[key].isStrict();
111
+ });
112
+ }
90
113
  unwrap() {
91
114
  return Object.keys(this.payload).reduce((o, field) => {
92
115
  o[field] = this.payload[field].unwrap();
@@ -193,23 +216,31 @@ export class ObjectCriteriaNode extends CriteriaNode {
193
216
  });
194
217
  return !primaryKeys && !nestedAlias && !operatorKeys && !embeddable;
195
218
  }
196
- autoJoin(qb, alias) {
219
+ autoJoin(qb, alias, options) {
197
220
  const nestedAlias = qb.getNextAlias(this.prop?.pivotTable ?? this.entityName);
198
221
  const customExpression = RawQueryFragment.isKnownFragment(this.key);
199
222
  const scalar = Utils.isPrimaryKey(this.payload) || this.payload instanceof RegExp || this.payload instanceof Date || customExpression;
200
223
  const operator = Utils.isPlainObject(this.payload) && Object.keys(this.payload).every(k => Utils.isOperator(k, false));
201
224
  const field = `${alias}.${this.prop.name}`;
202
225
  const method = qb.hasFlag(QueryFlag.INFER_POPULATE) ? 'joinAndSelect' : 'join';
226
+ const path = this.getPath();
203
227
  if (this.prop.kind === ReferenceKind.MANY_TO_MANY && (scalar || operator)) {
204
- qb.join(field, nestedAlias, undefined, JoinType.pivotJoin, this.getPath());
228
+ qb.join(field, nestedAlias, undefined, JoinType.pivotJoin, path);
205
229
  }
206
230
  else {
207
231
  const prev = qb._fields?.slice();
208
- qb[method](field, nestedAlias, undefined, JoinType.leftJoin, this.getPath());
232
+ const toOneProperty = [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(this.prop.kind);
233
+ const joinType = toOneProperty && !this.prop.nullable
234
+ ? JoinType.innerJoin
235
+ : JoinType.leftJoin;
236
+ qb[method](field, nestedAlias, undefined, joinType, path);
209
237
  if (!qb.hasFlag(QueryFlag.INFER_POPULATE)) {
210
238
  qb._fields = prev;
211
239
  }
212
240
  }
241
+ if (options?.type !== 'orderBy') {
242
+ qb.scheduleFilterCheck(path);
243
+ }
213
244
  return nestedAlias;
214
245
  }
215
246
  isPrefixed(field) {