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.
Files changed (29) hide show
  1. package/dist/entity/db-context.d.ts.map +1 -1
  2. package/dist/entity/db-context.js +3 -2
  3. package/dist/entity/db-context.js.map +1 -1
  4. package/dist/query/collection-strategy.interface.d.ts +25 -1
  5. package/dist/query/collection-strategy.interface.d.ts.map +1 -1
  6. package/dist/query/grouped-query.d.ts.map +1 -1
  7. package/dist/query/grouped-query.js +4 -2
  8. package/dist/query/grouped-query.js.map +1 -1
  9. package/dist/query/join-utils.d.ts +55 -0
  10. package/dist/query/join-utils.d.ts.map +1 -1
  11. package/dist/query/join-utils.js +92 -0
  12. package/dist/query/join-utils.js.map +1 -1
  13. package/dist/query/query-builder.d.ts +3 -1
  14. package/dist/query/query-builder.d.ts.map +1 -1
  15. package/dist/query/query-builder.js +57 -14
  16. package/dist/query/query-builder.js.map +1 -1
  17. package/dist/query/strategies/cte-collection-strategy.d.ts +15 -0
  18. package/dist/query/strategies/cte-collection-strategy.d.ts.map +1 -1
  19. package/dist/query/strategies/cte-collection-strategy.js +40 -10
  20. package/dist/query/strategies/cte-collection-strategy.js.map +1 -1
  21. package/dist/query/strategies/lateral-collection-strategy.d.ts +14 -0
  22. package/dist/query/strategies/lateral-collection-strategy.d.ts.map +1 -1
  23. package/dist/query/strategies/lateral-collection-strategy.js +27 -6
  24. package/dist/query/strategies/lateral-collection-strategy.js.map +1 -1
  25. package/dist/query/strategies/temptable-collection-strategy.d.ts +9 -0
  26. package/dist/query/strategies/temptable-collection-strategy.d.ts.map +1 -1
  27. package/dist/query/strategies/temptable-collection-strategy.js +25 -5
  28. package/dist/query/strategies/temptable-collection-strategy.js.map +1 -1
  29. 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 = `"${fkTableAlias}"."${foreignKey}" = "${sourceTable}"."id"`;
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