metal-orm 1.0.97 → 1.0.98
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/index.cjs +18 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +15 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/core/hydration/types.ts +57 -57
- package/src/orm/entity-relations.ts +19 -19
- package/src/orm/lazy-batch/belongs-to-many.ts +134 -134
- package/src/orm/lazy-batch/belongs-to.ts +108 -108
- package/src/orm/lazy-batch/has-many.ts +69 -69
- package/src/orm/lazy-batch/has-one.ts +68 -68
- package/src/orm/lazy-batch/shared.ts +125 -125
- package/src/orm/save-graph.ts +48 -48
- package/src/query-builder/column-selector.ts +9 -9
- package/src/query-builder/hydration-manager.ts +353 -353
- package/src/query-builder/hydration-planner.ts +22 -22
- package/src/query-builder/relation-conditions.ts +80 -80
- package/src/query-builder/select/projection-facet.ts +23 -23
- package/src/query-builder/select-query-state.ts +213 -213
- package/src/query-builder/select.ts +1155 -1133
- package/src/schema/relation.ts +22 -22
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { TableDef } from '../schema/table.js';
|
|
2
2
|
import { RelationDef, RelationKinds, BelongsToManyRelation } from '../schema/relation.js';
|
|
3
3
|
import { ProjectionNode } from './select-query-state.js';
|
|
4
|
-
import { HydrationPlan, HydrationRelationPlan } from '../core/hydration/types.js';
|
|
4
|
+
import { HydrationPlan, HydrationRelationPlan } from '../core/hydration/types.js';
|
|
5
5
|
import { isRelationAlias } from './relation-alias.js';
|
|
6
6
|
import { buildDefaultPivotColumns } from './relation-utils.js';
|
|
7
7
|
|
|
@@ -31,20 +31,20 @@ export class HydrationPlanner {
|
|
|
31
31
|
* @param columns - Columns to capture
|
|
32
32
|
* @returns Updated HydrationPlanner with captured columns
|
|
33
33
|
*/
|
|
34
|
-
captureRootColumns(columns: ProjectionNode[]): HydrationPlanner {
|
|
35
|
-
const currentPlan = this.getPlanOrDefault();
|
|
36
|
-
const rootCols = new Set(currentPlan.rootColumns);
|
|
37
|
-
let changed = false;
|
|
38
|
-
|
|
39
|
-
columns.forEach(node => {
|
|
40
|
-
const alias = node.type === 'Column' ? (node.alias || node.name) : node.alias;
|
|
41
|
-
if (!alias || isRelationAlias(alias)) return;
|
|
42
|
-
if (node.type === 'Column' && node.table !== this.table.name) return;
|
|
43
|
-
if (!rootCols.has(alias)) {
|
|
44
|
-
rootCols.add(alias);
|
|
45
|
-
changed = true;
|
|
46
|
-
}
|
|
47
|
-
});
|
|
34
|
+
captureRootColumns(columns: ProjectionNode[]): HydrationPlanner {
|
|
35
|
+
const currentPlan = this.getPlanOrDefault();
|
|
36
|
+
const rootCols = new Set(currentPlan.rootColumns);
|
|
37
|
+
let changed = false;
|
|
38
|
+
|
|
39
|
+
columns.forEach(node => {
|
|
40
|
+
const alias = node.type === 'Column' ? (node.alias || node.name) : node.alias;
|
|
41
|
+
if (!alias || isRelationAlias(alias)) return;
|
|
42
|
+
if (node.type === 'Column' && node.table !== this.table.name) return;
|
|
43
|
+
if (!rootCols.has(alias)) {
|
|
44
|
+
rootCols.add(alias);
|
|
45
|
+
changed = true;
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
48
|
|
|
49
49
|
if (!changed) return this;
|
|
50
50
|
return new HydrationPlanner(this.table, {
|
|
@@ -109,13 +109,13 @@ export class HydrationPlanner {
|
|
|
109
109
|
pivot?: { aliasPrefix: string; columns: string[] }
|
|
110
110
|
): HydrationRelationPlan {
|
|
111
111
|
switch (rel.type) {
|
|
112
|
-
case RelationKinds.HasMany:
|
|
113
|
-
case RelationKinds.HasOne: {
|
|
114
|
-
const localKey = rel.localKey || findPrimaryKey(this.table);
|
|
115
|
-
return {
|
|
116
|
-
name: relationName,
|
|
117
|
-
aliasPrefix,
|
|
118
|
-
type: rel.type,
|
|
112
|
+
case RelationKinds.HasMany:
|
|
113
|
+
case RelationKinds.HasOne: {
|
|
114
|
+
const localKey = rel.localKey || findPrimaryKey(this.table);
|
|
115
|
+
return {
|
|
116
|
+
name: relationName,
|
|
117
|
+
aliasPrefix,
|
|
118
|
+
type: rel.type,
|
|
119
119
|
targetTable: rel.target.name,
|
|
120
120
|
targetPrimaryKey: findPrimaryKey(rel.target),
|
|
121
121
|
foreignKey: rel.foreignKey,
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { TableDef } from '../schema/table.js';
|
|
2
|
-
import { RelationDef, RelationKinds, BelongsToManyRelation } from '../schema/relation.js';
|
|
3
|
-
import { ExpressionNode, eq, and } from '../core/ast/expression.js';
|
|
4
|
-
import { TableSourceNode } from '../core/ast/query.js';
|
|
5
|
-
import { findPrimaryKey } from './hydration-planner.js';
|
|
6
|
-
import { JoinNode } from '../core/ast/join.js';
|
|
7
|
-
import { JoinKind } from '../core/sql/sql.js';
|
|
8
|
-
import { createJoinNode } from '../core/ast/join-node.js';
|
|
1
|
+
import { TableDef } from '../schema/table.js';
|
|
2
|
+
import { RelationDef, RelationKinds, BelongsToManyRelation } from '../schema/relation.js';
|
|
3
|
+
import { ExpressionNode, eq, and } from '../core/ast/expression.js';
|
|
4
|
+
import { TableSourceNode } from '../core/ast/query.js';
|
|
5
|
+
import { findPrimaryKey } from './hydration-planner.js';
|
|
6
|
+
import { JoinNode } from '../core/ast/join.js';
|
|
7
|
+
import { JoinKind } from '../core/sql/sql.js';
|
|
8
|
+
import { createJoinNode } from '../core/ast/join-node.js';
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* Utility function to handle unreachable code paths
|
|
@@ -22,32 +22,32 @@ const assertNever = (value: never): never => {
|
|
|
22
22
|
* @param relation - Relation definition
|
|
23
23
|
* @returns Expression node representing the join condition
|
|
24
24
|
*/
|
|
25
|
-
const baseRelationCondition = (
|
|
26
|
-
root: TableDef,
|
|
27
|
-
relation: RelationDef,
|
|
28
|
-
rootAlias?: string,
|
|
29
|
-
targetTableName?: string
|
|
30
|
-
): ExpressionNode => {
|
|
31
|
-
const rootTable = rootAlias || root.name;
|
|
32
|
-
const targetTable = targetTableName ?? relation.target.name;
|
|
33
|
-
const defaultLocalKey =
|
|
34
|
-
relation.type === RelationKinds.HasMany || relation.type === RelationKinds.HasOne
|
|
35
|
-
? findPrimaryKey(root)
|
|
36
|
-
: findPrimaryKey(relation.target);
|
|
25
|
+
const baseRelationCondition = (
|
|
26
|
+
root: TableDef,
|
|
27
|
+
relation: RelationDef,
|
|
28
|
+
rootAlias?: string,
|
|
29
|
+
targetTableName?: string
|
|
30
|
+
): ExpressionNode => {
|
|
31
|
+
const rootTable = rootAlias || root.name;
|
|
32
|
+
const targetTable = targetTableName ?? relation.target.name;
|
|
33
|
+
const defaultLocalKey =
|
|
34
|
+
relation.type === RelationKinds.HasMany || relation.type === RelationKinds.HasOne
|
|
35
|
+
? findPrimaryKey(root)
|
|
36
|
+
: findPrimaryKey(relation.target);
|
|
37
37
|
const localKey = relation.localKey || defaultLocalKey;
|
|
38
38
|
|
|
39
39
|
switch (relation.type) {
|
|
40
|
-
case RelationKinds.HasMany:
|
|
41
|
-
case RelationKinds.HasOne:
|
|
42
|
-
return eq(
|
|
43
|
-
{ type: 'Column', table: targetTable, name: relation.foreignKey },
|
|
44
|
-
{ type: 'Column', table: rootTable, name: localKey }
|
|
45
|
-
);
|
|
46
|
-
case RelationKinds.BelongsTo:
|
|
47
|
-
return eq(
|
|
48
|
-
{ type: 'Column', table: targetTable, name: localKey },
|
|
49
|
-
{ type: 'Column', table: rootTable, name: relation.foreignKey }
|
|
50
|
-
);
|
|
40
|
+
case RelationKinds.HasMany:
|
|
41
|
+
case RelationKinds.HasOne:
|
|
42
|
+
return eq(
|
|
43
|
+
{ type: 'Column', table: targetTable, name: relation.foreignKey },
|
|
44
|
+
{ type: 'Column', table: rootTable, name: localKey }
|
|
45
|
+
);
|
|
46
|
+
case RelationKinds.BelongsTo:
|
|
47
|
+
return eq(
|
|
48
|
+
{ type: 'Column', table: targetTable, name: localKey },
|
|
49
|
+
{ type: 'Column', table: rootTable, name: relation.foreignKey }
|
|
50
|
+
);
|
|
51
51
|
case RelationKinds.BelongsToMany:
|
|
52
52
|
throw new Error('BelongsToMany relations do not support the standard join condition builder');
|
|
53
53
|
default:
|
|
@@ -65,16 +65,16 @@ const baseRelationCondition = (
|
|
|
65
65
|
* @param rootAlias - Optional alias for the root table
|
|
66
66
|
* @returns Array of join nodes for the pivot and target tables
|
|
67
67
|
*/
|
|
68
|
-
export const buildBelongsToManyJoins = (
|
|
69
|
-
root: TableDef,
|
|
70
|
-
relationName: string,
|
|
71
|
-
relation: BelongsToManyRelation,
|
|
72
|
-
joinKind: JoinKind,
|
|
73
|
-
extra?: ExpressionNode,
|
|
74
|
-
rootAlias?: string,
|
|
75
|
-
targetTable?: TableSourceNode,
|
|
76
|
-
targetTableName?: string
|
|
77
|
-
): JoinNode[] => {
|
|
68
|
+
export const buildBelongsToManyJoins = (
|
|
69
|
+
root: TableDef,
|
|
70
|
+
relationName: string,
|
|
71
|
+
relation: BelongsToManyRelation,
|
|
72
|
+
joinKind: JoinKind,
|
|
73
|
+
extra?: ExpressionNode,
|
|
74
|
+
rootAlias?: string,
|
|
75
|
+
targetTable?: TableSourceNode,
|
|
76
|
+
targetTableName?: string
|
|
77
|
+
): JoinNode[] => {
|
|
78
78
|
const rootKey = relation.localKey || findPrimaryKey(root);
|
|
79
79
|
const targetKey = relation.targetKey || findPrimaryKey(relation.target);
|
|
80
80
|
const rootTable = rootAlias || root.name;
|
|
@@ -90,27 +90,27 @@ export const buildBelongsToManyJoins = (
|
|
|
90
90
|
pivotCondition
|
|
91
91
|
);
|
|
92
92
|
|
|
93
|
-
const targetSource: TableSourceNode = targetTable ?? {
|
|
94
|
-
type: 'Table',
|
|
95
|
-
name: relation.target.name,
|
|
96
|
-
schema: relation.target.schema
|
|
97
|
-
};
|
|
98
|
-
const effectiveTargetName = targetTableName ?? relation.target.name;
|
|
99
|
-
let targetCondition: ExpressionNode = eq(
|
|
100
|
-
{ type: 'Column', table: effectiveTargetName, name: targetKey },
|
|
101
|
-
{ type: 'Column', table: relation.pivotTable.name, name: relation.pivotForeignKeyToTarget }
|
|
102
|
-
);
|
|
103
|
-
|
|
104
|
-
if (extra) {
|
|
105
|
-
targetCondition = and(targetCondition, extra);
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
const targetJoin = createJoinNode(
|
|
109
|
-
joinKind,
|
|
110
|
-
targetSource,
|
|
111
|
-
targetCondition,
|
|
112
|
-
relationName
|
|
113
|
-
);
|
|
93
|
+
const targetSource: TableSourceNode = targetTable ?? {
|
|
94
|
+
type: 'Table',
|
|
95
|
+
name: relation.target.name,
|
|
96
|
+
schema: relation.target.schema
|
|
97
|
+
};
|
|
98
|
+
const effectiveTargetName = targetTableName ?? relation.target.name;
|
|
99
|
+
let targetCondition: ExpressionNode = eq(
|
|
100
|
+
{ type: 'Column', table: effectiveTargetName, name: targetKey },
|
|
101
|
+
{ type: 'Column', table: relation.pivotTable.name, name: relation.pivotForeignKeyToTarget }
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
if (extra) {
|
|
105
|
+
targetCondition = and(targetCondition, extra);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const targetJoin = createJoinNode(
|
|
109
|
+
joinKind,
|
|
110
|
+
targetSource,
|
|
111
|
+
targetCondition,
|
|
112
|
+
relationName
|
|
113
|
+
);
|
|
114
114
|
|
|
115
115
|
return [pivotJoin, targetJoin];
|
|
116
116
|
};
|
|
@@ -123,16 +123,16 @@ export const buildBelongsToManyJoins = (
|
|
|
123
123
|
* @param rootAlias - Optional alias for the root table
|
|
124
124
|
* @returns Expression node representing the complete join condition
|
|
125
125
|
*/
|
|
126
|
-
export const buildRelationJoinCondition = (
|
|
127
|
-
root: TableDef,
|
|
128
|
-
relation: RelationDef,
|
|
129
|
-
extra?: ExpressionNode,
|
|
130
|
-
rootAlias?: string,
|
|
131
|
-
targetTableName?: string
|
|
132
|
-
): ExpressionNode => {
|
|
133
|
-
const base = baseRelationCondition(root, relation, rootAlias, targetTableName);
|
|
134
|
-
return extra ? and(base, extra) : base;
|
|
135
|
-
};
|
|
126
|
+
export const buildRelationJoinCondition = (
|
|
127
|
+
root: TableDef,
|
|
128
|
+
relation: RelationDef,
|
|
129
|
+
extra?: ExpressionNode,
|
|
130
|
+
rootAlias?: string,
|
|
131
|
+
targetTableName?: string
|
|
132
|
+
): ExpressionNode => {
|
|
133
|
+
const base = baseRelationCondition(root, relation, rootAlias, targetTableName);
|
|
134
|
+
return extra ? and(base, extra) : base;
|
|
135
|
+
};
|
|
136
136
|
|
|
137
137
|
/**
|
|
138
138
|
* Builds a relation correlation condition for subqueries
|
|
@@ -141,11 +141,11 @@ export const buildRelationJoinCondition = (
|
|
|
141
141
|
* @param rootAlias - Optional alias for the root table
|
|
142
142
|
* @returns Expression node representing the correlation condition
|
|
143
143
|
*/
|
|
144
|
-
export const buildRelationCorrelation = (
|
|
145
|
-
root: TableDef,
|
|
146
|
-
relation: RelationDef,
|
|
147
|
-
rootAlias?: string,
|
|
148
|
-
targetTableName?: string
|
|
149
|
-
): ExpressionNode => {
|
|
150
|
-
return baseRelationCondition(root, relation, rootAlias, targetTableName);
|
|
151
|
-
};
|
|
144
|
+
export const buildRelationCorrelation = (
|
|
145
|
+
root: TableDef,
|
|
146
|
+
relation: RelationDef,
|
|
147
|
+
rootAlias?: string,
|
|
148
|
+
targetTableName?: string
|
|
149
|
+
): ExpressionNode => {
|
|
150
|
+
return baseRelationCondition(root, relation, rootAlias, targetTableName);
|
|
151
|
+
};
|
|
@@ -22,12 +22,12 @@ export class SelectProjectionFacet {
|
|
|
22
22
|
* @param columns - Columns to select
|
|
23
23
|
* @returns Updated query context with selected columns
|
|
24
24
|
*/
|
|
25
|
-
select(
|
|
26
|
-
context: SelectQueryBuilderContext,
|
|
27
|
-
columns: Record<string, ColumnSelectionValue>
|
|
28
|
-
): SelectQueryBuilderContext {
|
|
29
|
-
return this.columnSelector.select(context, columns);
|
|
30
|
-
}
|
|
25
|
+
select(
|
|
26
|
+
context: SelectQueryBuilderContext,
|
|
27
|
+
columns: Record<string, ColumnSelectionValue>
|
|
28
|
+
): SelectQueryBuilderContext {
|
|
29
|
+
return this.columnSelector.select(context, columns);
|
|
30
|
+
}
|
|
31
31
|
|
|
32
32
|
/**
|
|
33
33
|
* Selects raw column expressions
|
|
@@ -35,9 +35,9 @@ export class SelectProjectionFacet {
|
|
|
35
35
|
* @param cols - Raw column expressions
|
|
36
36
|
* @returns Updated query context with raw column selections
|
|
37
37
|
*/
|
|
38
|
-
selectRaw(context: SelectQueryBuilderContext, cols: string[]): SelectQueryBuilderContext {
|
|
39
|
-
return this.columnSelector.selectRaw(context, cols);
|
|
40
|
-
}
|
|
38
|
+
selectRaw(context: SelectQueryBuilderContext, cols: string[]): SelectQueryBuilderContext {
|
|
39
|
+
return this.columnSelector.selectRaw(context, cols);
|
|
40
|
+
}
|
|
41
41
|
|
|
42
42
|
/**
|
|
43
43
|
* Selects a subquery as a column
|
|
@@ -46,13 +46,13 @@ export class SelectProjectionFacet {
|
|
|
46
46
|
* @param query - Subquery to select
|
|
47
47
|
* @returns Updated query context with subquery selection
|
|
48
48
|
*/
|
|
49
|
-
selectSubquery(
|
|
50
|
-
context: SelectQueryBuilderContext,
|
|
51
|
-
alias: string,
|
|
52
|
-
query: SelectQueryNode
|
|
53
|
-
): SelectQueryBuilderContext {
|
|
54
|
-
return this.columnSelector.selectSubquery(context, alias, query);
|
|
55
|
-
}
|
|
49
|
+
selectSubquery(
|
|
50
|
+
context: SelectQueryBuilderContext,
|
|
51
|
+
alias: string,
|
|
52
|
+
query: SelectQueryNode
|
|
53
|
+
): SelectQueryBuilderContext {
|
|
54
|
+
return this.columnSelector.selectSubquery(context, alias, query);
|
|
55
|
+
}
|
|
56
56
|
|
|
57
57
|
/**
|
|
58
58
|
* Adds DISTINCT clause to the query
|
|
@@ -60,11 +60,11 @@ export class SelectProjectionFacet {
|
|
|
60
60
|
* @param cols - Columns to make distinct
|
|
61
61
|
* @returns Updated query context with DISTINCT clause
|
|
62
62
|
*/
|
|
63
|
-
distinct(
|
|
64
|
-
context: SelectQueryBuilderContext,
|
|
65
|
-
cols: (ColumnDef | ColumnNode)[]
|
|
66
|
-
): SelectQueryBuilderContext {
|
|
67
|
-
return this.columnSelector.distinct(context, cols);
|
|
68
|
-
}
|
|
69
|
-
}
|
|
63
|
+
distinct(
|
|
64
|
+
context: SelectQueryBuilderContext,
|
|
65
|
+
cols: (ColumnDef | ColumnNode)[]
|
|
66
|
+
): SelectQueryBuilderContext {
|
|
67
|
+
return this.columnSelector.distinct(context, cols);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
70
|
|