linkgress-orm 0.4.22 → 0.4.24
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/dist/entity/db-context.d.ts.map +1 -1
- package/dist/entity/db-context.js +3 -2
- package/dist/entity/db-context.js.map +1 -1
- package/dist/query/collection-strategy.interface.d.ts +25 -1
- package/dist/query/collection-strategy.interface.d.ts.map +1 -1
- package/dist/query/grouped-query.d.ts.map +1 -1
- package/dist/query/grouped-query.js +4 -2
- package/dist/query/grouped-query.js.map +1 -1
- package/dist/query/join-utils.d.ts +55 -0
- package/dist/query/join-utils.d.ts.map +1 -1
- package/dist/query/join-utils.js +92 -0
- package/dist/query/join-utils.js.map +1 -1
- package/dist/query/query-builder.d.ts +3 -1
- package/dist/query/query-builder.d.ts.map +1 -1
- package/dist/query/query-builder.js +57 -14
- package/dist/query/query-builder.js.map +1 -1
- package/dist/query/strategies/cte-collection-strategy.d.ts +15 -0
- package/dist/query/strategies/cte-collection-strategy.d.ts.map +1 -1
- package/dist/query/strategies/cte-collection-strategy.js +40 -10
- package/dist/query/strategies/cte-collection-strategy.js.map +1 -1
- package/dist/query/strategies/lateral-collection-strategy.d.ts +14 -0
- package/dist/query/strategies/lateral-collection-strategy.d.ts.map +1 -1
- package/dist/query/strategies/lateral-collection-strategy.js +27 -6
- package/dist/query/strategies/lateral-collection-strategy.js.map +1 -1
- package/dist/query/strategies/temptable-collection-strategy.d.ts +9 -0
- package/dist/query/strategies/temptable-collection-strategy.d.ts.map +1 -1
- package/dist/query/strategies/temptable-collection-strategy.js +25 -5
- package/dist/query/strategies/temptable-collection-strategy.js.map +1 -1
- package/package.json +1 -1
|
@@ -254,8 +254,9 @@ class QueryBuilder {
|
|
|
254
254
|
// Non-enumerable to prevent Object.entries triggering getters (avoids stack overflow)
|
|
255
255
|
Object.defineProperty(mock, relName, {
|
|
256
256
|
get: () => {
|
|
257
|
-
return new CollectionQueryBuilder(relName, relConfig.targetTable, relConfig.foreignKey || relConfig.foreignKeys?.[0] || '', this.schema.name, targetSchema, this.schemaRegistry // Pass schema registry for nested navigation resolution
|
|
258
|
-
|
|
257
|
+
return new CollectionQueryBuilder(relName, relConfig.targetTable, relConfig.foreignKey || relConfig.foreignKeys?.[0] || '', this.schema.name, targetSchema, this.schemaRegistry, // Pass schema registry for nested navigation resolution
|
|
258
|
+
undefined, relConfig.foreignKeys, // Propagate composite FK / literal predicates
|
|
259
|
+
relConfig.matches);
|
|
259
260
|
},
|
|
260
261
|
enumerable: false,
|
|
261
262
|
configurable: true,
|
|
@@ -418,7 +419,8 @@ class QueryBuilder {
|
|
|
418
419
|
// Non-enumerable to prevent Object.entries triggering getters (avoids stack overflow)
|
|
419
420
|
Object.defineProperty(mock, relName, {
|
|
420
421
|
get: () => {
|
|
421
|
-
return new CollectionQueryBuilder(relName, relConfig.targetTable, relConfig.foreignKey || relConfig.foreignKeys?.[0] || '', schema.name, targetSchema
|
|
422
|
+
return new CollectionQueryBuilder(relName, relConfig.targetTable, relConfig.foreignKey || relConfig.foreignKeys?.[0] || '', schema.name, targetSchema, undefined, undefined, relConfig.foreignKeys, // Propagate composite FK / literal predicates
|
|
423
|
+
relConfig.matches);
|
|
422
424
|
},
|
|
423
425
|
enumerable: false,
|
|
424
426
|
configurable: true,
|
|
@@ -804,7 +806,8 @@ class SelectQueryBuilder {
|
|
|
804
806
|
// Non-enumerable to prevent Object.entries triggering getters (avoids stack overflow)
|
|
805
807
|
Object.defineProperty(mock, relName, {
|
|
806
808
|
get: () => {
|
|
807
|
-
return new CollectionQueryBuilder(relName, relConfig.targetTable, relConfig.foreignKey || relConfig.foreignKeys?.[0] || '', schema.name, targetSchema
|
|
809
|
+
return new CollectionQueryBuilder(relName, relConfig.targetTable, relConfig.foreignKey || relConfig.foreignKeys?.[0] || '', schema.name, targetSchema, undefined, undefined, relConfig.foreignKeys, // Propagate composite FK / literal predicates
|
|
810
|
+
relConfig.matches);
|
|
808
811
|
},
|
|
809
812
|
enumerable: false,
|
|
810
813
|
configurable: true,
|
|
@@ -2734,8 +2737,9 @@ ${joinClauses.join('\n')}`;
|
|
|
2734
2737
|
Object.defineProperty(mock, relName, {
|
|
2735
2738
|
get: () => {
|
|
2736
2739
|
return new CollectionQueryBuilder(relName, relConfig.targetTable, relConfig.foreignKey || relConfig.foreignKeys?.[0] || '', this.schema.name, targetSchema, // Pass the target schema directly
|
|
2737
|
-
this.schemaRegistry // Pass schema registry for nested resolution
|
|
2738
|
-
|
|
2740
|
+
this.schemaRegistry, // Pass schema registry for nested resolution
|
|
2741
|
+
undefined, relConfig.foreignKeys, // Propagate composite FK / literal predicates
|
|
2742
|
+
relConfig.matches);
|
|
2739
2743
|
},
|
|
2740
2744
|
enumerable: false,
|
|
2741
2745
|
configurable: true,
|
|
@@ -4474,6 +4478,9 @@ ${joinClauses.join('\n')}`;
|
|
|
4474
4478
|
else {
|
|
4475
4479
|
throw new Error('Aggregation selector must return a field reference');
|
|
4476
4480
|
}
|
|
4481
|
+
// Detect navigation property joins from WHERE condition (same as buildAggregateQuery)
|
|
4482
|
+
const navJoins = [];
|
|
4483
|
+
this.detectAndAddJoinsFromCondition(this.whereCond, navJoins);
|
|
4477
4484
|
// Build WHERE clause
|
|
4478
4485
|
let whereClause = '';
|
|
4479
4486
|
if (this.whereCond) {
|
|
@@ -4512,6 +4519,19 @@ ${joinClauses.join('\n')}`;
|
|
|
4512
4519
|
fromClause += `\n${joinTypeStr} "${manualJoin.table}" AS "${manualJoin.alias}" ON ${condSql}`;
|
|
4513
4520
|
}
|
|
4514
4521
|
}
|
|
4522
|
+
// Add JOINs for navigation properties referenced in WHERE clause
|
|
4523
|
+
for (const join of navJoins) {
|
|
4524
|
+
const joinType = join.isMandatory ? 'INNER JOIN' : 'LEFT JOIN';
|
|
4525
|
+
const sourceTable = join.sourceAlias || this.schema.name;
|
|
4526
|
+
const onConditions = [];
|
|
4527
|
+
for (let i = 0; i < join.foreignKeys.length; i++) {
|
|
4528
|
+
const fk = join.foreignKeys[i];
|
|
4529
|
+
const match = join.matches[i];
|
|
4530
|
+
onConditions.push(`${(0, join_utils_1.formatJoinValue)(sourceTable, fk)} = ${(0, join_utils_1.formatJoinValue)(join.alias, match)}`);
|
|
4531
|
+
}
|
|
4532
|
+
const joinTableName = this.getQualifiedTableName(join.targetTable, join.targetSchema);
|
|
4533
|
+
fromClause += `\n${joinType} ${joinTableName} AS "${join.alias}" ON ${onConditions.join(' AND ')}`;
|
|
4534
|
+
}
|
|
4515
4535
|
const sql = `SELECT ${aggregation}("${tableAlias}"."${fieldName}") as result\n${fromClause}\n${whereClause}`.trim();
|
|
4516
4536
|
return {
|
|
4517
4537
|
sql,
|
|
@@ -4814,8 +4834,9 @@ class ReferenceQueryBuilder {
|
|
|
4814
4834
|
return new CollectionQueryBuilder(relName, relConfig.targetTable, fk, this.relationName, // Use alias (relationName) for correlation in lateral joins
|
|
4815
4835
|
nestedTargetSchema, // Pass the target schema directly
|
|
4816
4836
|
this.schemaRegistry, // Pass schema registry for nested resolution
|
|
4817
|
-
extendedNavPath // Pass navigation path for intermediate joins (empty if main query)
|
|
4818
|
-
|
|
4837
|
+
extendedNavPath, // Pass navigation path for intermediate joins (empty if main query)
|
|
4838
|
+
relConfig.foreignKeys, // Propagate composite FK / literal predicates
|
|
4839
|
+
relConfig.matches);
|
|
4819
4840
|
},
|
|
4820
4841
|
enumerable: false,
|
|
4821
4842
|
configurable: true,
|
|
@@ -4853,7 +4874,7 @@ exports.ReferenceQueryBuilder = ReferenceQueryBuilder;
|
|
|
4853
4874
|
* Collection query builder for nested queries
|
|
4854
4875
|
*/
|
|
4855
4876
|
class CollectionQueryBuilder {
|
|
4856
|
-
constructor(relationName, targetTable, foreignKey, sourceTable, targetTableSchema, schemaRegistry, navigationPath) {
|
|
4877
|
+
constructor(relationName, targetTable, foreignKey, sourceTable, targetTableSchema, schemaRegistry, navigationPath, foreignKeys, matches) {
|
|
4857
4878
|
this.orderByFields = [];
|
|
4858
4879
|
this.isMarkedAsList = false;
|
|
4859
4880
|
this.isDistinct = false;
|
|
@@ -4863,6 +4884,12 @@ class CollectionQueryBuilder {
|
|
|
4863
4884
|
this.targetTable = targetTable;
|
|
4864
4885
|
this.targetTableSchema = targetTableSchema;
|
|
4865
4886
|
this.foreignKey = foreignKey;
|
|
4887
|
+
// Default `foreignKeys` to the single-column form so every strategy can
|
|
4888
|
+
// iterate uniformly. Constant FK predicates ([col, literal] + [id, true])
|
|
4889
|
+
// arrive via the explicit `foreignKeys`/`matches` arrays passed from the
|
|
4890
|
+
// navigation metadata.
|
|
4891
|
+
this.foreignKeys = (foreignKeys && foreignKeys.length > 0) ? foreignKeys : (foreignKey ? [foreignKey] : []);
|
|
4892
|
+
this.matches = (matches && matches.length > 0) ? matches : ['id'];
|
|
4866
4893
|
this.sourceTable = sourceTable;
|
|
4867
4894
|
this.schemaRegistry = schemaRegistry;
|
|
4868
4895
|
this.navigationPath = navigationPath || [];
|
|
@@ -4883,8 +4910,9 @@ class CollectionQueryBuilder {
|
|
|
4883
4910
|
*/
|
|
4884
4911
|
select(selector) {
|
|
4885
4912
|
const newBuilder = new CollectionQueryBuilder(this.relationName, this.targetTable, this.foreignKey, this.sourceTable, this.targetTableSchema, this.schemaRegistry, // Pass schema registry for nested navigation resolution
|
|
4886
|
-
this.navigationPath // Pass navigation path for intermediate joins
|
|
4887
|
-
|
|
4913
|
+
this.navigationPath, // Pass navigation path for intermediate joins
|
|
4914
|
+
this.foreignKeys, // Propagate composite/literal FK metadata
|
|
4915
|
+
this.matches);
|
|
4888
4916
|
newBuilder.selector = selector;
|
|
4889
4917
|
newBuilder.whereCond = this.whereCond;
|
|
4890
4918
|
newBuilder.limitValue = this.limitValue;
|
|
@@ -4968,9 +4996,10 @@ class CollectionQueryBuilder {
|
|
|
4968
4996
|
// Don't call build() - it returns schema without relations
|
|
4969
4997
|
const fk = relConfig.foreignKey || relConfig.foreignKeys?.[0] || '';
|
|
4970
4998
|
return new CollectionQueryBuilder(relName, relConfig.targetTable, fk, this.targetTable, undefined, // Don't pass schema, force registry lookup
|
|
4971
|
-
this.schemaRegistry // Pass schema registry for nested resolution
|
|
4999
|
+
this.schemaRegistry, // Pass schema registry for nested resolution
|
|
4972
5000
|
// No navigation path needed here - direct collection access from parent
|
|
4973
|
-
|
|
5001
|
+
undefined, relConfig.foreignKeys, // Propagate composite FK / literal predicates
|
|
5002
|
+
relConfig.matches);
|
|
4974
5003
|
},
|
|
4975
5004
|
enumerable: false,
|
|
4976
5005
|
configurable: true,
|
|
@@ -5263,8 +5292,17 @@ class CollectionQueryBuilder {
|
|
|
5263
5292
|
}
|
|
5264
5293
|
const navJoinsSQL = allJoins.join('\n');
|
|
5265
5294
|
// Build WHERE clause: correlation + additional conditions
|
|
5295
|
+
// Use full composite-FK / literal-predicate form when navigation declared
|
|
5296
|
+
// multiple key pairs (e.g. SCD2 `[productId, isCurrent] / [id, true]`).
|
|
5266
5297
|
const fkTableAlias = this.foreignKeyTableAlias || targetTable;
|
|
5267
|
-
let whereSQL
|
|
5298
|
+
let whereSQL;
|
|
5299
|
+
if (this.foreignKeys && this.foreignKeys.length > 0) {
|
|
5300
|
+
const matches = this.matches && this.matches.length > 0 ? this.matches : ['id'];
|
|
5301
|
+
whereSQL = (0, join_utils_1.buildCollectionCorrelationWhere)(fkTableAlias, sourceTable, this.foreignKeys, matches);
|
|
5302
|
+
}
|
|
5303
|
+
else {
|
|
5304
|
+
whereSQL = `"${fkTableAlias}"."${foreignKey}" = "${sourceTable}"."id"`;
|
|
5305
|
+
}
|
|
5268
5306
|
if (this.whereCond) {
|
|
5269
5307
|
const condBuilder = new conditions_1.ConditionBuilder();
|
|
5270
5308
|
const { sql: condSql, params, placeholders, paramCounter } = condBuilder.build(this.whereCond, context.paramCounter, context.placeholders);
|
|
@@ -5856,6 +5894,11 @@ class CollectionQueryBuilder {
|
|
|
5856
5894
|
relationName: this.relationName,
|
|
5857
5895
|
targetTable: this.targetTable,
|
|
5858
5896
|
foreignKey: this.foreignKey,
|
|
5897
|
+
// Composite FK metadata: enables strategies to emit constant FK predicates
|
|
5898
|
+
// (e.g. SCD2 `is_current = TRUE` from `withForeignKey: [col, isCurrent] /
|
|
5899
|
+
// withPrincipalKey: [id, true]`) in the projection WHERE clause.
|
|
5900
|
+
foreignKeys: this.foreignKeys,
|
|
5901
|
+
matches: this.matches,
|
|
5859
5902
|
foreignKeyTableAlias: this.foreignKeyTableAlias,
|
|
5860
5903
|
sourceTable: this.sourceTable,
|
|
5861
5904
|
parentIds, // Pass parent IDs for temp table strategy
|