@apisr/drizzle-model 0.0.1 → 0.0.2
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.d.mts +3 -0
- package/dist/index.mjs +4 -0
- package/dist/model/builder.d.mts +20 -0
- package/dist/model/builder.mjs +18 -0
- package/dist/model/config.d.mts +23 -0
- package/dist/model/core/joins.mjs +184 -0
- package/dist/model/core/projection.mjs +28 -0
- package/dist/model/core/runtime.mjs +198 -0
- package/dist/model/core/thenable.mjs +64 -0
- package/dist/model/core/transform.mjs +39 -0
- package/dist/model/core/where.mjs +130 -0
- package/dist/model/core/with.mjs +19 -0
- package/dist/model/dialect.d.mts +12 -0
- package/dist/model/format.d.mts +4 -0
- package/dist/model/index.d.mts +1 -0
- package/dist/model/index.mjs +3 -0
- package/dist/model/methods/exclude.d.mts +8 -0
- package/dist/model/methods/include.d.mts +6 -0
- package/dist/model/methods/insert.d.mts +7 -0
- package/dist/model/methods/query/where.d.mts +14 -0
- package/dist/model/methods/return.d.mts +12 -0
- package/dist/model/methods/select.d.mts +8 -0
- package/dist/model/methods/update.d.mts +7 -0
- package/dist/model/methods/upsert.d.mts +26 -0
- package/dist/model/methods/with.d.mts +16 -0
- package/dist/model/model.d.mts +105 -0
- package/dist/model/options.d.mts +28 -0
- package/dist/model/query/operations.d.mts +66 -0
- package/dist/model/relation.d.mts +68 -0
- package/dist/model/result.d.mts +34 -0
- package/dist/model/table.d.mts +69 -0
- package/dist/types.d.mts +10 -0
- package/drizzle.config.ts +6 -6
- package/package.json +1 -1
- package/src/model/builder.ts +37 -37
- package/src/model/config.ts +28 -25
- package/src/model/core/joins.ts +337 -252
- package/src/model/core/projection.ts +49 -35
- package/src/model/core/runtime.ts +302 -221
- package/src/model/core/thenable.ts +70 -61
- package/src/model/core/transform.ts +53 -33
- package/src/model/core/where.ts +228 -162
- package/src/model/core/with.ts +21 -21
- package/src/model/dialect.ts +12 -2
- package/src/model/foreigns.ts +6 -10
- package/src/model/format.ts +8 -8
- package/src/model/methods/include.ts +1 -1
- package/src/model/methods/insert.ts +13 -10
- package/src/model/methods/levels.ts +7 -1
- package/src/model/methods/query/where.ts +49 -36
- package/src/model/methods/return.ts +13 -12
- package/src/model/methods/select.ts +29 -29
- package/src/model/methods/update.ts +3 -1
- package/src/model/methods/upsert.ts +35 -36
- package/src/model/model.ts +115 -107
- package/src/model/options.ts +44 -37
- package/src/model/query/operations.ts +73 -72
- package/src/model/relation.ts +47 -46
- package/src/model/result.ts +79 -63
- package/src/model/shape.ts +5 -4
- package/src/model/table.ts +34 -33
- package/src/types.ts +3 -3
- package/tests/builder-v2-mysql.type-test.ts +31 -20
- package/tests/builder-v2.type-test.ts +246 -253
- package/tests/builder.test.ts +1 -1
- package/tests/db.ts +3 -3
- package/tests/find.test.ts +149 -138
- package/tests/insert.test.ts +217 -203
- package/tests/relations.ts +34 -34
- package/tests/schema.ts +34 -35
package/src/model/core/joins.ts
CHANGED
|
@@ -3,277 +3,362 @@ import { and, eq } from "drizzle-orm";
|
|
|
3
3
|
type AnyObj = Record<string, any>;
|
|
4
4
|
|
|
5
5
|
type JoinNode = {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
6
|
+
path: string[];
|
|
7
|
+
key: string;
|
|
8
|
+
relationType: "one" | "many";
|
|
9
|
+
sourceTableName: string;
|
|
10
|
+
targetTableName: string;
|
|
11
|
+
sourceTable: AnyObj;
|
|
12
|
+
targetTable: AnyObj;
|
|
13
|
+
targetAliasTable: AnyObj;
|
|
14
|
+
aliasKey: string;
|
|
15
|
+
sourceColumns: any[];
|
|
16
|
+
targetColumns: any[];
|
|
17
|
+
pkField: string;
|
|
18
|
+
parent?: JoinNode;
|
|
19
|
+
children: JoinNode[];
|
|
20
20
|
};
|
|
21
21
|
|
|
22
22
|
function isDrizzleColumn(value: any): boolean {
|
|
23
|
-
|
|
23
|
+
return (
|
|
24
|
+
!!value && typeof value === "object" && typeof value.getSQL === "function"
|
|
25
|
+
);
|
|
24
26
|
}
|
|
25
27
|
|
|
26
28
|
function getPrimaryKeyField(table: AnyObj): string {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
29
|
+
for (const [k, v] of Object.entries(table)) {
|
|
30
|
+
if (!isDrizzleColumn(v)) {
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
if ((v as any).primary === true) {
|
|
34
|
+
return k;
|
|
35
|
+
}
|
|
36
|
+
if ((v as any).config?.primaryKey === true) {
|
|
37
|
+
return k;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
if ("id" in table) {
|
|
41
|
+
return "id";
|
|
42
|
+
}
|
|
43
|
+
return (
|
|
44
|
+
Object.keys(table).find((k) => isDrizzleColumn((table as any)[k])) ?? "id"
|
|
45
|
+
);
|
|
34
46
|
}
|
|
35
47
|
|
|
36
48
|
function isAllNullRow(obj: AnyObj | null | undefined): boolean {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
49
|
+
if (!obj || typeof obj !== "object") {
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
for (const v of Object.values(obj)) {
|
|
53
|
+
if (v !== null && v !== undefined) {
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return true;
|
|
42
58
|
}
|
|
43
59
|
|
|
44
|
-
async function aliasTable(
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
60
|
+
async function aliasTable(
|
|
61
|
+
table: AnyObj,
|
|
62
|
+
aliasName: string,
|
|
63
|
+
dialect: string
|
|
64
|
+
): Promise<AnyObj> {
|
|
65
|
+
// Drizzle exports `alias()` from dialect-specific core modules.
|
|
66
|
+
// We keep this dynamic to avoid hard dependency on a single dialect.
|
|
67
|
+
if (dialect === "PostgreSQL") {
|
|
68
|
+
const mod: any = await import("drizzle-orm/pg-core");
|
|
69
|
+
if (typeof mod.alias === "function") {
|
|
70
|
+
return mod.alias(table, aliasName);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
if (dialect === "MySQL") {
|
|
74
|
+
const mod: any = await import("drizzle-orm/mysql-core");
|
|
75
|
+
if (typeof mod.alias === "function") {
|
|
76
|
+
return mod.alias(table, aliasName);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
if (dialect === "SQLite") {
|
|
80
|
+
const mod: any = await import("drizzle-orm/sqlite-core");
|
|
81
|
+
if (typeof mod.alias === "function") {
|
|
82
|
+
return mod.alias(table, aliasName);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return table;
|
|
61
87
|
}
|
|
62
88
|
|
|
63
89
|
function buildJoinOn(node: JoinNode): any {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
90
|
+
const parts = node.sourceColumns.map((src, i) => {
|
|
91
|
+
const tgt = node.targetColumns[i];
|
|
92
|
+
// tgt is a column bound to the *original* target table; we need the one from alias table.
|
|
93
|
+
const tgtKey = Object.entries(node.targetTable).find(
|
|
94
|
+
([, v]) => v === tgt
|
|
95
|
+
)?.[0];
|
|
96
|
+
const tgtCol = tgtKey ? (node.targetAliasTable as any)[tgtKey] : tgt;
|
|
97
|
+
return eq(tgtCol, src);
|
|
98
|
+
});
|
|
99
|
+
return parts.length === 1 ? parts[0] : and(...parts);
|
|
72
100
|
}
|
|
73
101
|
|
|
74
102
|
function buildSelectMapForTable(table: AnyObj): AnyObj {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
103
|
+
const out: AnyObj = {};
|
|
104
|
+
for (const [k, v] of Object.entries(table)) {
|
|
105
|
+
if (isDrizzleColumn(v)) {
|
|
106
|
+
out[k] = v;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return out;
|
|
80
110
|
}
|
|
81
111
|
|
|
82
112
|
export async function executeWithJoins(args: {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
113
|
+
db: any;
|
|
114
|
+
schema: Record<string, any>;
|
|
115
|
+
relations: Record<string, any>;
|
|
116
|
+
baseTableName: string;
|
|
117
|
+
baseTable: AnyObj;
|
|
118
|
+
dialect: string;
|
|
119
|
+
whereSql?: any;
|
|
120
|
+
withValue: AnyObj;
|
|
121
|
+
limitOne?: boolean;
|
|
92
122
|
}): Promise<any> {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
123
|
+
const {
|
|
124
|
+
db,
|
|
125
|
+
schema,
|
|
126
|
+
relations,
|
|
127
|
+
baseTableName,
|
|
128
|
+
baseTable,
|
|
129
|
+
dialect,
|
|
130
|
+
whereSql,
|
|
131
|
+
withValue,
|
|
132
|
+
limitOne,
|
|
133
|
+
} = args;
|
|
134
|
+
|
|
135
|
+
const usedAliasKeys = new Set<string>();
|
|
136
|
+
|
|
137
|
+
const buildNode = async (
|
|
138
|
+
parent: JoinNode | undefined,
|
|
139
|
+
currentTableName: string,
|
|
140
|
+
currentTable: AnyObj,
|
|
141
|
+
key: string,
|
|
142
|
+
value: any,
|
|
143
|
+
path: string[]
|
|
144
|
+
): Promise<JoinNode> => {
|
|
145
|
+
const relMeta = (relations as any)[currentTableName]?.relations?.[key];
|
|
146
|
+
if (!relMeta) {
|
|
147
|
+
throw new Error(
|
|
148
|
+
`Unknown relation '${key}' on table '${currentTableName}'.`
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const targetTableName: string = relMeta.targetTableName;
|
|
153
|
+
const targetTable: AnyObj = (schema as any)[targetTableName];
|
|
154
|
+
const aliasKeyBase = [...path, key].join("__");
|
|
155
|
+
let aliasKey = aliasKeyBase;
|
|
156
|
+
let idx = 1;
|
|
157
|
+
while (usedAliasKeys.has(aliasKey)) {
|
|
158
|
+
aliasKey = `${aliasKeyBase}_${idx++}`;
|
|
159
|
+
}
|
|
160
|
+
usedAliasKeys.add(aliasKey);
|
|
161
|
+
|
|
162
|
+
const needsAlias =
|
|
163
|
+
targetTableName === currentTableName ||
|
|
164
|
+
usedAliasKeys.has(`table:${targetTableName}`);
|
|
165
|
+
usedAliasKeys.add(`table:${targetTableName}`);
|
|
166
|
+
|
|
167
|
+
const targetAliasTable = needsAlias
|
|
168
|
+
? await aliasTable(targetTable, aliasKey, dialect)
|
|
169
|
+
: targetTable;
|
|
170
|
+
|
|
171
|
+
const node: JoinNode = {
|
|
172
|
+
path: [...path, key],
|
|
173
|
+
key,
|
|
174
|
+
relationType: relMeta.relationType,
|
|
175
|
+
sourceTableName: currentTableName,
|
|
176
|
+
targetTableName,
|
|
177
|
+
sourceTable: currentTable,
|
|
178
|
+
targetTable,
|
|
179
|
+
targetAliasTable,
|
|
180
|
+
aliasKey,
|
|
181
|
+
sourceColumns: relMeta.sourceColumns ?? [],
|
|
182
|
+
targetColumns: relMeta.targetColumns ?? [],
|
|
183
|
+
pkField: getPrimaryKeyField(targetAliasTable),
|
|
184
|
+
parent,
|
|
185
|
+
children: [],
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
if (value && typeof value === "object" && value !== true) {
|
|
189
|
+
for (const [childKey, childVal] of Object.entries(value)) {
|
|
190
|
+
if (
|
|
191
|
+
childVal !== true &&
|
|
192
|
+
(typeof childVal !== "object" || childVal == null)
|
|
193
|
+
) {
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
const child = await buildNode(
|
|
197
|
+
node,
|
|
198
|
+
targetTableName,
|
|
199
|
+
targetAliasTable,
|
|
200
|
+
childKey,
|
|
201
|
+
childVal,
|
|
202
|
+
[...path, key]
|
|
203
|
+
);
|
|
204
|
+
node.children.push(child);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
return node;
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
const root: JoinNode = {
|
|
212
|
+
path: [],
|
|
213
|
+
key: "$root",
|
|
214
|
+
relationType: "one",
|
|
215
|
+
sourceTableName: baseTableName,
|
|
216
|
+
targetTableName: baseTableName,
|
|
217
|
+
sourceTable: baseTable,
|
|
218
|
+
targetTable: baseTable,
|
|
219
|
+
targetAliasTable: baseTable,
|
|
220
|
+
aliasKey: "$base",
|
|
221
|
+
sourceColumns: [],
|
|
222
|
+
targetColumns: [],
|
|
223
|
+
pkField: getPrimaryKeyField(baseTable),
|
|
224
|
+
children: [],
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
for (const [key, value] of Object.entries(withValue)) {
|
|
228
|
+
if (value !== true && (typeof value !== "object" || value == null)) {
|
|
229
|
+
continue;
|
|
230
|
+
}
|
|
231
|
+
const child = await buildNode(
|
|
232
|
+
undefined,
|
|
233
|
+
baseTableName,
|
|
234
|
+
baseTable,
|
|
235
|
+
key,
|
|
236
|
+
value,
|
|
237
|
+
[]
|
|
238
|
+
);
|
|
239
|
+
root.children.push(child);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// Flatten nodes in join order (preorder)
|
|
243
|
+
const nodes: JoinNode[] = [];
|
|
244
|
+
const walk = (n: JoinNode) => {
|
|
245
|
+
for (const c of n.children) {
|
|
246
|
+
nodes.push(c);
|
|
247
|
+
walk(c);
|
|
248
|
+
}
|
|
249
|
+
};
|
|
250
|
+
walk(root);
|
|
251
|
+
|
|
252
|
+
// Build select map: base + each joined alias
|
|
253
|
+
const selectMap: AnyObj = {
|
|
254
|
+
base: buildSelectMapForTable(baseTable),
|
|
255
|
+
};
|
|
256
|
+
for (const n of nodes) {
|
|
257
|
+
selectMap[n.aliasKey] = buildSelectMapForTable(n.targetAliasTable);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
let q = db.select(selectMap).from(baseTable);
|
|
261
|
+
if (whereSql) {
|
|
262
|
+
q = q.where(whereSql);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// Apply joins
|
|
266
|
+
for (const n of nodes) {
|
|
267
|
+
const on = buildJoinOn(n);
|
|
268
|
+
q = q.leftJoin(n.targetAliasTable, on);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
if (limitOne) {
|
|
272
|
+
q = q.limit(1);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
const rows = await q;
|
|
276
|
+
|
|
277
|
+
// Group rows into nested objects.
|
|
278
|
+
const basePk = root.pkField;
|
|
279
|
+
const baseMap = new Map<any, AnyObj>();
|
|
280
|
+
|
|
281
|
+
const ensureManyContainer = (obj: AnyObj, key: string) => {
|
|
282
|
+
if (!Array.isArray(obj[key])) {
|
|
283
|
+
obj[key] = [];
|
|
284
|
+
}
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
const ensureOneContainer = (obj: AnyObj, key: string) => {
|
|
288
|
+
if (!(key in obj)) {
|
|
289
|
+
obj[key] = null;
|
|
290
|
+
}
|
|
291
|
+
};
|
|
292
|
+
|
|
293
|
+
const manyIndexByPath = new Map<string, Map<any, AnyObj>>();
|
|
294
|
+
|
|
295
|
+
for (const row of rows) {
|
|
296
|
+
const baseRow = (row as any).base;
|
|
297
|
+
const baseId = (baseRow as any)[basePk];
|
|
298
|
+
if (baseId === undefined) {
|
|
299
|
+
continue;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
const baseObj = (() => {
|
|
303
|
+
const existing = baseMap.get(baseId);
|
|
304
|
+
if (existing) {
|
|
305
|
+
return existing;
|
|
306
|
+
}
|
|
307
|
+
const created = { ...baseRow };
|
|
308
|
+
baseMap.set(baseId, created);
|
|
309
|
+
return created;
|
|
310
|
+
})();
|
|
311
|
+
|
|
312
|
+
// Walk nodes, attach to parent objects.
|
|
313
|
+
for (const n of nodes) {
|
|
314
|
+
const data = (row as any)[n.aliasKey];
|
|
315
|
+
const relPath = n.path.join(".");
|
|
316
|
+
|
|
317
|
+
// Resolve parent container
|
|
318
|
+
const parentPath = n.parent ? n.parent.path.join(".") : "";
|
|
319
|
+
let parentObj: AnyObj = baseObj;
|
|
320
|
+
if (parentPath) {
|
|
321
|
+
// parent might be many; we attach to the last inserted parent instance.
|
|
322
|
+
const parentIndex = manyIndexByPath.get(parentPath);
|
|
323
|
+
if (parentIndex && parentIndex.size) {
|
|
324
|
+
// pick last inserted (Map preserves insertion order)
|
|
325
|
+
parentObj = Array.from(parentIndex.values()).at(-1) as AnyObj;
|
|
326
|
+
} else {
|
|
327
|
+
const parentKey = n.parent?.key;
|
|
328
|
+
parentObj = parentKey
|
|
329
|
+
? ((baseObj as any)[parentKey] ?? baseObj)
|
|
330
|
+
: baseObj;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
if (isAllNullRow(data)) {
|
|
335
|
+
if (n.relationType === "one") {
|
|
336
|
+
ensureOneContainer(parentObj, n.key);
|
|
337
|
+
} else {
|
|
338
|
+
ensureManyContainer(parentObj, n.key);
|
|
339
|
+
}
|
|
340
|
+
continue;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
const pk = (data as any)[n.pkField];
|
|
344
|
+
if (n.relationType === "one") {
|
|
345
|
+
parentObj[n.key] = { ...(data as any) };
|
|
346
|
+
} else {
|
|
347
|
+
ensureManyContainer(parentObj, n.key);
|
|
348
|
+
const indexKey = relPath;
|
|
349
|
+
if (!manyIndexByPath.has(indexKey)) {
|
|
350
|
+
manyIndexByPath.set(indexKey, new Map());
|
|
351
|
+
}
|
|
352
|
+
const idxMap = manyIndexByPath.get(indexKey)!;
|
|
353
|
+
if (!idxMap.has(pk)) {
|
|
354
|
+
const obj = { ...(data as any) };
|
|
355
|
+
idxMap.set(pk, obj);
|
|
356
|
+
(parentObj[n.key] as any[]).push(obj);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
const out = Array.from(baseMap.values());
|
|
363
|
+
return limitOne ? out[0] : out;
|
|
279
364
|
}
|