@mikro-orm/knex 6.4.17-dev.74 → 6.4.17-dev.76
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/AbstractSqlDriver.js +5 -4
- package/package.json +2 -2
- package/query/ObjectCriteriaNode.js +17 -1
- package/query/QueryBuilder.d.ts +5 -0
- package/query/QueryBuilder.js +25 -1
- package/typings.d.ts +1 -0
package/AbstractSqlDriver.js
CHANGED
|
@@ -837,7 +837,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
837
837
|
const toPopulate = meta.relations
|
|
838
838
|
.filter(prop => prop.kind === core_1.ReferenceKind.ONE_TO_ONE && !prop.owner && !prop.lazy && !relationsToPopulate.includes(prop.name))
|
|
839
839
|
.filter(prop => fields.length === 0 || fields.some(f => prop.name === f || prop.name.startsWith(`${String(f)}.`)))
|
|
840
|
-
.map(prop => ({ field: `${prop.name}:ref`, strategy:
|
|
840
|
+
.map(prop => ({ field: `${prop.name}:ref`, strategy: core_1.LoadStrategy.JOINED }));
|
|
841
841
|
return [...populate, ...toPopulate];
|
|
842
842
|
}
|
|
843
843
|
/**
|
|
@@ -847,8 +847,8 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
847
847
|
return populate.filter(hint => {
|
|
848
848
|
const [propName] = hint.field.split(':', 2);
|
|
849
849
|
const prop = meta.properties[propName] || {};
|
|
850
|
-
const strategy = (0, core_1.getLoadingStrategy)(hint.strategy ||
|
|
851
|
-
if (hint.filter &&
|
|
850
|
+
const strategy = (0, core_1.getLoadingStrategy)(hint.strategy || prop.strategy || options?.strategy || this.config.get('loadStrategy'), prop.kind);
|
|
851
|
+
if (hint.filter && [core_1.ReferenceKind.ONE_TO_ONE, core_1.ReferenceKind.MANY_TO_ONE].includes(prop.kind) && !prop.nullable) {
|
|
852
852
|
return true;
|
|
853
853
|
}
|
|
854
854
|
// skip redundant joins for 1:1 owner population hints when using `mapToPk`
|
|
@@ -938,11 +938,12 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
938
938
|
if (!parentJoinPath && populateWhereAll && !hint.filter && !path.startsWith('[populate]')) {
|
|
939
939
|
path = '[populate]' + path;
|
|
940
940
|
}
|
|
941
|
+
const mandatoryToOneProperty = [core_1.ReferenceKind.MANY_TO_ONE, core_1.ReferenceKind.ONE_TO_ONE].includes(prop.kind) && !prop.nullable;
|
|
941
942
|
const joinType = pivotRefJoin
|
|
942
943
|
? query_1.JoinType.pivotJoin
|
|
943
944
|
: hint.joinType
|
|
944
945
|
? hint.joinType
|
|
945
|
-
: hint.filter && !prop.nullable
|
|
946
|
+
: (hint.filter && !prop.nullable) || mandatoryToOneProperty
|
|
946
947
|
? query_1.JoinType.innerJoin
|
|
947
948
|
: query_1.JoinType.leftJoin;
|
|
948
949
|
qb.join(field, tableAlias, {}, joinType, path);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mikro-orm/knex",
|
|
3
|
-
"version": "6.4.17-dev.
|
|
3
|
+
"version": "6.4.17-dev.76",
|
|
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
|
"main": "index.js",
|
|
6
6
|
"module": "index.mjs",
|
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
"@mikro-orm/core": "^6.4.16"
|
|
67
67
|
},
|
|
68
68
|
"peerDependencies": {
|
|
69
|
-
"@mikro-orm/core": "6.4.17-dev.
|
|
69
|
+
"@mikro-orm/core": "6.4.17-dev.76",
|
|
70
70
|
"better-sqlite3": "*",
|
|
71
71
|
"libsql": "*",
|
|
72
72
|
"mariadb": "*"
|
|
@@ -210,7 +210,23 @@ class ObjectCriteriaNode extends CriteriaNode_1.CriteriaNode {
|
|
|
210
210
|
}
|
|
211
211
|
else {
|
|
212
212
|
const prev = qb._fields?.slice();
|
|
213
|
-
|
|
213
|
+
const toOneProperty = [core_1.ReferenceKind.MANY_TO_ONE, core_1.ReferenceKind.ONE_TO_ONE].includes(this.prop.kind);
|
|
214
|
+
const joinType = toOneProperty && !this.prop.nullable
|
|
215
|
+
? enums_1.JoinType.innerJoin
|
|
216
|
+
: enums_1.JoinType.leftJoin;
|
|
217
|
+
qb[method](field, nestedAlias, undefined, joinType, path);
|
|
218
|
+
// if the property is nullable, we need to use left join, so we mimic the inner join behaviour
|
|
219
|
+
// with an exclusive condition on the join columns:
|
|
220
|
+
// - if the owning column is null, the row is missing, we don't apply the filter
|
|
221
|
+
// - if the target column is not null, the row is matched, we apply the filter
|
|
222
|
+
if (toOneProperty && this.prop.nullable && options?.filter) {
|
|
223
|
+
qb.andWhere({
|
|
224
|
+
$or: [
|
|
225
|
+
{ [field]: null },
|
|
226
|
+
{ [nestedAlias + '.' + core_1.Utils.getPrimaryKeyHash(this.prop.referencedPKs)]: { $ne: null } },
|
|
227
|
+
],
|
|
228
|
+
});
|
|
229
|
+
}
|
|
214
230
|
if (!qb.hasFlag(core_1.QueryFlag.INFER_POPULATE)) {
|
|
215
231
|
qb._fields = prev;
|
|
216
232
|
}
|
package/query/QueryBuilder.d.ts
CHANGED
|
@@ -299,6 +299,11 @@ export declare class QueryBuilder<Entity extends object = AnyEntity, RootAlias e
|
|
|
299
299
|
processPopulateHint(): void;
|
|
300
300
|
private processPopulateWhere;
|
|
301
301
|
private mergeOnConditions;
|
|
302
|
+
/**
|
|
303
|
+
* When adding an inner join on a left joined relation, we need to nest them,
|
|
304
|
+
* otherwise the inner join could discard rows of the root table.
|
|
305
|
+
*/
|
|
306
|
+
private processNestedJoins;
|
|
302
307
|
private hasToManyJoins;
|
|
303
308
|
protected wrapPaginateSubQuery(meta: EntityMetadata): void;
|
|
304
309
|
private wrapModifySubQuery;
|
package/query/QueryBuilder.js
CHANGED
|
@@ -1161,6 +1161,7 @@ class QueryBuilder {
|
|
|
1161
1161
|
const meta = this.mainAlias.metadata;
|
|
1162
1162
|
this.applyDiscriminatorCondition();
|
|
1163
1163
|
this.processPopulateHint();
|
|
1164
|
+
this.processNestedJoins();
|
|
1164
1165
|
if (meta && (this._fields?.includes('*') || this._fields?.includes(`${this.mainAlias.aliasName}.*`))) {
|
|
1165
1166
|
meta.props
|
|
1166
1167
|
.filter(prop => prop.formula && (!prop.lazy || this.flags.has(core_1.QueryFlag.INCLUDE_LAZY_FORMULAS)))
|
|
@@ -1240,7 +1241,7 @@ class QueryBuilder {
|
|
|
1240
1241
|
if (typeof this[key] === 'object') {
|
|
1241
1242
|
const cond = CriteriaNodeFactory_1.CriteriaNodeFactory
|
|
1242
1243
|
.createNode(this.metadata, this.mainAlias.entityName, this[key])
|
|
1243
|
-
.process(this, { matchPopulateJoins: true, ignoreBranching: true, preferNoBranch: true });
|
|
1244
|
+
.process(this, { matchPopulateJoins: true, ignoreBranching: true, preferNoBranch: true, filter });
|
|
1244
1245
|
// there might be new joins created by processing the `populateWhere` object
|
|
1245
1246
|
joins = Object.values(this._joins);
|
|
1246
1247
|
this.mergeOnConditions(joins, cond, filter);
|
|
@@ -1281,6 +1282,29 @@ class QueryBuilder {
|
|
|
1281
1282
|
}
|
|
1282
1283
|
}
|
|
1283
1284
|
}
|
|
1285
|
+
/**
|
|
1286
|
+
* When adding an inner join on a left joined relation, we need to nest them,
|
|
1287
|
+
* otherwise the inner join could discard rows of the root table.
|
|
1288
|
+
*/
|
|
1289
|
+
processNestedJoins() {
|
|
1290
|
+
if (this.flags.has(core_1.QueryFlag.DISABLE_NESTED_INNER_JOIN)) {
|
|
1291
|
+
return;
|
|
1292
|
+
}
|
|
1293
|
+
const joins = Object.values(this._joins);
|
|
1294
|
+
for (const join of joins) {
|
|
1295
|
+
if (join.type === enums_1.JoinType.innerJoin) {
|
|
1296
|
+
const parentJoin = joins.find(j => j.alias === join.ownerAlias);
|
|
1297
|
+
// https://stackoverflow.com/a/56815807/3665878
|
|
1298
|
+
if (parentJoin?.type === enums_1.JoinType.leftJoin || parentJoin?.type === enums_1.JoinType.nestedLeftJoin) {
|
|
1299
|
+
const nested = (parentJoin.nested ??= new Set());
|
|
1300
|
+
join.type = join.type === enums_1.JoinType.innerJoin
|
|
1301
|
+
? enums_1.JoinType.nestedInnerJoin
|
|
1302
|
+
: enums_1.JoinType.nestedLeftJoin;
|
|
1303
|
+
nested.add(join);
|
|
1304
|
+
}
|
|
1305
|
+
}
|
|
1306
|
+
}
|
|
1307
|
+
}
|
|
1284
1308
|
hasToManyJoins() {
|
|
1285
1309
|
return Object.values(this._joins).some(join => {
|
|
1286
1310
|
return [core_1.ReferenceKind.ONE_TO_MANY, core_1.ReferenceKind.MANY_TO_MANY].includes(join.prop.kind);
|
package/typings.d.ts
CHANGED