orchid-orm 1.3.15 → 1.4.16
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/CHANGELOG.md +20 -0
- package/dist/index.d.ts +59 -54
- package/dist/index.esm.js +777 -547
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +776 -546
- package/dist/index.js.map +1 -1
- package/jest-setup.ts +4 -0
- package/package.json +8 -4
- package/src/appCodeUpdater/appCodeUpdater.ts +19 -0
- package/src/appCodeUpdater/fileChanges.ts +41 -0
- package/src/appCodeUpdater/testUtils.ts +31 -0
- package/src/appCodeUpdater/tsUtils.ts +137 -0
- package/src/appCodeUpdater/updateMainFile.test.ts +230 -0
- package/src/appCodeUpdater/updateMainFile.ts +163 -0
- package/src/appCodeUpdater/updateTableFile.ts +19 -0
- package/src/index.ts +5 -1
- package/src/orm.test.ts +13 -13
- package/src/orm.ts +21 -21
- package/src/relations/belongsTo.test.ts +1 -1
- package/src/relations/belongsTo.ts +291 -186
- package/src/relations/hasAndBelongsToMany.test.ts +1 -1
- package/src/relations/hasAndBelongsToMany.ts +292 -218
- package/src/relations/hasMany.test.ts +16 -10
- package/src/relations/hasMany.ts +243 -172
- package/src/relations/hasOne.test.ts +10 -10
- package/src/relations/hasOne.ts +211 -138
- package/src/relations/relations.ts +85 -77
- package/src/relations/utils.ts +154 -4
- package/src/repo.test.ts +29 -29
- package/src/repo.ts +6 -6
- package/src/{model.test.ts → table.test.ts} +15 -15
- package/src/{model.ts → table.ts} +17 -17
- package/src/test-utils/test-db.ts +15 -15
- package/src/test-utils/{test-models.ts → test-tables.ts} +42 -42
- package/src/transaction.test.ts +1 -1
- package/src/transaction.ts +4 -4
- package/src/utils.ts +9 -0
- package/tsconfig.json +1 -0
|
@@ -1,19 +1,22 @@
|
|
|
1
1
|
import { RelationData, RelationThunkBase } from './relations';
|
|
2
|
-
import {
|
|
2
|
+
import { Table } from '../table';
|
|
3
3
|
import {
|
|
4
|
+
CreateCtx,
|
|
4
5
|
getQueryAs,
|
|
5
6
|
HasAndBelongsToManyRelation,
|
|
6
|
-
HasManyNestedInsert,
|
|
7
|
-
HasManyNestedUpdate,
|
|
8
7
|
MaybeArray,
|
|
9
8
|
NotFoundError,
|
|
10
9
|
pushQueryValue,
|
|
11
10
|
Query,
|
|
12
11
|
QueryBase,
|
|
13
12
|
toSqlCacheKey,
|
|
13
|
+
UpdateCtx,
|
|
14
|
+
VirtualColumn,
|
|
14
15
|
WhereArg,
|
|
15
16
|
WhereResult,
|
|
16
17
|
} from 'pqb';
|
|
18
|
+
import { hasRelationHandleCreate, hasRelationHandleUpdate } from './utils';
|
|
19
|
+
import { HasManyNestedInsert, HasManyNestedUpdate } from './hasMany';
|
|
17
20
|
|
|
18
21
|
export interface HasAndBelongsToMany extends RelationThunkBase {
|
|
19
22
|
type: 'hasAndBelongsToMany';
|
|
@@ -22,7 +25,7 @@ export interface HasAndBelongsToMany extends RelationThunkBase {
|
|
|
22
25
|
}
|
|
23
26
|
|
|
24
27
|
export type HasAndBelongsToManyInfo<
|
|
25
|
-
T extends
|
|
28
|
+
T extends Table,
|
|
26
29
|
Relation extends HasAndBelongsToMany,
|
|
27
30
|
> = {
|
|
28
31
|
params: Record<
|
|
@@ -41,12 +44,55 @@ type State = {
|
|
|
41
44
|
foreignKey: string;
|
|
42
45
|
associationPrimaryKey: string;
|
|
43
46
|
associationForeignKey: string;
|
|
47
|
+
foreignKeyFull: string;
|
|
48
|
+
associationPrimaryKeyFull: string;
|
|
49
|
+
associationForeignKeyFull: string;
|
|
44
50
|
};
|
|
45
51
|
|
|
52
|
+
class HasAndBelongsToManyVirtualColumn extends VirtualColumn {
|
|
53
|
+
private readonly nestedInsert: HasManyNestedInsert;
|
|
54
|
+
private readonly nestedUpdate: HasManyNestedUpdate;
|
|
55
|
+
|
|
56
|
+
constructor(private key: string, private state: State) {
|
|
57
|
+
super();
|
|
58
|
+
this.nestedInsert = nestedInsert(state);
|
|
59
|
+
this.nestedUpdate = nestedUpdate(state);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
create(
|
|
63
|
+
q: Query,
|
|
64
|
+
ctx: CreateCtx,
|
|
65
|
+
item: Record<string, unknown>,
|
|
66
|
+
rowIndex: number,
|
|
67
|
+
) {
|
|
68
|
+
hasRelationHandleCreate(
|
|
69
|
+
q,
|
|
70
|
+
ctx,
|
|
71
|
+
item,
|
|
72
|
+
rowIndex,
|
|
73
|
+
this.key,
|
|
74
|
+
this.state.primaryKey,
|
|
75
|
+
this.nestedInsert,
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
update(q: Query, ctx: UpdateCtx, set: Record<string, unknown>) {
|
|
80
|
+
hasRelationHandleUpdate(
|
|
81
|
+
q,
|
|
82
|
+
ctx,
|
|
83
|
+
set,
|
|
84
|
+
this.key,
|
|
85
|
+
this.state.primaryKey,
|
|
86
|
+
this.nestedUpdate,
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
46
91
|
export const makeHasAndBelongsToManyMethod = (
|
|
47
|
-
|
|
92
|
+
table: Query,
|
|
48
93
|
qb: Query,
|
|
49
94
|
relation: HasAndBelongsToMany,
|
|
95
|
+
relationName: string,
|
|
50
96
|
query: Query,
|
|
51
97
|
): RelationData => {
|
|
52
98
|
const {
|
|
@@ -61,14 +107,14 @@ export const makeHasAndBelongsToManyMethod = (
|
|
|
61
107
|
const associationForeignKeyFull = `${joinTable}.${afk}`;
|
|
62
108
|
const associationPrimaryKeyFull = `${getQueryAs(query)}.${apk}`;
|
|
63
109
|
|
|
64
|
-
const
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
[fk]:
|
|
110
|
+
const __table = Object.create(qb.__table);
|
|
111
|
+
__table.__table = __table;
|
|
112
|
+
__table.table = joinTable;
|
|
113
|
+
__table.shape = {
|
|
114
|
+
[fk]: table.shape[pk],
|
|
69
115
|
[afk]: query.shape[apk],
|
|
70
116
|
};
|
|
71
|
-
const subQuery = Object.create(
|
|
117
|
+
const subQuery = Object.create(__table);
|
|
72
118
|
subQuery.query = { ...subQuery.query };
|
|
73
119
|
|
|
74
120
|
const state: State = {
|
|
@@ -78,6 +124,9 @@ export const makeHasAndBelongsToManyMethod = (
|
|
|
78
124
|
foreignKey: fk,
|
|
79
125
|
associationPrimaryKey: apk,
|
|
80
126
|
associationForeignKey: afk,
|
|
127
|
+
foreignKeyFull,
|
|
128
|
+
associationForeignKeyFull,
|
|
129
|
+
associationPrimaryKeyFull,
|
|
81
130
|
};
|
|
82
131
|
|
|
83
132
|
return {
|
|
@@ -89,213 +138,7 @@ export const makeHasAndBelongsToManyMethod = (
|
|
|
89
138
|
}),
|
|
90
139
|
);
|
|
91
140
|
},
|
|
92
|
-
|
|
93
|
-
const connect = data.filter(
|
|
94
|
-
(
|
|
95
|
-
item,
|
|
96
|
-
): item is [
|
|
97
|
-
selfData: Record<string, unknown>,
|
|
98
|
-
relationData: {
|
|
99
|
-
connect: WhereArg<QueryBase>[];
|
|
100
|
-
},
|
|
101
|
-
] => Boolean(item[1].connect),
|
|
102
|
-
);
|
|
103
|
-
|
|
104
|
-
const t = query.transacting(q);
|
|
105
|
-
|
|
106
|
-
let connected: Record<string, unknown>[];
|
|
107
|
-
if (connect.length) {
|
|
108
|
-
connected = (await Promise.all(
|
|
109
|
-
connect.flatMap(([, { connect }]) =>
|
|
110
|
-
connect.map((item) => t.select(apk)._findBy(item)._take()),
|
|
111
|
-
),
|
|
112
|
-
)) as Record<string, unknown[]>[];
|
|
113
|
-
} else {
|
|
114
|
-
connected = [];
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
const connectOrCreate = data.filter(
|
|
118
|
-
(
|
|
119
|
-
item,
|
|
120
|
-
): item is [
|
|
121
|
-
Record<string, unknown>,
|
|
122
|
-
{
|
|
123
|
-
connectOrCreate: {
|
|
124
|
-
where: WhereArg<QueryBase>;
|
|
125
|
-
create: Record<string, unknown>;
|
|
126
|
-
}[];
|
|
127
|
-
},
|
|
128
|
-
] => Boolean(item[1].connectOrCreate),
|
|
129
|
-
);
|
|
130
|
-
|
|
131
|
-
let connectOrCreated: (Record<string, unknown> | undefined)[];
|
|
132
|
-
if (connectOrCreate.length) {
|
|
133
|
-
connectOrCreated = await Promise.all(
|
|
134
|
-
connectOrCreate.flatMap(([, { connectOrCreate }]) =>
|
|
135
|
-
connectOrCreate.map((item) =>
|
|
136
|
-
t.select(apk)._findBy(item.where)._takeOptional(),
|
|
137
|
-
),
|
|
138
|
-
),
|
|
139
|
-
);
|
|
140
|
-
} else {
|
|
141
|
-
connectOrCreated = [];
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
let connectOrCreateI = 0;
|
|
145
|
-
const create = data.filter(
|
|
146
|
-
(
|
|
147
|
-
item,
|
|
148
|
-
): item is [
|
|
149
|
-
Record<string, unknown>,
|
|
150
|
-
{
|
|
151
|
-
create?: Record<string, unknown>[];
|
|
152
|
-
connectOrCreate?: {
|
|
153
|
-
where: WhereArg<QueryBase>;
|
|
154
|
-
create: Record<string, unknown>;
|
|
155
|
-
}[];
|
|
156
|
-
},
|
|
157
|
-
] => {
|
|
158
|
-
if (item[1].connectOrCreate) {
|
|
159
|
-
const length = item[1].connectOrCreate.length;
|
|
160
|
-
connectOrCreateI += length;
|
|
161
|
-
for (let i = length; i > 0; i--) {
|
|
162
|
-
if (!connectOrCreated[connectOrCreateI - i]) return true;
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
return Boolean(item[1].create);
|
|
166
|
-
},
|
|
167
|
-
);
|
|
168
|
-
|
|
169
|
-
connectOrCreateI = 0;
|
|
170
|
-
let created: Record<string, unknown>[];
|
|
171
|
-
if (create.length) {
|
|
172
|
-
created = (await t
|
|
173
|
-
.select(apk)
|
|
174
|
-
._createMany(
|
|
175
|
-
create.flatMap(([, { create = [], connectOrCreate = [] }]) => [
|
|
176
|
-
...create,
|
|
177
|
-
...connectOrCreate
|
|
178
|
-
.filter(() => !connectOrCreated[connectOrCreateI++])
|
|
179
|
-
.map((item) => item.create),
|
|
180
|
-
]),
|
|
181
|
-
)) as Record<string, unknown>[];
|
|
182
|
-
} else {
|
|
183
|
-
created = [];
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
const allKeys = data as unknown as [
|
|
187
|
-
selfData: Record<string, unknown>,
|
|
188
|
-
relationKeys: Record<string, unknown>[],
|
|
189
|
-
][];
|
|
190
|
-
|
|
191
|
-
let createI = 0;
|
|
192
|
-
let connectI = 0;
|
|
193
|
-
connectOrCreateI = 0;
|
|
194
|
-
data.forEach(([, data], index) => {
|
|
195
|
-
if (data.create || data.connectOrCreate) {
|
|
196
|
-
if (data.create) {
|
|
197
|
-
const len = data.create.length;
|
|
198
|
-
allKeys[index][1] = created.slice(createI, createI + len);
|
|
199
|
-
createI += len;
|
|
200
|
-
}
|
|
201
|
-
if (data.connectOrCreate) {
|
|
202
|
-
const arr: Record<string, unknown>[] = [];
|
|
203
|
-
allKeys[index][1] = arr;
|
|
204
|
-
|
|
205
|
-
const len = data.connectOrCreate.length;
|
|
206
|
-
for (let i = 0; i < len; i++) {
|
|
207
|
-
const item = connectOrCreated[connectOrCreateI++];
|
|
208
|
-
if (item) {
|
|
209
|
-
arr.push(item);
|
|
210
|
-
} else {
|
|
211
|
-
arr.push(created[createI++]);
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
if (data.connect) {
|
|
218
|
-
const len = data.connect.length;
|
|
219
|
-
allKeys[index][1] = connected.slice(connectI, connectI + len);
|
|
220
|
-
connectI += len;
|
|
221
|
-
}
|
|
222
|
-
});
|
|
223
|
-
|
|
224
|
-
await subQuery
|
|
225
|
-
.transacting(q)
|
|
226
|
-
._count()
|
|
227
|
-
._createMany(
|
|
228
|
-
allKeys.flatMap(([selfData, relationKeys]) => {
|
|
229
|
-
const selfKey = selfData[pk];
|
|
230
|
-
return relationKeys.map((relationData) => ({
|
|
231
|
-
[fk]: selfKey,
|
|
232
|
-
[afk]: relationData[apk],
|
|
233
|
-
}));
|
|
234
|
-
}),
|
|
235
|
-
);
|
|
236
|
-
}) as HasManyNestedInsert,
|
|
237
|
-
nestedUpdate: (async (q, data, params) => {
|
|
238
|
-
if (params.create) {
|
|
239
|
-
const ids = await query
|
|
240
|
-
.transacting(q)
|
|
241
|
-
._pluck(apk)
|
|
242
|
-
._createMany(params.create);
|
|
243
|
-
|
|
244
|
-
await subQuery.transacting(q)._createMany(
|
|
245
|
-
data.flatMap((item) =>
|
|
246
|
-
ids.map((id) => ({
|
|
247
|
-
[fk]: item[pk],
|
|
248
|
-
[afk]: id,
|
|
249
|
-
})),
|
|
250
|
-
),
|
|
251
|
-
);
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
if (params.update) {
|
|
255
|
-
await (
|
|
256
|
-
query
|
|
257
|
-
.transacting(q)
|
|
258
|
-
._whereExists(subQuery, (q) =>
|
|
259
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
260
|
-
(q as any)
|
|
261
|
-
._on(associationForeignKeyFull, associationPrimaryKeyFull)
|
|
262
|
-
._where({
|
|
263
|
-
IN: {
|
|
264
|
-
columns: [foreignKeyFull],
|
|
265
|
-
values: [data.map((item) => item[pk])],
|
|
266
|
-
},
|
|
267
|
-
}),
|
|
268
|
-
)
|
|
269
|
-
._where(
|
|
270
|
-
Array.isArray(params.update.where)
|
|
271
|
-
? { OR: params.update.where }
|
|
272
|
-
: params.update.where,
|
|
273
|
-
) as WhereResult<Query>
|
|
274
|
-
)._update<WhereResult<Query>>(params.update.data);
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
if (params.disconnect) {
|
|
278
|
-
await queryJoinTable(state, q, data, params.disconnect)._delete();
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
if (params.delete) {
|
|
282
|
-
const j = queryJoinTable(state, q, data, params.delete);
|
|
283
|
-
|
|
284
|
-
const ids = await j._pluck(afk)._delete();
|
|
285
|
-
|
|
286
|
-
await queryRelatedTable(query, q, { [apk]: { in: ids } })._delete();
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
if (params.set) {
|
|
290
|
-
const j = queryJoinTable(state, q, data);
|
|
291
|
-
await j._delete();
|
|
292
|
-
delete j.query[toSqlCacheKey];
|
|
293
|
-
|
|
294
|
-
const ids = await queryRelatedTable(query, q, params.set)._pluck(apk);
|
|
295
|
-
|
|
296
|
-
await insertToJoinTable(state, j, data, ids);
|
|
297
|
-
}
|
|
298
|
-
}) as HasManyNestedUpdate,
|
|
141
|
+
virtualColumn: new HasAndBelongsToManyVirtualColumn(relationName, state),
|
|
299
142
|
// joinQuery can be a property of RelationQuery and be used by whereExists and other stuff which needs it
|
|
300
143
|
// and the chained query itself may be a query around this joinQuery
|
|
301
144
|
joinQuery(fromQuery, toQuery) {
|
|
@@ -396,3 +239,234 @@ const insertToJoinTable = (
|
|
|
396
239
|
),
|
|
397
240
|
);
|
|
398
241
|
};
|
|
242
|
+
|
|
243
|
+
const nestedInsert = ({
|
|
244
|
+
relatedTableQuery,
|
|
245
|
+
joinTableQuery,
|
|
246
|
+
primaryKey,
|
|
247
|
+
foreignKey,
|
|
248
|
+
associationPrimaryKey,
|
|
249
|
+
associationForeignKey,
|
|
250
|
+
}: State) => {
|
|
251
|
+
return (async (q, data) => {
|
|
252
|
+
const connect = data.filter(
|
|
253
|
+
(
|
|
254
|
+
item,
|
|
255
|
+
): item is [
|
|
256
|
+
selfData: Record<string, unknown>,
|
|
257
|
+
relationData: {
|
|
258
|
+
connect: WhereArg<QueryBase>[];
|
|
259
|
+
},
|
|
260
|
+
] => Boolean(item[1].connect),
|
|
261
|
+
);
|
|
262
|
+
|
|
263
|
+
const t = relatedTableQuery.transacting(q);
|
|
264
|
+
|
|
265
|
+
let connected: Record<string, unknown>[];
|
|
266
|
+
if (connect.length) {
|
|
267
|
+
connected = (await Promise.all(
|
|
268
|
+
connect.flatMap(([, { connect }]) =>
|
|
269
|
+
connect.map((item) =>
|
|
270
|
+
t.select(associationPrimaryKey)._findBy(item)._take(),
|
|
271
|
+
),
|
|
272
|
+
),
|
|
273
|
+
)) as Record<string, unknown[]>[];
|
|
274
|
+
} else {
|
|
275
|
+
connected = [];
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
const connectOrCreate = data.filter(
|
|
279
|
+
(
|
|
280
|
+
item,
|
|
281
|
+
): item is [
|
|
282
|
+
Record<string, unknown>,
|
|
283
|
+
{
|
|
284
|
+
connectOrCreate: {
|
|
285
|
+
where: WhereArg<QueryBase>;
|
|
286
|
+
create: Record<string, unknown>;
|
|
287
|
+
}[];
|
|
288
|
+
},
|
|
289
|
+
] => Boolean(item[1].connectOrCreate),
|
|
290
|
+
);
|
|
291
|
+
|
|
292
|
+
let connectOrCreated: (Record<string, unknown> | undefined)[];
|
|
293
|
+
if (connectOrCreate.length) {
|
|
294
|
+
connectOrCreated = await Promise.all(
|
|
295
|
+
connectOrCreate.flatMap(([, { connectOrCreate }]) =>
|
|
296
|
+
connectOrCreate.map((item) =>
|
|
297
|
+
t.select(associationPrimaryKey)._findBy(item.where)._takeOptional(),
|
|
298
|
+
),
|
|
299
|
+
),
|
|
300
|
+
);
|
|
301
|
+
} else {
|
|
302
|
+
connectOrCreated = [];
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
let connectOrCreateI = 0;
|
|
306
|
+
const create = data.filter(
|
|
307
|
+
(
|
|
308
|
+
item,
|
|
309
|
+
): item is [
|
|
310
|
+
Record<string, unknown>,
|
|
311
|
+
{
|
|
312
|
+
create?: Record<string, unknown>[];
|
|
313
|
+
connectOrCreate?: {
|
|
314
|
+
where: WhereArg<QueryBase>;
|
|
315
|
+
create: Record<string, unknown>;
|
|
316
|
+
}[];
|
|
317
|
+
},
|
|
318
|
+
] => {
|
|
319
|
+
if (item[1].connectOrCreate) {
|
|
320
|
+
const length = item[1].connectOrCreate.length;
|
|
321
|
+
connectOrCreateI += length;
|
|
322
|
+
for (let i = length; i > 0; i--) {
|
|
323
|
+
if (!connectOrCreated[connectOrCreateI - i]) return true;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
return Boolean(item[1].create);
|
|
327
|
+
},
|
|
328
|
+
);
|
|
329
|
+
|
|
330
|
+
connectOrCreateI = 0;
|
|
331
|
+
let created: Record<string, unknown>[];
|
|
332
|
+
if (create.length) {
|
|
333
|
+
created = (await t
|
|
334
|
+
.select(associationPrimaryKey)
|
|
335
|
+
._createMany(
|
|
336
|
+
create.flatMap(([, { create = [], connectOrCreate = [] }]) => [
|
|
337
|
+
...create,
|
|
338
|
+
...connectOrCreate
|
|
339
|
+
.filter(() => !connectOrCreated[connectOrCreateI++])
|
|
340
|
+
.map((item) => item.create),
|
|
341
|
+
]),
|
|
342
|
+
)) as Record<string, unknown>[];
|
|
343
|
+
} else {
|
|
344
|
+
created = [];
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
const allKeys = data as unknown as [
|
|
348
|
+
selfData: Record<string, unknown>,
|
|
349
|
+
relationKeys: Record<string, unknown>[],
|
|
350
|
+
][];
|
|
351
|
+
|
|
352
|
+
let createI = 0;
|
|
353
|
+
let connectI = 0;
|
|
354
|
+
connectOrCreateI = 0;
|
|
355
|
+
data.forEach(([, data], index) => {
|
|
356
|
+
if (data.create || data.connectOrCreate) {
|
|
357
|
+
if (data.create) {
|
|
358
|
+
const len = data.create.length;
|
|
359
|
+
allKeys[index][1] = created.slice(createI, createI + len);
|
|
360
|
+
createI += len;
|
|
361
|
+
}
|
|
362
|
+
if (data.connectOrCreate) {
|
|
363
|
+
const arr: Record<string, unknown>[] = [];
|
|
364
|
+
allKeys[index][1] = arr;
|
|
365
|
+
|
|
366
|
+
const len = data.connectOrCreate.length;
|
|
367
|
+
for (let i = 0; i < len; i++) {
|
|
368
|
+
const item = connectOrCreated[connectOrCreateI++];
|
|
369
|
+
if (item) {
|
|
370
|
+
arr.push(item);
|
|
371
|
+
} else {
|
|
372
|
+
arr.push(created[createI++]);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
if (data.connect) {
|
|
379
|
+
const len = data.connect.length;
|
|
380
|
+
allKeys[index][1] = connected.slice(connectI, connectI + len);
|
|
381
|
+
connectI += len;
|
|
382
|
+
}
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
await joinTableQuery
|
|
386
|
+
.transacting(q)
|
|
387
|
+
._count()
|
|
388
|
+
._createMany(
|
|
389
|
+
allKeys.flatMap(([selfData, relationKeys]) => {
|
|
390
|
+
const selfKey = selfData[primaryKey];
|
|
391
|
+
return relationKeys.map((relationData) => ({
|
|
392
|
+
[foreignKey]: selfKey,
|
|
393
|
+
[associationForeignKey]: relationData[associationPrimaryKey],
|
|
394
|
+
}));
|
|
395
|
+
}),
|
|
396
|
+
);
|
|
397
|
+
}) as HasManyNestedInsert;
|
|
398
|
+
};
|
|
399
|
+
|
|
400
|
+
const nestedUpdate = (state: State) => {
|
|
401
|
+
return (async (q, data, params) => {
|
|
402
|
+
if (params.create) {
|
|
403
|
+
const ids = await state.relatedTableQuery
|
|
404
|
+
.transacting(q)
|
|
405
|
+
._pluck(state.associationPrimaryKey)
|
|
406
|
+
._createMany(params.create);
|
|
407
|
+
|
|
408
|
+
await state.joinTableQuery.transacting(q)._createMany(
|
|
409
|
+
data.flatMap((item) =>
|
|
410
|
+
ids.map((id) => ({
|
|
411
|
+
[state.foreignKey]: item[state.primaryKey],
|
|
412
|
+
[state.associationForeignKey]: id,
|
|
413
|
+
})),
|
|
414
|
+
),
|
|
415
|
+
);
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
if (params.update) {
|
|
419
|
+
await (
|
|
420
|
+
state.relatedTableQuery
|
|
421
|
+
.transacting(q)
|
|
422
|
+
._whereExists(state.joinTableQuery, (q) =>
|
|
423
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
424
|
+
(q as any)
|
|
425
|
+
._on(
|
|
426
|
+
state.associationForeignKeyFull,
|
|
427
|
+
state.associationPrimaryKeyFull,
|
|
428
|
+
)
|
|
429
|
+
._where({
|
|
430
|
+
IN: {
|
|
431
|
+
columns: [state.foreignKeyFull],
|
|
432
|
+
values: [data.map((item) => item[state.primaryKey])],
|
|
433
|
+
},
|
|
434
|
+
}),
|
|
435
|
+
)
|
|
436
|
+
._where(
|
|
437
|
+
Array.isArray(params.update.where)
|
|
438
|
+
? { OR: params.update.where }
|
|
439
|
+
: params.update.where,
|
|
440
|
+
) as WhereResult<Query>
|
|
441
|
+
)._update<WhereResult<Query>>(params.update.data);
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
if (params.disconnect) {
|
|
445
|
+
await queryJoinTable(state, q, data, params.disconnect)._delete();
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
if (params.delete) {
|
|
449
|
+
const j = queryJoinTable(state, q, data, params.delete);
|
|
450
|
+
|
|
451
|
+
const ids = await j._pluck(state.associationForeignKey)._delete();
|
|
452
|
+
|
|
453
|
+
await queryRelatedTable(state.relatedTableQuery, q, {
|
|
454
|
+
[state.associationPrimaryKey]: { in: ids },
|
|
455
|
+
})._delete();
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
if (params.set) {
|
|
459
|
+
const j = queryJoinTable(state, q, data);
|
|
460
|
+
await j._delete();
|
|
461
|
+
delete j.query[toSqlCacheKey];
|
|
462
|
+
|
|
463
|
+
const ids = await queryRelatedTable(
|
|
464
|
+
state.relatedTableQuery,
|
|
465
|
+
q,
|
|
466
|
+
params.set,
|
|
467
|
+
)._pluck(state.associationPrimaryKey);
|
|
468
|
+
|
|
469
|
+
await insertToJoinTable(state, j, data, ids);
|
|
470
|
+
}
|
|
471
|
+
}) as HasManyNestedUpdate;
|
|
472
|
+
};
|
|
@@ -9,7 +9,13 @@ import {
|
|
|
9
9
|
useTestDatabase,
|
|
10
10
|
} from '../test-utils/test-utils';
|
|
11
11
|
import { RelationQuery } from 'pqb';
|
|
12
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
Chat,
|
|
14
|
+
Message,
|
|
15
|
+
BaseTable,
|
|
16
|
+
Profile,
|
|
17
|
+
User,
|
|
18
|
+
} from '../test-utils/test-tables';
|
|
13
19
|
import { orchidORM } from '../orm';
|
|
14
20
|
|
|
15
21
|
describe('hasMany', () => {
|
|
@@ -1653,8 +1659,8 @@ describe('hasMany', () => {
|
|
|
1653
1659
|
});
|
|
1654
1660
|
|
|
1655
1661
|
describe('hasMany through', () => {
|
|
1656
|
-
it('should resolve recursive situation when both
|
|
1657
|
-
class Post extends
|
|
1662
|
+
it('should resolve recursive situation when both tables depends on each other', () => {
|
|
1663
|
+
class Post extends BaseTable {
|
|
1658
1664
|
table = 'post';
|
|
1659
1665
|
columns = this.setColumns((t) => ({
|
|
1660
1666
|
id: t.serial().primaryKey(),
|
|
@@ -1673,7 +1679,7 @@ describe('hasMany through', () => {
|
|
|
1673
1679
|
};
|
|
1674
1680
|
}
|
|
1675
1681
|
|
|
1676
|
-
class Tag extends
|
|
1682
|
+
class Tag extends BaseTable {
|
|
1677
1683
|
table = 'tag';
|
|
1678
1684
|
columns = this.setColumns((t) => ({
|
|
1679
1685
|
id: t.serial().primaryKey(),
|
|
@@ -1692,7 +1698,7 @@ describe('hasMany through', () => {
|
|
|
1692
1698
|
};
|
|
1693
1699
|
}
|
|
1694
1700
|
|
|
1695
|
-
class PostTag extends
|
|
1701
|
+
class PostTag extends BaseTable {
|
|
1696
1702
|
table = 'postTag';
|
|
1697
1703
|
columns = this.setColumns((t) => ({
|
|
1698
1704
|
postId: t.integer().foreignKey(() => Post, 'id'),
|
|
@@ -1730,7 +1736,7 @@ describe('hasMany through', () => {
|
|
|
1730
1736
|
});
|
|
1731
1737
|
|
|
1732
1738
|
it('should throw if through relation is not defined', () => {
|
|
1733
|
-
class Post extends
|
|
1739
|
+
class Post extends BaseTable {
|
|
1734
1740
|
table = 'post';
|
|
1735
1741
|
columns = this.setColumns((t) => ({
|
|
1736
1742
|
id: t.serial().primaryKey(),
|
|
@@ -1744,7 +1750,7 @@ describe('hasMany through', () => {
|
|
|
1744
1750
|
};
|
|
1745
1751
|
}
|
|
1746
1752
|
|
|
1747
|
-
class Tag extends
|
|
1753
|
+
class Tag extends BaseTable {
|
|
1748
1754
|
table = 'tag';
|
|
1749
1755
|
columns = this.setColumns((t) => ({
|
|
1750
1756
|
id: t.serial().primaryKey(),
|
|
@@ -1768,7 +1774,7 @@ describe('hasMany through', () => {
|
|
|
1768
1774
|
});
|
|
1769
1775
|
|
|
1770
1776
|
it('should throw if source relation is not defined', () => {
|
|
1771
|
-
class Post extends
|
|
1777
|
+
class Post extends BaseTable {
|
|
1772
1778
|
table = 'post';
|
|
1773
1779
|
columns = this.setColumns((t) => ({
|
|
1774
1780
|
id: t.serial().primaryKey(),
|
|
@@ -1787,14 +1793,14 @@ describe('hasMany through', () => {
|
|
|
1787
1793
|
};
|
|
1788
1794
|
}
|
|
1789
1795
|
|
|
1790
|
-
class Tag extends
|
|
1796
|
+
class Tag extends BaseTable {
|
|
1791
1797
|
table = 'tag';
|
|
1792
1798
|
columns = this.setColumns((t) => ({
|
|
1793
1799
|
id: t.serial().primaryKey(),
|
|
1794
1800
|
}));
|
|
1795
1801
|
}
|
|
1796
1802
|
|
|
1797
|
-
class PostTag extends
|
|
1803
|
+
class PostTag extends BaseTable {
|
|
1798
1804
|
table = 'postTag';
|
|
1799
1805
|
columns = this.setColumns((t) => ({
|
|
1800
1806
|
postId: t.integer().foreignKey(() => Post, 'id'),
|