orchid-orm 1.14.4 → 1.15.1

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.js CHANGED
@@ -82,7 +82,6 @@ const create = (columnTypes, filePathOrStack, snakeCase, nowSQL, exportAs = "Bas
82
82
  belongsTo(fn, options) {
83
83
  return {
84
84
  type: "belongsTo",
85
- returns: "one",
86
85
  fn,
87
86
  options
88
87
  };
@@ -90,7 +89,6 @@ const create = (columnTypes, filePathOrStack, snakeCase, nowSQL, exportAs = "Bas
90
89
  hasOne(fn, options) {
91
90
  return {
92
91
  type: "hasOne",
93
- returns: "one",
94
92
  fn,
95
93
  options
96
94
  };
@@ -98,7 +96,6 @@ const create = (columnTypes, filePathOrStack, snakeCase, nowSQL, exportAs = "Bas
98
96
  hasMany(fn, options) {
99
97
  return {
100
98
  type: "hasMany",
101
- returns: "many",
102
99
  fn,
103
100
  options
104
101
  };
@@ -106,7 +103,6 @@ const create = (columnTypes, filePathOrStack, snakeCase, nowSQL, exportAs = "Bas
106
103
  hasAndBelongsToMany(fn, options) {
107
104
  return {
108
105
  type: "hasAndBelongsToMany",
109
- returns: "many",
110
106
  fn,
111
107
  options
112
108
  };
@@ -117,6 +113,98 @@ const create = (columnTypes, filePathOrStack, snakeCase, nowSQL, exportAs = "Bas
117
113
  return base;
118
114
  };
119
115
 
116
+ const getThroughRelation = (table, through) => {
117
+ var _a;
118
+ return (_a = table.relations[through]) == null ? void 0 : _a.relationConfig;
119
+ };
120
+ const getSourceRelation = (throughRelation, source) => {
121
+ var _a;
122
+ return (_a = throughRelation.table.relations[source]) == null ? void 0 : _a.relationConfig;
123
+ };
124
+ const hasRelationHandleCreate = (q, ctx, item, rowIndex, key, primaryKeys, nestedInsert) => {
125
+ const value = item[key];
126
+ if ((!value.create || Array.isArray(value.create) && value.create.length === 0) && (!value.connect || Array.isArray(value.connect) && value.connect.length === 0) && (!value.connectOrCreate || Array.isArray(value.connectOrCreate) && value.connectOrCreate.length === 0))
127
+ return;
128
+ const store = ctx;
129
+ if (!store.hasRelation)
130
+ store.hasRelation = {};
131
+ const values = [rowIndex, value];
132
+ if (store.hasRelation[key]) {
133
+ store.hasRelation[key].push(values);
134
+ return;
135
+ }
136
+ q.q.wrapInTransaction = true;
137
+ const relationData = [values];
138
+ store.hasRelation[key] = relationData;
139
+ q._afterCreate(
140
+ primaryKeys,
141
+ (rows, q2) => nestedInsert(
142
+ q2,
143
+ relationData.map(([rowIndex2, data]) => [
144
+ rows[rowIndex2],
145
+ data
146
+ ])
147
+ )
148
+ );
149
+ };
150
+ const hasRelationHandleUpdate = (q, set, key, primaryKeys, nestedUpdate) => {
151
+ const value = set[key];
152
+ if (!value.set && !("upsert" in value) && (!value.disconnect || Array.isArray(value.disconnect) && value.disconnect.length === 0) && (!value.delete || Array.isArray(value.delete) && value.delete.length === 0) && (!value.update || Array.isArray(value.update.where) && value.update.where.length === 0) && (!value.create || Array.isArray(value.create) && value.create.length === 0))
153
+ return;
154
+ selectIfNotSelected(q, primaryKeys);
155
+ q.q.wrapInTransaction = true;
156
+ q._afterUpdate(q.primaryKeys, (rows, q2) => {
157
+ return nestedUpdate(
158
+ q2,
159
+ rows,
160
+ value
161
+ );
162
+ });
163
+ };
164
+ const selectIfNotSelected = (q, columns) => {
165
+ const select = q.q.select || [];
166
+ if (!select.includes("*")) {
167
+ for (const column of columns) {
168
+ if (!select.includes(column)) {
169
+ select.push(column);
170
+ }
171
+ }
172
+ q.q.select = select;
173
+ }
174
+ };
175
+ const relationWhere = (len, keys, valueKeys) => (params) => {
176
+ const obj = {};
177
+ for (let i = 0; i < len; i++) {
178
+ obj[keys[i]] = params[valueKeys[i]];
179
+ }
180
+ return obj;
181
+ };
182
+ function joinHasThrough(q, fromQuery, toQuery, throughRelation, sourceRelation) {
183
+ return q.whereExists(
184
+ throughRelation.joinQuery(fromQuery, throughRelation.query),
185
+ () => {
186
+ const as = pqb.getQueryAs(toQuery);
187
+ return sourceRelation.joinQuery(
188
+ throughRelation.query,
189
+ sourceRelation.query.as(as)
190
+ );
191
+ }
192
+ );
193
+ }
194
+ function joinHasRelation(fromQuery, toQuery, primaryKeys, foreignKeys, len) {
195
+ const q = toQuery.clone();
196
+ pqb.setQueryObjectValue(
197
+ q,
198
+ "joinedShapes",
199
+ fromQuery.q.as || fromQuery.table,
200
+ fromQuery.q.shape
201
+ );
202
+ for (let i = 0; i < len; i++) {
203
+ pqb.pushQueryOn(q, fromQuery, toQuery, foreignKeys[i], primaryKeys[i]);
204
+ }
205
+ return q;
206
+ }
207
+
120
208
  class BelongsToVirtualColumn extends pqb.VirtualColumn {
121
209
  constructor(key, state) {
122
210
  super();
@@ -128,16 +216,19 @@ class BelongsToVirtualColumn extends pqb.VirtualColumn {
128
216
  create(q, ctx, item, rowIndex) {
129
217
  const {
130
218
  key,
131
- state: { foreignKey, primaryKey }
219
+ state: { primaryKeys, foreignKeys }
132
220
  } = this;
133
- let columnIndex = ctx.columns.get(foreignKey);
134
- if (columnIndex === void 0) {
135
- ctx.columns.set(foreignKey, columnIndex = ctx.columns.size);
136
- }
221
+ const columnIndexes = foreignKeys.map((key2) => {
222
+ let index = ctx.columns.get(key2);
223
+ if (index === void 0) {
224
+ ctx.columns.set(key2, index = ctx.columns.size);
225
+ }
226
+ return index;
227
+ });
137
228
  const store = ctx;
138
229
  if (!store.belongsTo)
139
230
  store.belongsTo = {};
140
- const values = [rowIndex, columnIndex, item[key]];
231
+ const values = [rowIndex, columnIndexes, item[key]];
141
232
  if (store.belongsTo[key]) {
142
233
  store.belongsTo[key].push(values);
143
234
  return;
@@ -151,9 +242,13 @@ class BelongsToVirtualColumn extends pqb.VirtualColumn {
151
242
  relationData.map(([, , data]) => data)
152
243
  );
153
244
  const { values: values2 } = q2.q;
154
- relationData.forEach(([rowIndex2, columnIndex2], index) => {
155
- values2[rowIndex2][columnIndex2] = inserted[index][primaryKey];
156
- });
245
+ for (let i = 0, len = relationData.length; i < len; i++) {
246
+ const [rowIndex2, columnIndexes2] = relationData[i];
247
+ const row = values2[rowIndex2];
248
+ for (let c = 0, len2 = columnIndexes2.length; c < len2; c++) {
249
+ row[columnIndexes2[c]] = inserted[i][primaryKeys[c]];
250
+ }
251
+ }
157
252
  });
158
253
  }
159
254
  update(q, ctx, set) {
@@ -165,68 +260,91 @@ class BelongsToVirtualColumn extends pqb.VirtualColumn {
165
260
  }
166
261
  }
167
262
  const makeBelongsToMethod = (relation, relationName, query) => {
168
- const { primaryKey, foreignKey } = relation.options;
169
- const state = { query, primaryKey, foreignKey };
263
+ const primaryKeys = "columns" in relation.options ? relation.options.references : [relation.options.primaryKey];
264
+ const foreignKeys = "columns" in relation.options ? relation.options.columns : [relation.options.foreignKey];
265
+ const len = primaryKeys.length;
266
+ const state = { query, primaryKeys, foreignKeys, len };
267
+ const makeWhere = relationWhere(len, primaryKeys, foreignKeys);
268
+ const join = (fromQuery, toQuery, primaryKeys2, foreignKeys2) => {
269
+ const q = toQuery.clone();
270
+ pqb.setQueryObjectValue(
271
+ q,
272
+ "joinedShapes",
273
+ fromQuery.q.as || fromQuery.table,
274
+ fromQuery.q.shape
275
+ );
276
+ for (let i = 0; i < len; i++) {
277
+ pqb.pushQueryOn(q, fromQuery, toQuery, primaryKeys2[i], foreignKeys2[i]);
278
+ }
279
+ return q;
280
+ };
170
281
  return {
171
282
  returns: "one",
172
- method: (params) => {
173
- return query.where({ [primaryKey]: params[foreignKey] });
283
+ method(params) {
284
+ return query.where(makeWhere(params));
174
285
  },
175
286
  virtualColumn: new BelongsToVirtualColumn(relationName, state),
176
287
  joinQuery(fromQuery, toQuery) {
177
- return pqb.addQueryOn(toQuery, fromQuery, toQuery, primaryKey, foreignKey);
288
+ return join(fromQuery, toQuery, primaryKeys, foreignKeys);
178
289
  },
179
290
  reverseJoin(fromQuery, toQuery) {
180
- return pqb.addQueryOn(fromQuery, toQuery, fromQuery, foreignKey, primaryKey);
291
+ return join(toQuery, fromQuery, foreignKeys, primaryKeys);
181
292
  }
182
293
  };
183
294
  };
184
- const nestedInsert$3 = ({ query, primaryKey }) => {
295
+ const nestedInsert$3 = ({ query, primaryKeys }) => {
185
296
  return async (_, data) => {
186
- const connectOrCreate = data.filter(
187
- (item) => Boolean(item.connectOrCreate)
188
- );
189
297
  const t = query.clone();
298
+ const items = [];
299
+ for (const item of data) {
300
+ if (item.connectOrCreate) {
301
+ items.push(item);
302
+ }
303
+ }
190
304
  let connectOrCreated;
191
- if (connectOrCreate.length) {
192
- connectOrCreated = await Promise.all(
193
- connectOrCreate.map(
194
- (item) => t.findBy(item.connectOrCreate.where)._takeOptional()
195
- )
196
- );
305
+ if (items.length) {
306
+ for (let i = 0, len = items.length; i < len; i++) {
307
+ items[i] = t.findByOptional(
308
+ items[i].connectOrCreate.where
309
+ );
310
+ }
311
+ connectOrCreated = await Promise.all(items);
197
312
  } else {
198
- connectOrCreated = [];
313
+ connectOrCreated = orchidCore.emptyArray;
199
314
  }
200
315
  let connectOrCreatedI = 0;
201
- const create = data.filter(
202
- (item) => {
203
- if (item.connectOrCreate) {
204
- return !connectOrCreated[connectOrCreatedI++];
205
- } else {
206
- return Boolean(item.create);
207
- }
316
+ items.length = 0;
317
+ for (const item of data) {
318
+ if (item.connectOrCreate) {
319
+ if (!connectOrCreated[connectOrCreatedI++])
320
+ items.push(item);
321
+ } else if (item.create) {
322
+ items.push(item);
208
323
  }
209
- );
324
+ }
210
325
  let created;
211
- if (create.length) {
212
- created = await t.select(primaryKey)._createMany(
213
- create.map(
214
- (item) => "create" in item ? item.create : item.connectOrCreate.create
215
- )
216
- );
326
+ if (items.length) {
327
+ for (let i = 0, len = items.length; i < len; i++) {
328
+ items[i] = "create" in items[i] ? items[i].create : items[i].connectOrCreate.create;
329
+ }
330
+ created = await t.select(...primaryKeys)._createMany(items);
217
331
  } else {
218
- created = [];
332
+ created = orchidCore.emptyArray;
333
+ }
334
+ items.length = 0;
335
+ for (const item of data) {
336
+ if (item.connect) {
337
+ items.push(item);
338
+ }
219
339
  }
220
- const connect = data.filter(
221
- (item) => Boolean(item.connect)
222
- );
223
340
  let connected;
224
- if (connect.length) {
225
- connected = await Promise.all(
226
- connect.map((item) => t.findBy(item.connect)._take())
227
- );
341
+ if (items.length) {
342
+ for (let i = 0, len = items.length; i < len; i++) {
343
+ items[i] = t.findBy(items[i].connect);
344
+ }
345
+ connected = await Promise.all(items);
228
346
  } else {
229
- connected = [];
347
+ connected = orchidCore.emptyArray;
230
348
  }
231
349
  let createdI = 0;
232
350
  let connectedI = 0;
@@ -236,125 +354,122 @@ const nestedInsert$3 = ({ query, primaryKey }) => {
236
354
  });
237
355
  };
238
356
  };
239
- const nestedUpdate$3 = ({ query, primaryKey, foreignKey }) => {
357
+ const nestedUpdate$3 = ({ query, primaryKeys, foreignKeys, len }) => {
240
358
  return (q, update, params, state) => {
241
- var _a, _b, _c;
359
+ var _a;
242
360
  if (params.upsert && pqb.isQueryReturnsAll(q)) {
243
361
  throw new Error("`upsert` option is not allowed in a batch update");
244
362
  }
245
363
  let idsForDelete;
246
364
  q._beforeUpdate(async (q2) => {
247
365
  if (params.disconnect) {
248
- update[foreignKey] = null;
366
+ for (const key of foreignKeys) {
367
+ update[key] = null;
368
+ }
249
369
  } else if (params.set) {
250
- if (primaryKey in params.set) {
251
- update[foreignKey] = params.set[primaryKey];
252
- } else {
253
- update[foreignKey] = await query.findBy(params.set)._get(primaryKey);
370
+ let loadPrimaryKeys;
371
+ let loadForeignKeys;
372
+ for (let i = 0; i < len; i++) {
373
+ const primaryKey = primaryKeys[i];
374
+ if (primaryKey in params.set) {
375
+ update[foreignKeys[i]] = params.set[primaryKey];
376
+ } else {
377
+ (loadPrimaryKeys != null ? loadPrimaryKeys : loadPrimaryKeys = []).push(primaryKey);
378
+ (loadForeignKeys != null ? loadForeignKeys : loadForeignKeys = []).push(foreignKeys[i]);
379
+ }
380
+ }
381
+ if (loadPrimaryKeys) {
382
+ const record = await query.select(...loadPrimaryKeys)._findBy(params.set);
383
+ for (let i = 0, len2 = loadPrimaryKeys.length; i < len2; i++) {
384
+ update[loadForeignKeys[i]] = record[loadPrimaryKeys[i]];
385
+ }
254
386
  }
255
387
  } else if (params.create) {
256
- update[foreignKey] = await query.get(primaryKey)._create(params.create);
388
+ const q3 = query.clone();
389
+ q3.q.select = primaryKeys;
390
+ const record = await q3._create(params.create);
391
+ for (let i = 0; i < len; i++) {
392
+ update[foreignKeys[i]] = record[primaryKeys[i]];
393
+ }
257
394
  } else if (params.delete) {
258
395
  const selectQuery = q2.clone();
259
396
  selectQuery.q.type = void 0;
260
- idsForDelete = await selectQuery._pluck(foreignKey);
261
- update[foreignKey] = null;
397
+ selectQuery.q.distinct = orchidCore.emptyArray;
398
+ idsForDelete = await selectQuery._rows();
399
+ for (const foreignKey of foreignKeys) {
400
+ update[foreignKey] = null;
401
+ }
262
402
  }
263
403
  });
264
404
  const { upsert } = params;
265
405
  if (upsert || params.update || params.delete) {
266
- if (!((_a = q.q.select) == null ? void 0 : _a.includes("*")) && !((_b = q.q.select) == null ? void 0 : _b.includes(foreignKey))) {
267
- q._select(foreignKey);
268
- }
406
+ selectIfNotSelected(q, foreignKeys);
269
407
  }
270
408
  if (upsert) {
271
- ((_c = state.queries) != null ? _c : state.queries = []).push(async (queryResult) => {
409
+ ((_a = state.queries) != null ? _a : state.queries = []).push(async (queryResult) => {
272
410
  var _a2;
273
- const id = queryResult.rows[0][foreignKey];
274
- if (id !== null) {
275
- await query.findBy({ [primaryKey]: id })._update(upsert.update);
411
+ const row = queryResult.rows[0];
412
+ let obj = {};
413
+ for (let i = 0; i < len; i++) {
414
+ const id = row[foreignKeys[i]];
415
+ if (id === null) {
416
+ obj = void 0;
417
+ break;
418
+ }
419
+ obj[primaryKeys[i]] = id;
420
+ }
421
+ if (obj) {
422
+ await query.findBy(obj)._update(upsert.update);
276
423
  } else {
277
424
  const data = typeof upsert.create === "function" ? upsert.create() : upsert.create;
278
- const result = await query.select(primaryKey)._create(data);
279
- ((_a2 = state.updateData) != null ? _a2 : state.updateData = {})[foreignKey] = result[primaryKey];
425
+ const result = await query.select(...primaryKeys)._create(data);
426
+ for (let i = 0; i < len; i++) {
427
+ ((_a2 = state.updateData) != null ? _a2 : state.updateData = {})[foreignKeys[i]] = result[primaryKeys[i]];
428
+ }
280
429
  }
281
430
  });
282
431
  } else if (params.delete || params.update) {
283
432
  q._afterUpdate([], async (data) => {
284
- const id = params.delete ? { in: idsForDelete } : Array.isArray(data) ? data.length === 0 ? null : {
285
- in: data.map((item) => item[foreignKey]).filter((id2) => id2 !== null)
286
- } : data[foreignKey];
287
- if (id !== void 0 && id !== null) {
288
- const t = query.where({
289
- [primaryKey]: id
290
- });
291
- if (params.delete) {
292
- await t._delete();
293
- } else if (params.update) {
294
- await t._update(params.update);
433
+ let ids;
434
+ if (params.delete) {
435
+ ids = idsForDelete;
436
+ } else {
437
+ ids = [];
438
+ for (const item of data) {
439
+ let row;
440
+ for (const foreignKey of foreignKeys) {
441
+ const id = item[foreignKey];
442
+ if (id === null) {
443
+ row = void 0;
444
+ break;
445
+ } else {
446
+ (row != null ? row : row = []).push(id);
447
+ }
448
+ }
449
+ if (row)
450
+ ids.push(row);
295
451
  }
296
452
  }
453
+ if (!(ids == null ? void 0 : ids.length))
454
+ return;
455
+ const t = query.whereIn(
456
+ primaryKeys,
457
+ ids
458
+ );
459
+ if (params.delete) {
460
+ await t._delete();
461
+ } else {
462
+ await t._update(
463
+ params.update
464
+ );
465
+ }
297
466
  });
298
467
  }
299
468
  return !params.update && !params.upsert;
300
469
  };
301
470
  };
302
471
 
303
- const getThroughRelation = (table, through) => {
304
- var _a;
305
- return (_a = table.relations[through]) == null ? void 0 : _a.relationConfig;
306
- };
307
- const getSourceRelation = (throughRelation, source) => {
308
- var _a;
309
- return (_a = throughRelation.table.relations[source]) == null ? void 0 : _a.relationConfig;
310
- };
311
- const hasRelationHandleCreate = (q, ctx, item, rowIndex, key, primaryKey, nestedInsert) => {
312
- const value = item[key];
313
- if ((!value.create || Array.isArray(value.create) && value.create.length === 0) && (!value.connect || Array.isArray(value.connect) && value.connect.length === 0) && (!value.connectOrCreate || Array.isArray(value.connectOrCreate) && value.connectOrCreate.length === 0))
314
- return;
315
- const store = ctx;
316
- if (!store.hasRelation)
317
- store.hasRelation = {};
318
- const values = [rowIndex, value];
319
- if (store.hasRelation[key]) {
320
- store.hasRelation[key].push(values);
321
- return;
322
- }
323
- q.q.wrapInTransaction = true;
324
- const relationData = [values];
325
- store.hasRelation[key] = relationData;
326
- q._afterCreate(
327
- [primaryKey],
328
- (rows, q2) => nestedInsert(
329
- q2,
330
- relationData.map(([rowIndex2, data]) => [
331
- rows[rowIndex2],
332
- data
333
- ])
334
- )
335
- );
336
- };
337
- const hasRelationHandleUpdate = (q, set, key, primaryKey, nestedUpdate) => {
338
- var _a, _b;
339
- const value = set[key];
340
- if (!value.set && !("upsert" in value) && (!value.disconnect || Array.isArray(value.disconnect) && value.disconnect.length === 0) && (!value.delete || Array.isArray(value.delete) && value.delete.length === 0) && (!value.update || Array.isArray(value.update.where) && value.update.where.length === 0) && (!value.create || Array.isArray(value.create) && value.create.length === 0))
341
- return;
342
- if (!((_a = q.q.select) == null ? void 0 : _a.includes("*")) && !((_b = q.q.select) == null ? void 0 : _b.includes(primaryKey))) {
343
- q._select(primaryKey);
344
- }
345
- q.q.wrapInTransaction = true;
346
- q._afterUpdate(q.primaryKeys, (rows, q2) => {
347
- return nestedUpdate(
348
- q2,
349
- rows,
350
- value
351
- );
352
- });
353
- };
354
-
355
472
  var __defProp$5 = Object.defineProperty;
356
- var __defProps$3 = Object.defineProperties;
357
- var __getOwnPropDescs$3 = Object.getOwnPropertyDescriptors;
358
473
  var __getOwnPropSymbols$5 = Object.getOwnPropertySymbols;
359
474
  var __hasOwnProp$5 = Object.prototype.hasOwnProperty;
360
475
  var __propIsEnum$5 = Object.prototype.propertyIsEnumerable;
@@ -370,7 +485,6 @@ var __spreadValues$5 = (a, b) => {
370
485
  }
371
486
  return a;
372
487
  };
373
- var __spreadProps$3 = (a, b) => __defProps$3(a, __getOwnPropDescs$3(b));
374
488
  class HasOneVirtualColumn extends pqb.VirtualColumn {
375
489
  constructor(key, state) {
376
490
  super();
@@ -386,7 +500,7 @@ class HasOneVirtualColumn extends pqb.VirtualColumn {
386
500
  item,
387
501
  rowIndex,
388
502
  this.key,
389
- this.state.primaryKey,
503
+ this.state.primaryKeys,
390
504
  this.nestedInsert
391
505
  );
392
506
  }
@@ -400,7 +514,7 @@ class HasOneVirtualColumn extends pqb.VirtualColumn {
400
514
  q,
401
515
  set,
402
516
  this.key,
403
- this.state.primaryKey,
517
+ this.state.primaryKeys,
404
518
  this.nestedUpdate
405
519
  );
406
520
  }
@@ -422,46 +536,49 @@ const makeHasOneMethod = (table, relation, relationName, query) => {
422
536
  );
423
537
  },
424
538
  joinQuery(fromQuery, toQuery) {
425
- return toQuery.whereExists(
426
- throughRelation.joinQuery(fromQuery, throughRelation.query),
427
- () => {
428
- const as = pqb.getQueryAs(toQuery);
429
- return sourceRelation.joinQuery(
430
- throughRelation.query,
431
- sourceRelation.query.as(as)
432
- );
433
- }
539
+ return joinHasThrough(
540
+ toQuery,
541
+ fromQuery,
542
+ toQuery,
543
+ throughRelation,
544
+ sourceRelation
434
545
  );
435
546
  },
436
547
  reverseJoin(fromQuery, toQuery) {
437
- return fromQuery.whereExists(
438
- throughRelation.joinQuery(fromQuery, throughRelation.query),
439
- () => {
440
- const as = pqb.getQueryAs(toQuery);
441
- return sourceRelation.joinQuery(
442
- throughRelation.query,
443
- sourceRelation.query.as(as)
444
- );
445
- }
548
+ return joinHasThrough(
549
+ fromQuery,
550
+ fromQuery,
551
+ toQuery,
552
+ throughRelation,
553
+ sourceRelation
446
554
  );
447
555
  }
448
556
  };
449
557
  }
450
- const { primaryKey, foreignKey } = relation.options;
451
- const state = { query, primaryKey, foreignKey };
452
- const fromQuerySelect = [{ selectAs: { [foreignKey]: primaryKey } }];
558
+ const primaryKeys = "columns" in relation.options ? relation.options.columns : [relation.options.primaryKey];
559
+ const foreignKeys = "columns" in relation.options ? relation.options.references : [relation.options.foreignKey];
560
+ const state = { query, primaryKeys, foreignKeys };
561
+ const len = primaryKeys.length;
562
+ const reversedOn = {};
563
+ for (let i = 0; i < len; i++) {
564
+ reversedOn[foreignKeys[i]] = primaryKeys[i];
565
+ }
566
+ const fromQuerySelect = [{ selectAs: reversedOn }];
453
567
  return {
454
568
  returns: "one",
455
569
  method: (params) => {
456
- const values = { [foreignKey]: params[primaryKey] };
570
+ const values = {};
571
+ for (let i = 0; i < len; i++) {
572
+ values[foreignKeys[i]] = params[primaryKeys[i]];
573
+ }
457
574
  return query.where(values)._defaults(values);
458
575
  },
459
576
  virtualColumn: new HasOneVirtualColumn(relationName, state),
460
577
  joinQuery(fromQuery, toQuery) {
461
- return pqb.addQueryOn(toQuery, fromQuery, toQuery, foreignKey, primaryKey);
578
+ return joinHasRelation(fromQuery, toQuery, primaryKeys, foreignKeys, len);
462
579
  },
463
580
  reverseJoin(fromQuery, toQuery) {
464
- return pqb.addQueryOn(fromQuery, toQuery, fromQuery, primaryKey, foreignKey);
581
+ return joinHasRelation(toQuery, fromQuery, foreignKeys, primaryKeys, len);
465
582
  },
466
583
  modifyRelatedQuery(relationQuery) {
467
584
  return (query2) => {
@@ -474,57 +591,88 @@ const makeHasOneMethod = (table, relation, relationName, query) => {
474
591
  }
475
592
  };
476
593
  };
477
- const nestedInsert$2 = ({ query, primaryKey, foreignKey }) => {
594
+ const nestedInsert$2 = ({ query, primaryKeys, foreignKeys }) => {
478
595
  return async (_, data) => {
479
- const connect = data.filter(
480
- (item) => Boolean(item[1].connect || item[1].connectOrCreate)
481
- );
482
596
  const t = query.clone();
597
+ const items = [];
598
+ for (const item of data) {
599
+ if (item[1].connect || item[1].connectOrCreate) {
600
+ items.push(item);
601
+ }
602
+ }
483
603
  let connected;
484
- if (connect.length) {
485
- connected = await Promise.all(
486
- connect.map(([selfData, item]) => {
487
- const data2 = { [foreignKey]: selfData[primaryKey] };
488
- return "connect" in item ? t.where(item.connect)._updateOrThrow(data2) : t.where(item.connectOrCreate.where)._update(data2);
489
- })
490
- );
604
+ if (items.length) {
605
+ for (let i = 0, len = items.length; i < len; i++) {
606
+ const [selfData, item] = items[i];
607
+ const data2 = {};
608
+ for (let i2 = 0; i2 < len; i2++) {
609
+ data2[foreignKeys[i2]] = selfData[primaryKeys[i2]];
610
+ }
611
+ items[i] = "connect" in item ? t.where(item.connect)._updateOrThrow(data2) : t.where(
612
+ item.connectOrCreate.where
613
+ )._update(data2);
614
+ }
615
+ connected = await Promise.all(items);
491
616
  } else {
492
617
  connected = [];
493
618
  }
494
619
  let connectedI = 0;
495
- const create = data.filter(
496
- (item) => {
497
- if (item[1].connectOrCreate) {
498
- return !connected[connectedI++];
620
+ items.length = 0;
621
+ for (const item of data) {
622
+ if (item[1].connectOrCreate) {
623
+ if (!connected[connectedI++]) {
624
+ items.push(item);
499
625
  }
500
- return Boolean(item[1].create);
626
+ } else if (item[1].create) {
627
+ items.push(item);
501
628
  }
502
- );
503
- if (create.length) {
504
- await t._count()._createMany(
505
- create.map(([selfData, item]) => __spreadValues$5({
506
- [foreignKey]: selfData[primaryKey]
507
- }, "create" in item ? item.create : item.connectOrCreate.create))
508
- );
629
+ }
630
+ if (items.length) {
631
+ for (let i = 0, len = items.length; i < len; i++) {
632
+ const [selfData, item] = items[i];
633
+ const data2 = __spreadValues$5({}, "create" in item ? item.create : item.connectOrCreate.create);
634
+ for (let i2 = 0; i2 < primaryKeys.length; i2++) {
635
+ data2[foreignKeys[i2]] = selfData[primaryKeys[i2]];
636
+ }
637
+ items[i] = data2;
638
+ }
639
+ await t._count()._createMany(items);
509
640
  }
510
641
  };
511
642
  };
512
- const nestedUpdate$2 = ({ query, primaryKey, foreignKey }) => {
643
+ const nestedUpdate$2 = ({ query, primaryKeys, foreignKeys }) => {
644
+ const len = primaryKeys.length;
645
+ const setNulls = {};
646
+ for (const foreignKey of foreignKeys) {
647
+ setNulls[foreignKey] = null;
648
+ }
513
649
  return async (_, data, params) => {
514
650
  const t = query.clone();
515
- const ids = data.map((item) => item[primaryKey]);
516
- const currentRelationsQuery = t.where({
517
- [foreignKey]: { in: ids }
518
- });
651
+ const ids = data.map(
652
+ (item) => primaryKeys.map((primaryKey) => item[primaryKey])
653
+ );
654
+ const currentRelationsQuery = t.whereIn(
655
+ foreignKeys,
656
+ ids
657
+ );
519
658
  if (params.create || params.disconnect || params.set) {
520
- await currentRelationsQuery._update({ [foreignKey]: null });
659
+ await currentRelationsQuery._update(
660
+ setNulls
661
+ );
662
+ const record = data[0];
521
663
  if (params.create) {
522
- await t._count()._create(__spreadProps$3(__spreadValues$5({}, params.create), {
523
- [foreignKey]: data[0][primaryKey]
524
- }));
664
+ const obj = __spreadValues$5({}, params.create);
665
+ for (let i = 0; i < len; i++) {
666
+ obj[foreignKeys[i]] = record[primaryKeys[i]];
667
+ }
668
+ await t._count()._create(obj);
525
669
  }
526
670
  if (params.set) {
527
- await t._where(params.set)._update({ [foreignKey]: data[0][primaryKey] });
671
+ const obj = {};
672
+ for (let i = 0; i < len; i++) {
673
+ obj[foreignKeys[i]] = record[primaryKeys[i]];
674
+ }
675
+ await t._where(params.set)._update(obj);
528
676
  }
529
677
  } else if (params.update) {
530
678
  await currentRelationsQuery._update(params.update);
@@ -532,13 +680,23 @@ const nestedUpdate$2 = ({ query, primaryKey, foreignKey }) => {
532
680
  await currentRelationsQuery._delete();
533
681
  } else if (params.upsert) {
534
682
  const { update, create } = params.upsert;
535
- const updatedIds = await currentRelationsQuery._pluck(foreignKey)._update(update);
683
+ currentRelationsQuery.q.select = foreignKeys;
684
+ const updatedIds = await currentRelationsQuery._rows()._update(update);
536
685
  if (updatedIds.length < ids.length) {
537
686
  const data2 = typeof create === "function" ? create() : create;
538
687
  await t.createMany(
539
- ids.filter((id) => !updatedIds.includes(id)).map((id) => __spreadProps$3(__spreadValues$5({}, data2), {
540
- [foreignKey]: id
541
- }))
688
+ ids.reduce((rows, ids2) => {
689
+ if (!updatedIds.some(
690
+ (updated) => updated.every((value, i) => value === ids2[i])
691
+ )) {
692
+ const obj = __spreadValues$5({}, data2);
693
+ for (let i = 0; i < len; i++) {
694
+ obj[foreignKeys[i]] = ids2[i];
695
+ }
696
+ rows.push(obj);
697
+ }
698
+ return rows;
699
+ }, [])
542
700
  );
543
701
  }
544
702
  }
@@ -546,8 +704,6 @@ const nestedUpdate$2 = ({ query, primaryKey, foreignKey }) => {
546
704
  };
547
705
 
548
706
  var __defProp$4 = Object.defineProperty;
549
- var __defProps$2 = Object.defineProperties;
550
- var __getOwnPropDescs$2 = Object.getOwnPropertyDescriptors;
551
707
  var __getOwnPropSymbols$4 = Object.getOwnPropertySymbols;
552
708
  var __hasOwnProp$4 = Object.prototype.hasOwnProperty;
553
709
  var __propIsEnum$4 = Object.prototype.propertyIsEnumerable;
@@ -563,7 +719,6 @@ var __spreadValues$4 = (a, b) => {
563
719
  }
564
720
  return a;
565
721
  };
566
- var __spreadProps$2 = (a, b) => __defProps$2(a, __getOwnPropDescs$2(b));
567
722
  class HasManyVirtualColumn extends pqb.VirtualColumn {
568
723
  constructor(key, state) {
569
724
  super();
@@ -579,7 +734,7 @@ class HasManyVirtualColumn extends pqb.VirtualColumn {
579
734
  item,
580
735
  rowIndex,
581
736
  this.key,
582
- this.state.primaryKey,
737
+ this.state.primaryKeys,
583
738
  this.nestedInsert
584
739
  );
585
740
  }
@@ -593,7 +748,7 @@ class HasManyVirtualColumn extends pqb.VirtualColumn {
593
748
  q,
594
749
  set,
595
750
  this.key,
596
- this.state.primaryKey,
751
+ this.state.primaryKeys,
597
752
  this.nestedUpdate
598
753
  );
599
754
  }
@@ -619,46 +774,49 @@ const makeHasManyMethod = (table, relation, relationName, query) => {
619
774
  );
620
775
  },
621
776
  joinQuery(fromQuery, toQuery) {
622
- return toQuery.whereExists(
623
- throughRelation.joinQuery(fromQuery, throughRelation.query),
624
- () => {
625
- const as = pqb.getQueryAs(toQuery);
626
- return sourceRelation.joinQuery(
627
- throughRelation.query,
628
- sourceRelation.query.as(as)
629
- );
630
- }
777
+ return joinHasThrough(
778
+ toQuery,
779
+ fromQuery,
780
+ toQuery,
781
+ throughRelation,
782
+ sourceRelation
631
783
  );
632
784
  },
633
785
  reverseJoin(fromQuery, toQuery) {
634
- return fromQuery.whereExists(
635
- throughRelation.joinQuery(fromQuery, throughRelation.query),
636
- () => {
637
- const as = pqb.getQueryAs(toQuery);
638
- return sourceRelation.joinQuery(
639
- throughRelation.query,
640
- sourceRelation.query.as(as)
641
- );
642
- }
786
+ return joinHasThrough(
787
+ fromQuery,
788
+ fromQuery,
789
+ toQuery,
790
+ throughRelation,
791
+ sourceRelation
643
792
  );
644
793
  }
645
794
  };
646
795
  }
647
- const { primaryKey, foreignKey } = relation.options;
648
- const state = { query, primaryKey, foreignKey };
649
- const fromQuerySelect = [{ selectAs: { [foreignKey]: primaryKey } }];
796
+ const primaryKeys = "columns" in relation.options ? relation.options.columns : [relation.options.primaryKey];
797
+ const foreignKeys = "columns" in relation.options ? relation.options.references : [relation.options.foreignKey];
798
+ const state = { query, primaryKeys, foreignKeys };
799
+ const len = primaryKeys.length;
800
+ const reversedOn = {};
801
+ for (let i = 0; i < len; i++) {
802
+ reversedOn[foreignKeys[i]] = primaryKeys[i];
803
+ }
804
+ const fromQuerySelect = [{ selectAs: reversedOn }];
650
805
  return {
651
806
  returns: "many",
652
807
  method: (params) => {
653
- const values = { [foreignKey]: params[primaryKey] };
808
+ const values = {};
809
+ for (let i = 0; i < len; i++) {
810
+ values[foreignKeys[i]] = params[primaryKeys[i]];
811
+ }
654
812
  return query.where(values)._defaults(values);
655
813
  },
656
814
  virtualColumn: new HasManyVirtualColumn(relationName, state),
657
815
  joinQuery(fromQuery, toQuery) {
658
- return pqb.addQueryOn(toQuery, fromQuery, toQuery, foreignKey, primaryKey);
816
+ return joinHasRelation(fromQuery, toQuery, primaryKeys, foreignKeys, len);
659
817
  },
660
818
  reverseJoin(fromQuery, toQuery) {
661
- return pqb.addQueryOn(fromQuery, toQuery, fromQuery, primaryKey, foreignKey);
819
+ return joinHasRelation(toQuery, fromQuery, foreignKeys, primaryKeys, len);
662
820
  },
663
821
  modifyRelatedQuery(relationQuery) {
664
822
  return (query2) => {
@@ -671,121 +829,150 @@ const makeHasManyMethod = (table, relation, relationName, query) => {
671
829
  }
672
830
  };
673
831
  };
674
- const getWhereForNestedUpdate = (data, params, primaryKey, foreignKey) => {
675
- const where = {
676
- [foreignKey]: { in: data.map((item) => item[primaryKey]) }
677
- };
678
- if (params) {
679
- if (Array.isArray(params)) {
680
- where.OR = params;
681
- } else {
682
- Object.assign(where, params);
683
- }
684
- }
685
- return where;
832
+ const getWhereForNestedUpdate = (t, data, params, primaryKeys, foreignKeys) => {
833
+ return t.where({
834
+ IN: {
835
+ columns: foreignKeys,
836
+ values: data.map((item) => primaryKeys.map((key) => item[key]))
837
+ },
838
+ OR: params ? orchidCore.toArray(params) : void 0
839
+ });
686
840
  };
687
- const nestedInsert$1 = ({ query, primaryKey, foreignKey }) => {
841
+ const nestedInsert$1 = ({ query, primaryKeys, foreignKeys }) => {
842
+ const len = primaryKeys.length;
688
843
  return async (_, data) => {
689
- const connect = data.filter(
690
- (item) => Boolean(item[1].connect)
691
- );
692
844
  const t = query.clone();
693
- if (connect.length) {
694
- await Promise.all(
695
- connect.flatMap(
696
- ([selfData, { connect: connect2 }]) => t.or(...connect2)._updateOrThrow({
697
- [foreignKey]: selfData[primaryKey]
698
- })
699
- )
700
- );
845
+ const items = [];
846
+ for (const item of data) {
847
+ if (item[1].connect) {
848
+ items.push(item);
849
+ }
850
+ }
851
+ if (items.length) {
852
+ for (let i = 0, len2 = items.length; i < len2; i++) {
853
+ const [selfData, { connect }] = items[i];
854
+ const obj = {};
855
+ for (let i2 = 0; i2 < len2; i2++) {
856
+ obj[foreignKeys[i2]] = selfData[primaryKeys[i2]];
857
+ }
858
+ items[i] = t.or(...connect)._updateOrThrow(obj);
859
+ }
860
+ await Promise.all(items);
861
+ }
862
+ items.length = 0;
863
+ for (const item of data) {
864
+ if (item[1].connectOrCreate) {
865
+ items.push(item);
866
+ }
701
867
  }
702
- const connectOrCreate = data.filter(
703
- (item) => Boolean(item[1].connectOrCreate)
704
- );
705
868
  let connected;
706
- if (connectOrCreate.length) {
707
- connected = await Promise.all(
708
- connectOrCreate.flatMap(
709
- ([selfData, { connectOrCreate: connectOrCreate2 }]) => connectOrCreate2.map(
710
- (item) => t.where(item.where)._update({
711
- [foreignKey]: selfData[primaryKey]
712
- })
713
- )
714
- )
715
- );
869
+ if (items.length) {
870
+ const queries = [];
871
+ for (let i = 0, len2 = items.length; i < len2; i++) {
872
+ const [selfData, { connectOrCreate }] = items[i];
873
+ for (const item of connectOrCreate) {
874
+ const obj = {};
875
+ for (let i2 = 0; i2 < len2; i2++) {
876
+ obj[foreignKeys[i2]] = selfData[primaryKeys[i2]];
877
+ }
878
+ queries.push(
879
+ t.where(item.where)._update(obj)
880
+ );
881
+ }
882
+ }
883
+ connected = await Promise.all(queries);
716
884
  } else {
717
885
  connected = [];
718
886
  }
719
887
  let connectedI = 0;
720
- const create = data.filter(
721
- (item) => {
722
- if (item[1].connectOrCreate) {
723
- const length = item[1].connectOrCreate.length;
724
- connectedI += length;
725
- for (let i = length; i > 0; i--) {
726
- if (connected[connectedI - i] === 0)
727
- return true;
888
+ items.length = 0;
889
+ for (const item of data) {
890
+ if (item[1].connectOrCreate) {
891
+ const length = item[1].connectOrCreate.length;
892
+ connectedI += length;
893
+ for (let i = length; i > 0; i--) {
894
+ if (connected[connectedI - i] === 0) {
895
+ items.push(item);
896
+ break;
728
897
  }
729
898
  }
730
- return Boolean(item[1].create);
899
+ } else if (item[1].create) {
900
+ items.push(item);
731
901
  }
732
- );
902
+ }
733
903
  connectedI = 0;
734
- if (create.length) {
735
- await t._createMany(
736
- create.flatMap(([selfData, { create: create2 = [], connectOrCreate: connectOrCreate2 = [] }]) => {
737
- return [
738
- ...create2.map((item) => __spreadValues$4({
739
- [foreignKey]: selfData[primaryKey]
740
- }, item)),
741
- ...connectOrCreate2.filter(() => connected[connectedI++] === 0).map((item) => __spreadValues$4({
742
- [foreignKey]: selfData[primaryKey]
743
- }, item.create))
744
- ];
745
- })
746
- );
904
+ if (items.length) {
905
+ const records = [];
906
+ for (const [selfData, { create, connectOrCreate }] of items) {
907
+ const obj = {};
908
+ for (let i = 0; i < len; i++) {
909
+ obj[foreignKeys[i]] = selfData[primaryKeys[i]];
910
+ }
911
+ if (create) {
912
+ for (const item of create) {
913
+ records.push(__spreadValues$4(__spreadValues$4({}, item), obj));
914
+ }
915
+ }
916
+ if (connectOrCreate) {
917
+ for (const item of connectOrCreate) {
918
+ if (connected[connectedI++] === 0) {
919
+ records.push(__spreadValues$4(__spreadValues$4({}, item.create), obj));
920
+ }
921
+ }
922
+ }
923
+ }
924
+ await t._createMany(records);
747
925
  }
748
926
  };
749
927
  };
750
- const nestedUpdate$1 = ({ query, primaryKey, foreignKey }) => {
928
+ const nestedUpdate$1 = ({ query, primaryKeys, foreignKeys }) => {
929
+ const len = primaryKeys.length;
751
930
  return async (_, data, params) => {
752
931
  var _a;
753
932
  const t = query.clone();
754
933
  if (params.create) {
934
+ const obj = {};
935
+ for (let i = 0; i < len; i++) {
936
+ obj[foreignKeys[i]] = data[0][primaryKeys[i]];
937
+ }
755
938
  await t._count()._createMany(
756
- params.create.map((create) => __spreadProps$2(__spreadValues$4({}, create), {
757
- [foreignKey]: data[0][primaryKey]
758
- }))
939
+ params.create.map((create) => __spreadValues$4(__spreadValues$4({}, create), obj))
759
940
  );
760
941
  delete t.q[pqb.toSQLCacheKey];
761
942
  }
762
943
  if (params.disconnect || params.set) {
763
- await t.where(
764
- getWhereForNestedUpdate(
765
- data,
766
- params.disconnect,
767
- primaryKey,
768
- foreignKey
769
- )
770
- )._update({ [foreignKey]: null });
944
+ const obj = {};
945
+ for (const foreignKey of foreignKeys) {
946
+ obj[foreignKey] = null;
947
+ }
948
+ await getWhereForNestedUpdate(
949
+ t,
950
+ data,
951
+ params.disconnect,
952
+ primaryKeys,
953
+ foreignKeys
954
+ )._update(obj);
771
955
  if (params.set) {
772
956
  delete t.q[pqb.toSQLCacheKey];
957
+ const obj2 = {};
958
+ for (let i = 0; i < len; i++) {
959
+ obj2[foreignKeys[i]] = data[0][primaryKeys[i]];
960
+ }
773
961
  await t.where(
774
962
  Array.isArray(params.set) ? {
775
963
  OR: params.set
776
964
  } : params.set
777
- )._update({ [foreignKey]: data[0][primaryKey] });
965
+ )._update(obj2);
778
966
  }
779
967
  }
780
968
  if (params.delete || params.update) {
781
969
  delete t.q[pqb.toSQLCacheKey];
782
- const q = t._where(
783
- getWhereForNestedUpdate(
784
- data,
785
- params.delete || ((_a = params.update) == null ? void 0 : _a.where),
786
- primaryKey,
787
- foreignKey
788
- )
970
+ const q = getWhereForNestedUpdate(
971
+ t,
972
+ data,
973
+ params.delete || ((_a = params.update) == null ? void 0 : _a.where),
974
+ primaryKeys,
975
+ foreignKeys
789
976
  );
790
977
  if (params.delete) {
791
978
  await q._delete();
@@ -830,7 +1017,7 @@ class HasAndBelongsToManyVirtualColumn extends pqb.VirtualColumn {
830
1017
  item,
831
1018
  rowIndex,
832
1019
  this.key,
833
- this.state.primaryKey,
1020
+ this.state.primaryKeys,
834
1021
  this.nestedInsert
835
1022
  );
836
1023
  }
@@ -839,7 +1026,7 @@ class HasAndBelongsToManyVirtualColumn extends pqb.VirtualColumn {
839
1026
  q,
840
1027
  set,
841
1028
  this.key,
842
- this.state.primaryKey,
1029
+ this.state.primaryKeys,
843
1030
  this.nestedUpdate
844
1031
  );
845
1032
  }
@@ -852,23 +1039,48 @@ const removeColumnName = (column) => {
852
1039
  return cloned;
853
1040
  };
854
1041
  const makeHasAndBelongsToManyMethod = (table, qb, relation, relationName, query) => {
855
- const {
856
- primaryKey: pk,
857
- foreignKey: fk,
858
- associationPrimaryKey: apk,
859
- associationForeignKey: afk,
860
- joinTable
861
- } = relation.options;
862
- const foreignKeyFull = `${joinTable}.${fk}`;
863
- const associationForeignKeyFull = `${joinTable}.${afk}`;
864
- const associationPrimaryKeyFull = `${pqb.getQueryAs(query)}.${apk}`;
1042
+ let primaryKeys;
1043
+ let foreignKeys;
1044
+ let joinTable;
1045
+ let throughForeignKeys;
1046
+ let throughPrimaryKeys;
1047
+ const { options } = relation;
1048
+ if ("columns" in options) {
1049
+ primaryKeys = options.columns;
1050
+ foreignKeys = options.references;
1051
+ joinTable = options.through.table;
1052
+ throughForeignKeys = options.through.columns;
1053
+ throughPrimaryKeys = options.through.references;
1054
+ } else {
1055
+ primaryKeys = [options.primaryKey];
1056
+ foreignKeys = [options.foreignKey];
1057
+ joinTable = options.joinTable;
1058
+ throughForeignKeys = [options.associationForeignKey];
1059
+ throughPrimaryKeys = [options.associationPrimaryKey];
1060
+ }
1061
+ const foreignKeysFull = foreignKeys.map((key) => `${joinTable}.${key}`);
1062
+ const throughForeignKeysFull = throughForeignKeys.map(
1063
+ (key) => `${joinTable}.${key}`
1064
+ );
1065
+ const foreignTable = pqb.getQueryAs(query);
1066
+ const throughPrimaryKeysFull = throughPrimaryKeys.map(
1067
+ (key) => `${foreignTable}.${key}`
1068
+ );
1069
+ const len = primaryKeys.length;
1070
+ const throughLen = throughPrimaryKeys.length;
865
1071
  const baseQuery = Object.create(qb.baseQuery);
866
1072
  baseQuery.baseQuery = baseQuery;
867
1073
  baseQuery.table = joinTable;
868
- baseQuery.shape = {
869
- [fk]: removeColumnName(table.shape[pk]),
870
- [afk]: removeColumnName(query.shape[apk])
871
- };
1074
+ const shape = {};
1075
+ for (let i = 0; i < len; i++) {
1076
+ shape[foreignKeys[i]] = removeColumnName(table.shape[primaryKeys[i]]);
1077
+ }
1078
+ for (let i = 0; i < throughLen; i++) {
1079
+ shape[throughForeignKeys[i]] = removeColumnName(
1080
+ query.shape[throughPrimaryKeys[i]]
1081
+ );
1082
+ }
1083
+ baseQuery.shape = shape;
872
1084
  baseQuery.q = __spreadProps$1(__spreadValues$3({}, baseQuery.q), {
873
1085
  shape: baseQuery.shape
874
1086
  });
@@ -876,31 +1088,56 @@ const makeHasAndBelongsToManyMethod = (table, qb, relation, relationName, query)
876
1088
  const state = {
877
1089
  relatedTableQuery: query,
878
1090
  joinTableQuery: subQuery,
879
- primaryKey: pk,
880
- foreignKey: fk,
881
- associationPrimaryKey: apk,
882
- associationForeignKey: afk,
883
- foreignKeyFull,
884
- associationForeignKeyFull,
885
- associationPrimaryKeyFull
1091
+ primaryKeys,
1092
+ foreignKeys,
1093
+ throughForeignKeys,
1094
+ throughPrimaryKeys,
1095
+ foreignKeysFull,
1096
+ throughForeignKeysFull,
1097
+ throughPrimaryKeysFull
886
1098
  };
1099
+ const joinQuery = (toQuery, tableAs, foreignAs) => {
1100
+ return toQuery.whereExists(subQuery, (q) => {
1101
+ for (let i = 0; i < throughLen; i++) {
1102
+ q._on(
1103
+ throughForeignKeysFull[i],
1104
+ `${foreignAs}.${throughPrimaryKeys[i]}`
1105
+ );
1106
+ }
1107
+ for (let i = 0; i < len; i++) {
1108
+ q._on(foreignKeysFull[i], `${tableAs}.${primaryKeys[i]}`);
1109
+ }
1110
+ return q;
1111
+ });
1112
+ };
1113
+ const obj = {};
1114
+ for (let i = 0; i < len; i++) {
1115
+ obj[foreignKeys[i]] = primaryKeys[i];
1116
+ }
1117
+ const selectPrimaryKeysAsForeignKeys = [{ selectAs: obj }];
887
1118
  return {
888
1119
  returns: "many",
889
1120
  method(params) {
890
- return query.whereExists(
891
- subQuery,
892
- (q) => q.on(associationForeignKeyFull, associationPrimaryKeyFull).where({
893
- [foreignKeyFull]: params[pk]
894
- })
895
- );
1121
+ return query.whereExists(subQuery, (q) => {
1122
+ q = q.clone();
1123
+ const where = {};
1124
+ for (let i = 0; i < len; i++) {
1125
+ where[foreignKeysFull[i]] = params[primaryKeys[i]];
1126
+ }
1127
+ for (let i = 0; i < throughLen; i++) {
1128
+ q._on(throughForeignKeysFull[i], throughPrimaryKeysFull[i]);
1129
+ }
1130
+ return q._where(where);
1131
+ });
896
1132
  },
897
1133
  virtualColumn: new HasAndBelongsToManyVirtualColumn(relationName, state),
898
1134
  // joinQuery can be a property of RelationQuery and be used by whereExists and other stuff which needs it
899
1135
  // and the chained query itself may be a query around this joinQuery
900
1136
  joinQuery(fromQuery, toQuery) {
901
- const join = toQuery.whereExists(
902
- subQuery,
903
- (q) => q._on(associationForeignKeyFull, `${pqb.getQueryAs(toQuery)}.${apk}`)._on(foreignKeyFull, `${pqb.getQueryAs(fromQuery)}.${pk}`)
1137
+ const join = joinQuery(
1138
+ toQuery,
1139
+ pqb.getQueryAs(fromQuery),
1140
+ pqb.getQueryAs(toQuery)
904
1141
  );
905
1142
  join.q.joinedShapes = __spreadProps$1(__spreadValues$3({}, join.q.joinedShapes), {
906
1143
  [fromQuery.q.as || fromQuery.table]: fromQuery.q.shape
@@ -908,10 +1145,7 @@ const makeHasAndBelongsToManyMethod = (table, qb, relation, relationName, query)
908
1145
  return join;
909
1146
  },
910
1147
  reverseJoin(fromQuery, toQuery) {
911
- return fromQuery.whereExists(
912
- subQuery,
913
- (q) => q._on(associationForeignKeyFull, `${pqb.getQueryAs(toQuery)}.${apk}`)._on(foreignKeyFull, `${pqb.getQueryAs(fromQuery)}.${pk}`)
914
- );
1148
+ return joinQuery(fromQuery, pqb.getQueryAs(fromQuery), pqb.getQueryAs(toQuery));
915
1149
  },
916
1150
  modifyRelatedQuery(relationQuery) {
917
1151
  const ref = {};
@@ -923,14 +1157,12 @@ const makeHasAndBelongsToManyMethod = (table, qb, relation, relationName, query)
923
1157
  );
924
1158
  }
925
1159
  const fromQuery = ref.q.clone();
926
- fromQuery.q.select = [{ selectAs: { [fk]: pk } }];
927
- const createdCount = await subQuery.count()._createFrom(
928
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
929
- fromQuery,
930
- {
931
- [afk]: result[0][apk]
932
- }
933
- );
1160
+ fromQuery.q.select = selectPrimaryKeysAsForeignKeys;
1161
+ const data = {};
1162
+ for (let i = 0; i < throughLen; i++) {
1163
+ data[throughForeignKeys[i]] = result[0][throughPrimaryKeys[i]];
1164
+ }
1165
+ const createdCount = await subQuery.count()._createFrom(fromQuery, data);
934
1166
  if (createdCount === 0) {
935
1167
  throw new pqb.NotFoundError(fromQuery);
936
1168
  }
@@ -942,97 +1174,127 @@ const makeHasAndBelongsToManyMethod = (table, qb, relation, relationName, query)
942
1174
  };
943
1175
  };
944
1176
  const queryJoinTable = (state, data, conditions) => {
945
- const t = state.joinTableQuery.clone();
946
- const where = {
947
- [state.foreignKey]: { in: data.map((item) => item[state.primaryKey]) }
948
- };
1177
+ const t = state.joinTableQuery.where({
1178
+ IN: {
1179
+ columns: state.foreignKeys,
1180
+ values: data.map((item) => state.primaryKeys.map((key) => item[key]))
1181
+ }
1182
+ });
949
1183
  if (conditions) {
950
- where[state.associationForeignKey] = {
951
- in: state.relatedTableQuery.where(
952
- Array.isArray(conditions) ? { OR: conditions } : conditions
953
- )._select(state.associationPrimaryKey)
954
- };
1184
+ t._where({
1185
+ IN: {
1186
+ columns: state.throughForeignKeys,
1187
+ values: state.relatedTableQuery.where(conditionsToWhereArg(conditions))._select(...state.throughPrimaryKeys)
1188
+ }
1189
+ });
955
1190
  }
956
- return t._where(where);
957
- };
958
- const queryRelatedTable = (query, conditions) => {
959
- return query.where(
960
- Array.isArray(conditions) ? { OR: conditions } : conditions
961
- );
1191
+ return t;
962
1192
  };
963
- const insertToJoinTable = (state, joinTableTransaction, data, ids) => {
964
- return joinTableTransaction._count()._createMany(
965
- data.flatMap(
966
- (item) => ids.map((id) => ({
967
- [state.foreignKey]: item[state.primaryKey],
968
- [state.associationForeignKey]: id
969
- }))
970
- )
971
- );
1193
+ const conditionsToWhereArg = (conditions) => Array.isArray(conditions) ? { OR: conditions } : conditions;
1194
+ const insertToJoinTable = (state, joinTableTransaction, data, idsRows) => {
1195
+ const len = state.primaryKeys.length;
1196
+ const throughLen = state.throughPrimaryKeys.length;
1197
+ const records = [];
1198
+ for (const item of data) {
1199
+ const obj = {};
1200
+ for (let i = 0; i < len; i++) {
1201
+ obj[state.foreignKeys[i]] = item[state.primaryKeys[i]];
1202
+ }
1203
+ for (const ids of idsRows) {
1204
+ const record = __spreadValues$3({}, obj);
1205
+ for (let i = 0; i < throughLen; i++) {
1206
+ record[state.throughForeignKeys[i]] = ids[i];
1207
+ }
1208
+ records.push(record);
1209
+ }
1210
+ }
1211
+ return joinTableTransaction._count()._createMany(records);
972
1212
  };
973
1213
  const nestedInsert = ({
974
1214
  relatedTableQuery,
975
1215
  joinTableQuery,
976
- primaryKey,
977
- foreignKey,
978
- associationPrimaryKey,
979
- associationForeignKey
1216
+ primaryKeys,
1217
+ foreignKeys,
1218
+ throughPrimaryKeys,
1219
+ throughForeignKeys
980
1220
  }) => {
1221
+ const len = primaryKeys.length;
1222
+ const throughLen = primaryKeys.length;
981
1223
  return async (_, data) => {
982
- const connect = data.filter(
983
- (item) => Boolean(item[1].connect)
984
- );
985
1224
  const t = relatedTableQuery.clone();
1225
+ const items = [];
1226
+ for (const item of data) {
1227
+ if (item[1].connect) {
1228
+ items.push(item);
1229
+ }
1230
+ }
986
1231
  let connected;
987
- if (connect.length) {
988
- connected = await Promise.all(
989
- connect.flatMap(
990
- ([, { connect: connect2 }]) => connect2.map(
991
- (item) => t.select(associationPrimaryKey)._findBy(item)._take()
992
- )
993
- )
994
- );
1232
+ if (items.length) {
1233
+ const queries = [];
1234
+ for (const [, { connect }] of items) {
1235
+ for (const item of connect) {
1236
+ queries.push(
1237
+ t.select(...throughPrimaryKeys)._findBy(item)._take()
1238
+ );
1239
+ }
1240
+ }
1241
+ connected = await Promise.all(queries);
995
1242
  } else {
996
1243
  connected = [];
997
1244
  }
998
- const connectOrCreate = data.filter(
999
- (item) => Boolean(item[1].connectOrCreate)
1000
- );
1245
+ items.length = 0;
1246
+ for (const item of data) {
1247
+ if (item[1].connectOrCreate) {
1248
+ items.push(item);
1249
+ }
1250
+ }
1001
1251
  let connectOrCreated;
1002
- if (connectOrCreate.length) {
1003
- connectOrCreated = await Promise.all(
1004
- connectOrCreate.flatMap(
1005
- ([, { connectOrCreate: connectOrCreate2 }]) => connectOrCreate2.map(
1006
- (item) => t.select(associationPrimaryKey)._findBy(item.where)._takeOptional()
1007
- )
1008
- )
1009
- );
1252
+ if (items.length) {
1253
+ const queries = [];
1254
+ for (const [, { connectOrCreate }] of items) {
1255
+ for (const item of connectOrCreate) {
1256
+ queries.push(
1257
+ t.select(...throughPrimaryKeys)._findBy(item.where)._takeOptional()
1258
+ );
1259
+ }
1260
+ }
1261
+ connectOrCreated = await Promise.all(queries);
1010
1262
  } else {
1011
1263
  connectOrCreated = [];
1012
1264
  }
1013
1265
  let connectOrCreateI = 0;
1014
- const create = data.filter(
1015
- (item) => {
1016
- if (item[1].connectOrCreate) {
1017
- const length = item[1].connectOrCreate.length;
1018
- connectOrCreateI += length;
1019
- for (let i = length; i > 0; i--) {
1020
- if (!connectOrCreated[connectOrCreateI - i])
1021
- return true;
1266
+ items.length = 0;
1267
+ for (const item of data) {
1268
+ if (item[1].connectOrCreate) {
1269
+ const length = item[1].connectOrCreate.length;
1270
+ connectOrCreateI += length;
1271
+ for (let i = length; i > 0; i--) {
1272
+ if (!connectOrCreated[connectOrCreateI - i]) {
1273
+ items.push(item);
1274
+ break;
1022
1275
  }
1023
1276
  }
1024
- return Boolean(item[1].create);
1277
+ } else if (item[1].create) {
1278
+ items.push(item);
1025
1279
  }
1026
- );
1280
+ }
1027
1281
  connectOrCreateI = 0;
1028
1282
  let created;
1029
- if (create.length) {
1030
- created = await t.select(associationPrimaryKey)._createMany(
1031
- create.flatMap(([, { create: create2 = [], connectOrCreate: connectOrCreate2 = [] }]) => [
1032
- ...create2,
1033
- ...connectOrCreate2.filter(() => !connectOrCreated[connectOrCreateI++]).map((item) => item.create)
1034
- ])
1035
- );
1283
+ if (items.length) {
1284
+ const records2 = [];
1285
+ for (const [, { create, connectOrCreate }] of items) {
1286
+ if (create) {
1287
+ records2.push(...create);
1288
+ }
1289
+ if (connectOrCreate) {
1290
+ for (const item of connectOrCreate) {
1291
+ if (!connectOrCreated[connectOrCreateI++]) {
1292
+ records2.push(item.create);
1293
+ }
1294
+ }
1295
+ }
1296
+ }
1297
+ created = await t.select(...throughPrimaryKeys)._createMany(records2);
1036
1298
  } else {
1037
1299
  created = [];
1038
1300
  }
@@ -1040,95 +1302,110 @@ const nestedInsert = ({
1040
1302
  let createI = 0;
1041
1303
  let connectI = 0;
1042
1304
  connectOrCreateI = 0;
1043
- data.forEach(([, data2], index) => {
1044
- if (data2.create || data2.connectOrCreate) {
1045
- if (data2.create) {
1046
- const len = data2.create.length;
1047
- allKeys[index][1] = created.slice(createI, createI + len);
1048
- createI += len;
1305
+ for (let index = 0, len2 = data.length; index < len2; index++) {
1306
+ const item = data[index][1];
1307
+ if (item.create || item.connectOrCreate) {
1308
+ if (item.create) {
1309
+ const len3 = item.create.length;
1310
+ allKeys[index][1] = created.slice(createI, createI + len3);
1311
+ createI += len3;
1049
1312
  }
1050
- if (data2.connectOrCreate) {
1313
+ if (item.connectOrCreate) {
1051
1314
  const arr = [];
1052
1315
  allKeys[index][1] = arr;
1053
- const len = data2.connectOrCreate.length;
1054
- for (let i = 0; i < len; i++) {
1055
- const item = connectOrCreated[connectOrCreateI++];
1056
- if (item) {
1057
- arr.push(item);
1316
+ const len3 = item.connectOrCreate.length;
1317
+ for (let i = 0; i < len3; i++) {
1318
+ const item2 = connectOrCreated[connectOrCreateI++];
1319
+ if (item2) {
1320
+ arr.push(item2);
1058
1321
  } else {
1059
1322
  arr.push(created[createI++]);
1060
1323
  }
1061
1324
  }
1062
1325
  }
1063
1326
  }
1064
- if (data2.connect) {
1065
- const len = data2.connect.length;
1066
- allKeys[index][1] = connected.slice(connectI, connectI + len);
1067
- connectI += len;
1327
+ if (item.connect) {
1328
+ const len3 = item.connect.length;
1329
+ allKeys[index][1] = connected.slice(connectI, connectI + len3);
1330
+ connectI += len3;
1068
1331
  }
1069
- });
1070
- await joinTableQuery.count()._createMany(
1071
- allKeys.flatMap(([selfData, relationKeys]) => {
1072
- const selfKey = selfData[primaryKey];
1073
- return relationKeys.map((relationData) => ({
1074
- [foreignKey]: selfKey,
1075
- [associationForeignKey]: relationData[associationPrimaryKey]
1076
- }));
1077
- })
1078
- );
1332
+ }
1333
+ const records = [];
1334
+ for (const [selfData, relationKeys] of allKeys) {
1335
+ const obj = {};
1336
+ for (let i = 0; i < len; i++) {
1337
+ obj[foreignKeys[i]] = selfData[primaryKeys[i]];
1338
+ }
1339
+ for (const relationData of relationKeys) {
1340
+ const record = __spreadValues$3({}, obj);
1341
+ for (let i = 0; i < throughLen; i++) {
1342
+ record[throughForeignKeys[i]] = relationData[throughPrimaryKeys[i]];
1343
+ }
1344
+ records.push(record);
1345
+ }
1346
+ }
1347
+ await joinTableQuery.count()._createMany(records);
1079
1348
  };
1080
1349
  };
1081
1350
  const nestedUpdate = (state) => {
1351
+ const len = state.primaryKeys.length;
1352
+ const throughLen = state.throughPrimaryKeys.length;
1082
1353
  return async (_, data, params) => {
1083
1354
  if (params.create) {
1084
- const ids = await state.relatedTableQuery.pluck(state.associationPrimaryKey)._createMany(params.create);
1085
- await state.joinTableQuery.createMany(
1086
- data.flatMap(
1087
- (item) => ids.map((id) => ({
1088
- [state.foreignKey]: item[state.primaryKey],
1089
- [state.associationForeignKey]: id
1090
- }))
1091
- )
1092
- );
1355
+ const idsRows = await state.relatedTableQuery.select(...state.throughPrimaryKeys)._rows()._createMany(params.create);
1356
+ const records = [];
1357
+ for (const item of data) {
1358
+ const obj = {};
1359
+ for (let i = 0; i < len; i++) {
1360
+ obj[state.foreignKeys[i]] = item[state.primaryKeys[i]];
1361
+ }
1362
+ for (const ids of idsRows) {
1363
+ const record = __spreadValues$3({}, obj);
1364
+ for (let i = 0; i < throughLen; i++) {
1365
+ record[state.throughForeignKeys[i]] = ids[i];
1366
+ }
1367
+ records.push(record);
1368
+ }
1369
+ }
1370
+ await state.joinTableQuery.createMany(records);
1093
1371
  }
1094
1372
  if (params.update) {
1095
- await state.relatedTableQuery.whereExists(
1096
- state.joinTableQuery,
1097
- (q) => (
1098
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1373
+ await state.relatedTableQuery.whereExists(state.joinTableQuery, (q) => {
1374
+ for (let i = 0; i < throughLen; i++) {
1099
1375
  q._on(
1100
- state.associationForeignKeyFull,
1101
- state.associationPrimaryKeyFull
1102
- )._where({
1103
- IN: {
1104
- columns: [state.foreignKeyFull],
1105
- values: [data.map((item) => item[state.primaryKey])]
1106
- }
1107
- })
1108
- )
1109
- )._where(
1110
- Array.isArray(params.update.where) ? { OR: params.update.where } : params.update.where
1111
- )._update(params.update.data);
1376
+ state.throughForeignKeysFull[i],
1377
+ state.throughPrimaryKeysFull[i]
1378
+ );
1379
+ }
1380
+ return q._where({
1381
+ IN: {
1382
+ columns: state.foreignKeysFull,
1383
+ values: data.map(
1384
+ (item) => state.primaryKeys.map((key) => item[key])
1385
+ )
1386
+ }
1387
+ });
1388
+ })._where(conditionsToWhereArg(params.update.where))._update(params.update.data);
1112
1389
  }
1113
1390
  if (params.disconnect) {
1114
1391
  await queryJoinTable(state, data, params.disconnect)._delete();
1115
1392
  }
1116
1393
  if (params.delete) {
1117
1394
  const j = queryJoinTable(state, data, params.delete);
1118
- const ids = await j._pluck(state.associationForeignKey)._delete();
1119
- await queryRelatedTable(state.relatedTableQuery, {
1120
- [state.associationPrimaryKey]: { in: ids }
1395
+ const idsRows = await j._select(...state.throughForeignKeys)._rows()._delete();
1396
+ await state.relatedTableQuery.where({
1397
+ IN: {
1398
+ columns: state.throughPrimaryKeys,
1399
+ values: idsRows
1400
+ }
1121
1401
  })._delete();
1122
1402
  }
1123
1403
  if (params.set) {
1124
1404
  const j = queryJoinTable(state, data);
1125
1405
  await j._delete();
1126
1406
  delete j.q[pqb.toSQLCacheKey];
1127
- const ids = await queryRelatedTable(
1128
- state.relatedTableQuery,
1129
- params.set
1130
- )._pluck(state.associationPrimaryKey);
1131
- await insertToJoinTable(state, j, data, ids);
1407
+ const idsRows = await state.relatedTableQuery.where(conditionsToWhereArg(params.set))._select(...state.throughPrimaryKeys)._rows();
1408
+ await insertToJoinTable(state, j, data, idsRows);
1132
1409
  }
1133
1410
  };
1134
1411
  };