@mikro-orm/sql 7.2.0-dev.17 → 7.2.0-dev.19

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.
@@ -1346,7 +1346,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
1346
1346
  ],
1347
1347
  populateWhere: undefined,
1348
1348
  _populateWhere: 'infer',
1349
- populateFilter: this.wrapPopulateFilter(options, pivotProp2.name),
1349
+ populateFilter: this.wrapPopulateFilter(options, pivotProp1.name),
1350
1350
  };
1351
1351
  if (pivotFindOptions._partitionLimit) {
1352
1352
  pivotFindOptions._partitionLimit.partitionBy = pivotProp2.name;
@@ -14,6 +14,8 @@ export declare class BaseMySqlPlatform extends AbstractSqlPlatform {
14
14
  readonly "desc nulls first": 'is not null';
15
15
  readonly "desc nulls last": 'is null';
16
16
  };
17
+ /** mysql and mariadb treat null as the lowest value when no placement is requested. */
18
+ sortsNullsLowest(): boolean;
17
19
  supportsMultiColumnCountDistinct(): boolean;
18
20
  /** @internal */
19
21
  createNativeQueryBuilder(): MySqlNativeQueryBuilder;
@@ -18,6 +18,10 @@ export class BaseMySqlPlatform extends AbstractSqlPlatform {
18
18
  [QueryOrder.desc_nulls_first]: 'is not null',
19
19
  [QueryOrder.desc_nulls_last]: 'is null',
20
20
  };
21
+ /** mysql and mariadb treat null as the lowest value when no placement is requested. */
22
+ sortsNullsLowest() {
23
+ return true;
24
+ }
21
25
  supportsMultiColumnCountDistinct() {
22
26
  return true;
23
27
  }
@@ -6,6 +6,8 @@ import { SqliteExceptionConverter } from './SqliteExceptionConverter.js';
6
6
  export declare class SqlitePlatform extends AbstractSqlPlatform {
7
7
  protected readonly schemaHelper: SqliteSchemaHelper;
8
8
  protected readonly exceptionConverter: SqliteExceptionConverter;
9
+ /** sqlite treats null as the lowest value when no placement is requested. */
10
+ sortsNullsLowest(): boolean;
9
11
  /** @internal */
10
12
  createNativeQueryBuilder(): SqliteNativeQueryBuilder;
11
13
  usesDefaultKeyword(): boolean;
@@ -5,6 +5,10 @@ import { SqliteExceptionConverter } from './SqliteExceptionConverter.js';
5
5
  export class SqlitePlatform extends AbstractSqlPlatform {
6
6
  schemaHelper = new SqliteSchemaHelper(this);
7
7
  exceptionConverter = new SqliteExceptionConverter();
8
+ /** sqlite treats null as the lowest value when no placement is requested. */
9
+ sortsNullsLowest() {
10
+ return true;
11
+ }
8
12
  /** @internal */
9
13
  createNativeQueryBuilder() {
10
14
  return new SqliteNativeQueryBuilder(this);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mikro-orm/sql",
3
- "version": "7.2.0-dev.17",
3
+ "version": "7.2.0-dev.19",
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
  "keywords": [
6
6
  "data-mapper",
@@ -53,7 +53,7 @@
53
53
  "@mikro-orm/core": "^7.1.15"
54
54
  },
55
55
  "peerDependencies": {
56
- "@mikro-orm/core": "7.2.0-dev.17"
56
+ "@mikro-orm/core": "7.2.0-dev.19"
57
57
  },
58
58
  "engines": {
59
59
  "node": ">= 22.17.0"
@@ -13,6 +13,7 @@ export declare class ObjectCriteriaNode<T extends object> extends CriteriaNode<T
13
13
  private inlineArrayChildPayload;
14
14
  private inlineChildPayload;
15
15
  private inlineCondition;
16
+ private isCollectionOperator;
16
17
  private shouldAutoJoin;
17
18
  private autoJoin;
18
19
  private isPrefixed;
@@ -1,7 +1,7 @@
1
1
  import { ALIAS_REPLACEMENT, GroupOperator, QueryFlag, raw, RawQueryFragment, ReferenceKind, Utils, } from '@mikro-orm/core';
2
2
  import { CriteriaNode } from './CriteriaNode.js';
3
3
  import { JoinType, QueryType } from './enums.js';
4
- const COLLECTION_OPERATORS = ['$some', '$none', '$every', '$size'];
4
+ const COLLECTION_OPERATORS = ['$some', '$none', '$every', '$size', '$all'];
5
5
  /**
6
6
  * @internal
7
7
  */
@@ -17,7 +17,7 @@ export class ObjectCriteriaNode extends CriteriaNode {
17
17
  alias = nestedAlias;
18
18
  }
19
19
  if (this.shouldAutoJoin(qb, nestedAlias)) {
20
- if (keys.some(k => COLLECTION_OPERATORS.includes(k))) {
20
+ if (keys.some(k => this.isCollectionOperator(k))) {
21
21
  if (![ReferenceKind.MANY_TO_MANY, ReferenceKind.ONE_TO_MANY].includes(this.prop.kind)) {
22
22
  // ignore collection operators when used on a non-relational property - this can happen when they get into
23
23
  // populateWhere via `infer` on m:n properties with select-in strategy
@@ -34,11 +34,24 @@ export class ObjectCriteriaNode extends CriteriaNode {
34
34
  const primaryKeys = parentMeta.primaryKeys.map(pk => {
35
35
  return [QueryType.SELECT, QueryType.COUNT].includes(qb.type) ? `${knownKey ? alias : ownerAlias}.${pk}` : pk;
36
36
  });
37
+ const conditions = [];
38
+ let matchNothing = false;
37
39
  for (const key of keys) {
38
40
  if (typeof key !== 'string' || !COLLECTION_OPERATORS.includes(key)) {
39
41
  throw new Error('Mixing collection operators with other filters is not allowed.');
40
42
  }
41
43
  const payload = this.payload[key].unwrap();
44
+ // `$all` requires every listed item to be present, which is an intersection of `$some` conditions
45
+ if (key === '$all') {
46
+ // an empty `$all` matches nothing, same as in mongo
47
+ matchNothing ||= payload.length === 0;
48
+ conditions.push(...payload.map(item => ['$some', item]));
49
+ }
50
+ else {
51
+ conditions.push([key, payload]);
52
+ }
53
+ }
54
+ for (const [key, payload] of conditions) {
42
55
  // entities with a fixed schema must resolve the `from` table's own schema in the subquery,
43
56
  // otherwise a nested operator inherits the root entity's schema (GH #7894); for wildcard or
44
57
  // schema-less entities the schema is resolved dynamically and needs to be carried over
@@ -70,6 +83,9 @@ export class ObjectCriteriaNode extends CriteriaNode {
70
83
  [Utils.getPrimaryKeyHash(primaryKeys)]: { [op]: sub.getNativeQuery().toRaw() },
71
84
  });
72
85
  }
86
+ if (matchNothing) {
87
+ $and.push({ [Utils.getPrimaryKeyHash(primaryKeys)]: { $in: [] } });
88
+ }
73
89
  if ($and.length === 1) {
74
90
  return $and[0];
75
91
  }
@@ -154,7 +170,7 @@ export class ObjectCriteriaNode extends CriteriaNode {
154
170
  alias = nestedAlias;
155
171
  }
156
172
  if (this.shouldAutoJoin(qb, nestedAlias)) {
157
- return !keys.some(k => COLLECTION_OPERATORS.includes(k));
173
+ return !keys.some(k => this.isCollectionOperator(k));
158
174
  }
159
175
  return keys.some(field => {
160
176
  const childNode = this.payload[field];
@@ -238,6 +254,14 @@ export class ObjectCriteriaNode extends CriteriaNode {
238
254
  delete o[key];
239
255
  o.$and = $and;
240
256
  }
257
+ isCollectionOperator(key) {
258
+ if (typeof key !== 'string' || !COLLECTION_OPERATORS.includes(key)) {
259
+ return false;
260
+ }
261
+ // `$all` is primarily a mongo array operator, in SQL it is supported only on collections
262
+ return (key !== '$all' ||
263
+ (!!this.prop && [ReferenceKind.MANY_TO_MANY, ReferenceKind.ONE_TO_MANY].includes(this.prop.kind)));
264
+ }
241
265
  shouldAutoJoin(qb, nestedAlias) {
242
266
  if (!this.prop || !this.parent) {
243
267
  return false;
@@ -246,7 +270,7 @@ export class ObjectCriteriaNode extends CriteriaNode {
246
270
  if (keys.every(k => typeof k === 'string' && k.includes('.') && k.startsWith(`${qb.alias}.`))) {
247
271
  return false;
248
272
  }
249
- if (keys.some(k => COLLECTION_OPERATORS.includes(k))) {
273
+ if (keys.some(k => this.isCollectionOperator(k))) {
250
274
  return true;
251
275
  }
252
276
  const meta = this.metadata.find(this.entityName);
@@ -643,6 +643,10 @@ export class QueryBuilderHelper {
643
643
  return '?';
644
644
  }
645
645
  getOperatorReplacement(op, value) {
646
+ // collection properties expand `$all` into `$some` sub-queries in `ObjectCriteriaNode`, nothing else has a SQL equivalent
647
+ if (op === '$all') {
648
+ throw new Error('The `$all` operator is supported only on collection properties in SQL drivers, use `$contains` for array columns instead.');
649
+ }
646
650
  let replacement = QueryOperator[op];
647
651
  if (op === '$exists') {
648
652
  replacement = value[op] ? 'is not' : 'is';