orchid-orm 0.0.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/.env.example +1 -0
- package/.rush/temp/shrinkwrap-deps.json +327 -0
- package/README.md +5 -0
- package/dist/index.d.ts +228 -0
- package/dist/index.esm.js +1116 -0
- package/dist/index.esm.js.map +1 -0
- package/dist/index.js +1122 -0
- package/dist/index.js.map +1 -0
- package/jest-setup.ts +7 -0
- package/package.json +58 -0
- package/rollup.config.js +3 -0
- package/src/index.ts +3 -0
- package/src/model.test.ts +92 -0
- package/src/model.ts +169 -0
- package/src/orm.test.ts +73 -0
- package/src/orm.ts +74 -0
- package/src/relations/belongsTo.test.ts +890 -0
- package/src/relations/belongsTo.ts +247 -0
- package/src/relations/hasAndBelongsToMany.test.ts +1122 -0
- package/src/relations/hasAndBelongsToMany.ts +398 -0
- package/src/relations/hasMany.test.ts +2003 -0
- package/src/relations/hasMany.ts +332 -0
- package/src/relations/hasOne.test.ts +1219 -0
- package/src/relations/hasOne.ts +278 -0
- package/src/relations/relations.test.ts +37 -0
- package/src/relations/relations.ts +316 -0
- package/src/relations/utils.ts +12 -0
- package/src/repo.test.ts +200 -0
- package/src/repo.ts +119 -0
- package/src/test-utils/test-db.ts +32 -0
- package/src/test-utils/test-models.ts +194 -0
- package/src/test-utils/test-utils.ts +69 -0
- package/src/transaction.test.ts +45 -0
- package/src/transaction.ts +27 -0
- package/tsconfig.json +13 -0
|
@@ -0,0 +1,2003 @@
|
|
|
1
|
+
import { db, pgConfig } from '../test-utils/test-db';
|
|
2
|
+
import {
|
|
3
|
+
assertType,
|
|
4
|
+
chatData,
|
|
5
|
+
expectSql,
|
|
6
|
+
messageData,
|
|
7
|
+
userData,
|
|
8
|
+
useTestDatabase,
|
|
9
|
+
} from '../test-utils/test-utils';
|
|
10
|
+
import { RelationQuery } from 'pqb';
|
|
11
|
+
import { Chat, Message, Model, Profile, User } from '../test-utils/test-models';
|
|
12
|
+
import { orchidORM } from '../orm';
|
|
13
|
+
|
|
14
|
+
describe('hasMany', () => {
|
|
15
|
+
useTestDatabase();
|
|
16
|
+
|
|
17
|
+
describe('querying', () => {
|
|
18
|
+
it('should have method to query related data', async () => {
|
|
19
|
+
const messagesQuery = db.message.all();
|
|
20
|
+
|
|
21
|
+
assertType<
|
|
22
|
+
typeof db.user.messages,
|
|
23
|
+
RelationQuery<
|
|
24
|
+
'messages',
|
|
25
|
+
{ id: number },
|
|
26
|
+
'authorId',
|
|
27
|
+
typeof messagesQuery,
|
|
28
|
+
false,
|
|
29
|
+
true,
|
|
30
|
+
true
|
|
31
|
+
>
|
|
32
|
+
>();
|
|
33
|
+
|
|
34
|
+
const userId = await db.user.get('id').create(userData);
|
|
35
|
+
const chatId = await db.chat.get('id').create(chatData);
|
|
36
|
+
|
|
37
|
+
await db.message.createMany([
|
|
38
|
+
{ ...messageData, authorId: userId, chatId },
|
|
39
|
+
{ ...messageData, authorId: userId, chatId },
|
|
40
|
+
]);
|
|
41
|
+
|
|
42
|
+
const user = await db.user.find(userId);
|
|
43
|
+
const query = db.user.messages(user);
|
|
44
|
+
|
|
45
|
+
expectSql(
|
|
46
|
+
query.toSql(),
|
|
47
|
+
`
|
|
48
|
+
SELECT * FROM "message" AS "messages"
|
|
49
|
+
WHERE "messages"."authorId" = $1
|
|
50
|
+
`,
|
|
51
|
+
[userId],
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
const messages = await query;
|
|
55
|
+
|
|
56
|
+
expect(messages).toMatchObject([messageData, messageData]);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('should handle chained query', () => {
|
|
60
|
+
const query = db.user
|
|
61
|
+
.where({ name: 'name' })
|
|
62
|
+
.messages.where({ text: 'text' });
|
|
63
|
+
|
|
64
|
+
expectSql(
|
|
65
|
+
query.toSql(),
|
|
66
|
+
`
|
|
67
|
+
SELECT * FROM "message" AS "messages"
|
|
68
|
+
WHERE EXISTS (
|
|
69
|
+
SELECT 1 FROM "user"
|
|
70
|
+
WHERE "user"."name" = $1
|
|
71
|
+
AND "user"."id" = "messages"."authorId"
|
|
72
|
+
LIMIT 1
|
|
73
|
+
)
|
|
74
|
+
AND "messages"."text" = $2
|
|
75
|
+
`,
|
|
76
|
+
['name', 'text'],
|
|
77
|
+
);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('should have create with defaults of provided id', () => {
|
|
81
|
+
const user = { id: 1 };
|
|
82
|
+
const query = db.user.messages(user).count().create({
|
|
83
|
+
chatId: 2,
|
|
84
|
+
text: 'text',
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
expectSql(
|
|
88
|
+
query.toSql(),
|
|
89
|
+
`
|
|
90
|
+
INSERT INTO "message"("authorId", "chatId", "text")
|
|
91
|
+
VALUES ($1, $2, $3)
|
|
92
|
+
`,
|
|
93
|
+
[1, 2, 'text'],
|
|
94
|
+
);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
describe('create based on a query', () => {
|
|
98
|
+
it('should have create based on a query', () => {
|
|
99
|
+
const query = db.chat.find(1).messages.create({
|
|
100
|
+
text: 'text',
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
expectSql(
|
|
104
|
+
query.toSql(),
|
|
105
|
+
`
|
|
106
|
+
INSERT INTO "message"("chatId", "text")
|
|
107
|
+
SELECT "chat"."id" AS "chatId", $1
|
|
108
|
+
FROM "chat"
|
|
109
|
+
WHERE "chat"."id" = $2
|
|
110
|
+
LIMIT $3
|
|
111
|
+
RETURNING *
|
|
112
|
+
`,
|
|
113
|
+
['text', 1, 1],
|
|
114
|
+
);
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it('should throw when the main query returns many records', async () => {
|
|
118
|
+
await expect(
|
|
119
|
+
async () =>
|
|
120
|
+
await db.chat.messages.create({
|
|
121
|
+
text: 'text',
|
|
122
|
+
}),
|
|
123
|
+
).rejects.toThrow(
|
|
124
|
+
'Cannot create based on a query which returns multiple records',
|
|
125
|
+
);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it('should throw when main record is not found', async () => {
|
|
129
|
+
await expect(
|
|
130
|
+
async () =>
|
|
131
|
+
await db.chat.find(1).messages.create({
|
|
132
|
+
text: 'text',
|
|
133
|
+
}),
|
|
134
|
+
).rejects.toThrow('Record is not found');
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it('should not throw when searching with findOptional', async () => {
|
|
138
|
+
await db.chat.findOptional(1).messages.takeOptional().create({
|
|
139
|
+
text: 'text',
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it('should have chained delete', () => {
|
|
145
|
+
const query = db.chat
|
|
146
|
+
.where({ title: 'title' })
|
|
147
|
+
.messages.where({ text: 'text' })
|
|
148
|
+
.delete();
|
|
149
|
+
|
|
150
|
+
expectSql(
|
|
151
|
+
query.toSql(),
|
|
152
|
+
`
|
|
153
|
+
DELETE FROM "message" AS "messages"
|
|
154
|
+
WHERE EXISTS (
|
|
155
|
+
SELECT 1 FROM "chat"
|
|
156
|
+
WHERE "chat"."title" = $1
|
|
157
|
+
AND "chat"."id" = "messages"."chatId"
|
|
158
|
+
LIMIT 1
|
|
159
|
+
)
|
|
160
|
+
AND "messages"."text" = $2
|
|
161
|
+
`,
|
|
162
|
+
['title', 'text'],
|
|
163
|
+
);
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
it('should have proper joinQuery', () => {
|
|
167
|
+
expectSql(
|
|
168
|
+
db.user.relations.messages
|
|
169
|
+
.joinQuery(db.user.as('u'), db.message.as('m'))
|
|
170
|
+
.toSql(),
|
|
171
|
+
`
|
|
172
|
+
SELECT * FROM "message" AS "m"
|
|
173
|
+
WHERE "m"."authorId" = "u"."id"
|
|
174
|
+
`,
|
|
175
|
+
);
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
it('should be supported in whereExists', () => {
|
|
179
|
+
expectSql(
|
|
180
|
+
db.user.whereExists('messages').toSql(),
|
|
181
|
+
`
|
|
182
|
+
SELECT * FROM "user"
|
|
183
|
+
WHERE EXISTS (
|
|
184
|
+
SELECT 1 FROM "message" AS "messages"
|
|
185
|
+
WHERE "messages"."authorId" = "user"."id"
|
|
186
|
+
LIMIT 1
|
|
187
|
+
)
|
|
188
|
+
`,
|
|
189
|
+
);
|
|
190
|
+
|
|
191
|
+
expectSql(
|
|
192
|
+
db.user
|
|
193
|
+
.as('u')
|
|
194
|
+
.whereExists('messages', (q) => q.where({ text: 'text' }))
|
|
195
|
+
.toSql(),
|
|
196
|
+
`
|
|
197
|
+
SELECT * FROM "user" AS "u"
|
|
198
|
+
WHERE EXISTS (
|
|
199
|
+
SELECT 1 FROM "message" AS "messages"
|
|
200
|
+
WHERE "messages"."authorId" = "u"."id"
|
|
201
|
+
AND "messages"."text" = $1
|
|
202
|
+
LIMIT 1
|
|
203
|
+
)
|
|
204
|
+
`,
|
|
205
|
+
['text'],
|
|
206
|
+
);
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
it('should be supported in join', () => {
|
|
210
|
+
const query = db.user
|
|
211
|
+
.as('u')
|
|
212
|
+
.join('messages', (q) => q.where({ text: 'text' }))
|
|
213
|
+
.select('name', 'messages.text');
|
|
214
|
+
|
|
215
|
+
assertType<Awaited<typeof query>, { name: string; text: string }[]>();
|
|
216
|
+
|
|
217
|
+
expectSql(
|
|
218
|
+
query.toSql(),
|
|
219
|
+
`
|
|
220
|
+
SELECT "u"."name", "messages"."text" FROM "user" AS "u"
|
|
221
|
+
JOIN "message" AS "messages"
|
|
222
|
+
ON "messages"."authorId" = "u"."id"
|
|
223
|
+
AND "messages"."text" = $1
|
|
224
|
+
`,
|
|
225
|
+
['text'],
|
|
226
|
+
);
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
describe('select', () => {
|
|
230
|
+
it('should be selectable', () => {
|
|
231
|
+
const query = db.user.as('u').select('id', {
|
|
232
|
+
messages: (q) => q.messages.where({ text: 'text' }),
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
assertType<
|
|
236
|
+
Awaited<typeof query>,
|
|
237
|
+
{ id: number; messages: Message[] }[]
|
|
238
|
+
>();
|
|
239
|
+
|
|
240
|
+
expectSql(
|
|
241
|
+
query.toSql(),
|
|
242
|
+
`
|
|
243
|
+
SELECT
|
|
244
|
+
"u"."id",
|
|
245
|
+
(
|
|
246
|
+
SELECT COALESCE(json_agg(row_to_json("t".*)), '[]')
|
|
247
|
+
FROM (
|
|
248
|
+
SELECT * FROM "message" AS "messages"
|
|
249
|
+
WHERE "messages"."text" = $1
|
|
250
|
+
AND "messages"."authorId" = "u"."id"
|
|
251
|
+
) AS "t"
|
|
252
|
+
) AS "messages"
|
|
253
|
+
FROM "user" AS "u"
|
|
254
|
+
`,
|
|
255
|
+
['text'],
|
|
256
|
+
);
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
it('should be selectable by relation name', () => {
|
|
260
|
+
const query = db.user.select('id', 'messages');
|
|
261
|
+
|
|
262
|
+
assertType<
|
|
263
|
+
Awaited<typeof query>,
|
|
264
|
+
{ id: number; messages: Message[] }[]
|
|
265
|
+
>();
|
|
266
|
+
|
|
267
|
+
expectSql(
|
|
268
|
+
query.toSql(),
|
|
269
|
+
`
|
|
270
|
+
SELECT
|
|
271
|
+
"user"."id",
|
|
272
|
+
(
|
|
273
|
+
SELECT COALESCE(json_agg(row_to_json("t".*)), '[]')
|
|
274
|
+
FROM (
|
|
275
|
+
SELECT * FROM "message" AS "messages"
|
|
276
|
+
WHERE "messages"."authorId" = "user"."id"
|
|
277
|
+
) AS "t"
|
|
278
|
+
) AS "messages"
|
|
279
|
+
FROM "user"
|
|
280
|
+
`,
|
|
281
|
+
);
|
|
282
|
+
});
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
it('should allow to select count', () => {
|
|
286
|
+
const query = db.user.as('u').select('id', {
|
|
287
|
+
messagesCount: (q) => q.messages.count(),
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
assertType<
|
|
291
|
+
Awaited<typeof query>,
|
|
292
|
+
{ id: number; messagesCount: number }[]
|
|
293
|
+
>();
|
|
294
|
+
|
|
295
|
+
expectSql(
|
|
296
|
+
query.toSql(),
|
|
297
|
+
`
|
|
298
|
+
SELECT
|
|
299
|
+
"u"."id",
|
|
300
|
+
(
|
|
301
|
+
SELECT count(*) FROM "message" AS "messages"
|
|
302
|
+
WHERE "messages"."authorId" = "u"."id"
|
|
303
|
+
) AS "messagesCount"
|
|
304
|
+
FROM "user" AS "u"
|
|
305
|
+
`,
|
|
306
|
+
);
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
it('should allow to pluck values', () => {
|
|
310
|
+
const query = db.user.as('u').select('id', {
|
|
311
|
+
texts: (q) => q.messages.pluck('text'),
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
assertType<Awaited<typeof query>, { id: number; texts: string[] }[]>();
|
|
315
|
+
|
|
316
|
+
expectSql(
|
|
317
|
+
query.toSql(),
|
|
318
|
+
`
|
|
319
|
+
SELECT
|
|
320
|
+
"u"."id",
|
|
321
|
+
(
|
|
322
|
+
SELECT COALESCE(json_agg("c"), '[]')
|
|
323
|
+
FROM (
|
|
324
|
+
SELECT "messages"."text" AS "c"
|
|
325
|
+
FROM "message" AS "messages"
|
|
326
|
+
WHERE "messages"."authorId" = "u"."id"
|
|
327
|
+
) AS "t"
|
|
328
|
+
) AS "texts"
|
|
329
|
+
FROM "user" AS "u"
|
|
330
|
+
`,
|
|
331
|
+
);
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
it('should handle exists sub query', () => {
|
|
335
|
+
const query = db.user.as('u').select('id', {
|
|
336
|
+
hasMessages: (q) => q.messages.exists(),
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
assertType<
|
|
340
|
+
Awaited<typeof query>,
|
|
341
|
+
{ id: number; hasMessages: boolean }[]
|
|
342
|
+
>();
|
|
343
|
+
|
|
344
|
+
expectSql(
|
|
345
|
+
query.toSql(),
|
|
346
|
+
`
|
|
347
|
+
SELECT
|
|
348
|
+
"u"."id",
|
|
349
|
+
COALESCE((
|
|
350
|
+
SELECT true
|
|
351
|
+
FROM "message" AS "messages"
|
|
352
|
+
WHERE "messages"."authorId" = "u"."id"
|
|
353
|
+
), false) AS "hasMessages"
|
|
354
|
+
FROM "user" AS "u"
|
|
355
|
+
`,
|
|
356
|
+
);
|
|
357
|
+
});
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
describe('create', () => {
|
|
361
|
+
const checkUser = (user: User, name: string) => {
|
|
362
|
+
expect(user).toEqual({
|
|
363
|
+
...userData,
|
|
364
|
+
id: user.id,
|
|
365
|
+
name: name,
|
|
366
|
+
active: null,
|
|
367
|
+
age: null,
|
|
368
|
+
data: null,
|
|
369
|
+
picture: null,
|
|
370
|
+
});
|
|
371
|
+
};
|
|
372
|
+
|
|
373
|
+
const checkMessages = ({
|
|
374
|
+
messages,
|
|
375
|
+
userId,
|
|
376
|
+
chatId,
|
|
377
|
+
text1,
|
|
378
|
+
text2,
|
|
379
|
+
}: {
|
|
380
|
+
messages: Message[];
|
|
381
|
+
userId: number;
|
|
382
|
+
chatId: number;
|
|
383
|
+
text1: string;
|
|
384
|
+
text2: string;
|
|
385
|
+
}) => {
|
|
386
|
+
expect(messages).toMatchObject([
|
|
387
|
+
{
|
|
388
|
+
id: messages[0].id,
|
|
389
|
+
authorId: userId,
|
|
390
|
+
text: text1,
|
|
391
|
+
chatId,
|
|
392
|
+
meta: null,
|
|
393
|
+
},
|
|
394
|
+
{
|
|
395
|
+
id: messages[1].id,
|
|
396
|
+
authorId: userId,
|
|
397
|
+
text: text2,
|
|
398
|
+
chatId,
|
|
399
|
+
meta: null,
|
|
400
|
+
},
|
|
401
|
+
]);
|
|
402
|
+
};
|
|
403
|
+
|
|
404
|
+
describe('nested create', () => {
|
|
405
|
+
it('should support create', async () => {
|
|
406
|
+
const chatId = await db.chat.get('id').create(chatData);
|
|
407
|
+
|
|
408
|
+
const user = await db.user.create({
|
|
409
|
+
...userData,
|
|
410
|
+
name: 'user 1',
|
|
411
|
+
messages: {
|
|
412
|
+
create: [
|
|
413
|
+
{
|
|
414
|
+
...messageData,
|
|
415
|
+
text: 'message 1',
|
|
416
|
+
chatId,
|
|
417
|
+
},
|
|
418
|
+
{
|
|
419
|
+
...messageData,
|
|
420
|
+
text: 'message 2',
|
|
421
|
+
chatId,
|
|
422
|
+
},
|
|
423
|
+
],
|
|
424
|
+
},
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
checkUser(user, 'user 1');
|
|
428
|
+
|
|
429
|
+
const messages = await db.message.order('text');
|
|
430
|
+
checkMessages({
|
|
431
|
+
messages,
|
|
432
|
+
userId: user.id,
|
|
433
|
+
chatId,
|
|
434
|
+
text1: 'message 1',
|
|
435
|
+
text2: 'message 2',
|
|
436
|
+
});
|
|
437
|
+
});
|
|
438
|
+
|
|
439
|
+
it('should support create in batch create', async () => {
|
|
440
|
+
const chatId = await db.chat.get('id').create(chatData);
|
|
441
|
+
|
|
442
|
+
const user = await db.user.createMany([
|
|
443
|
+
{
|
|
444
|
+
...userData,
|
|
445
|
+
name: 'user 1',
|
|
446
|
+
messages: {
|
|
447
|
+
create: [
|
|
448
|
+
{
|
|
449
|
+
...messageData,
|
|
450
|
+
text: 'message 1',
|
|
451
|
+
chatId,
|
|
452
|
+
},
|
|
453
|
+
{
|
|
454
|
+
...messageData,
|
|
455
|
+
text: 'message 2',
|
|
456
|
+
chatId,
|
|
457
|
+
},
|
|
458
|
+
],
|
|
459
|
+
},
|
|
460
|
+
},
|
|
461
|
+
{
|
|
462
|
+
...userData,
|
|
463
|
+
name: 'user 2',
|
|
464
|
+
messages: {
|
|
465
|
+
create: [
|
|
466
|
+
{
|
|
467
|
+
...messageData,
|
|
468
|
+
text: 'message 3',
|
|
469
|
+
chatId,
|
|
470
|
+
},
|
|
471
|
+
{
|
|
472
|
+
...messageData,
|
|
473
|
+
text: 'message 4',
|
|
474
|
+
chatId,
|
|
475
|
+
},
|
|
476
|
+
],
|
|
477
|
+
},
|
|
478
|
+
},
|
|
479
|
+
]);
|
|
480
|
+
|
|
481
|
+
checkUser(user[0], 'user 1');
|
|
482
|
+
checkUser(user[1], 'user 2');
|
|
483
|
+
|
|
484
|
+
const messages = await db.message.order('text');
|
|
485
|
+
checkMessages({
|
|
486
|
+
messages: messages.slice(0, 2),
|
|
487
|
+
userId: user[0].id,
|
|
488
|
+
chatId,
|
|
489
|
+
text1: 'message 1',
|
|
490
|
+
text2: 'message 2',
|
|
491
|
+
});
|
|
492
|
+
|
|
493
|
+
checkMessages({
|
|
494
|
+
messages: messages.slice(2, 4),
|
|
495
|
+
userId: user[1].id,
|
|
496
|
+
chatId,
|
|
497
|
+
text1: 'message 3',
|
|
498
|
+
text2: 'message 4',
|
|
499
|
+
});
|
|
500
|
+
});
|
|
501
|
+
|
|
502
|
+
it('should ignore empty create list', async () => {
|
|
503
|
+
const user = await db.user.create({
|
|
504
|
+
...userData,
|
|
505
|
+
name: 'user 1',
|
|
506
|
+
messages: {
|
|
507
|
+
create: [],
|
|
508
|
+
},
|
|
509
|
+
});
|
|
510
|
+
|
|
511
|
+
checkUser(user, 'user 1');
|
|
512
|
+
});
|
|
513
|
+
});
|
|
514
|
+
|
|
515
|
+
describe('nested connect', () => {
|
|
516
|
+
it('should support connect', async () => {
|
|
517
|
+
const chatId = await db.chat.get('id').create(chatData);
|
|
518
|
+
await db.message.createMany([
|
|
519
|
+
{
|
|
520
|
+
...messageData,
|
|
521
|
+
chatId,
|
|
522
|
+
user: { create: { ...userData, name: 'tmp' } },
|
|
523
|
+
text: 'message 1',
|
|
524
|
+
},
|
|
525
|
+
{
|
|
526
|
+
...messageData,
|
|
527
|
+
chatId,
|
|
528
|
+
user: { connect: { name: 'tmp' } },
|
|
529
|
+
text: 'message 2',
|
|
530
|
+
},
|
|
531
|
+
]);
|
|
532
|
+
|
|
533
|
+
const user = await db.user.create({
|
|
534
|
+
...userData,
|
|
535
|
+
name: 'user 1',
|
|
536
|
+
messages: {
|
|
537
|
+
connect: [
|
|
538
|
+
{
|
|
539
|
+
text: 'message 1',
|
|
540
|
+
},
|
|
541
|
+
{
|
|
542
|
+
text: 'message 2',
|
|
543
|
+
},
|
|
544
|
+
],
|
|
545
|
+
},
|
|
546
|
+
});
|
|
547
|
+
|
|
548
|
+
checkUser(user, 'user 1');
|
|
549
|
+
|
|
550
|
+
const messages = await db.message.order('text');
|
|
551
|
+
checkMessages({
|
|
552
|
+
messages,
|
|
553
|
+
userId: user.id,
|
|
554
|
+
chatId,
|
|
555
|
+
text1: 'message 1',
|
|
556
|
+
text2: 'message 2',
|
|
557
|
+
});
|
|
558
|
+
});
|
|
559
|
+
|
|
560
|
+
it('should support connect in batch create', async () => {
|
|
561
|
+
const chatId = await db.chat.get('id').create(chatData);
|
|
562
|
+
await db.message.createMany([
|
|
563
|
+
{
|
|
564
|
+
...messageData,
|
|
565
|
+
chatId,
|
|
566
|
+
user: { create: { ...userData, name: 'tmp' } },
|
|
567
|
+
text: 'message 1',
|
|
568
|
+
},
|
|
569
|
+
{
|
|
570
|
+
...messageData,
|
|
571
|
+
chatId,
|
|
572
|
+
user: { connect: { name: 'tmp' } },
|
|
573
|
+
text: 'message 2',
|
|
574
|
+
},
|
|
575
|
+
{
|
|
576
|
+
...messageData,
|
|
577
|
+
chatId,
|
|
578
|
+
user: { connect: { name: 'tmp' } },
|
|
579
|
+
text: 'message 3',
|
|
580
|
+
},
|
|
581
|
+
{
|
|
582
|
+
...messageData,
|
|
583
|
+
chatId,
|
|
584
|
+
user: { connect: { name: 'tmp' } },
|
|
585
|
+
text: 'message 4',
|
|
586
|
+
},
|
|
587
|
+
]);
|
|
588
|
+
|
|
589
|
+
const user = await db.user.createMany([
|
|
590
|
+
{
|
|
591
|
+
...userData,
|
|
592
|
+
name: 'user 1',
|
|
593
|
+
messages: {
|
|
594
|
+
connect: [
|
|
595
|
+
{
|
|
596
|
+
text: 'message 1',
|
|
597
|
+
},
|
|
598
|
+
{
|
|
599
|
+
text: 'message 2',
|
|
600
|
+
},
|
|
601
|
+
],
|
|
602
|
+
},
|
|
603
|
+
},
|
|
604
|
+
{
|
|
605
|
+
...userData,
|
|
606
|
+
name: 'user 2',
|
|
607
|
+
messages: {
|
|
608
|
+
connect: [
|
|
609
|
+
{
|
|
610
|
+
text: 'message 3',
|
|
611
|
+
},
|
|
612
|
+
{
|
|
613
|
+
text: 'message 4',
|
|
614
|
+
},
|
|
615
|
+
],
|
|
616
|
+
},
|
|
617
|
+
},
|
|
618
|
+
]);
|
|
619
|
+
|
|
620
|
+
checkUser(user[0], 'user 1');
|
|
621
|
+
checkUser(user[1], 'user 2');
|
|
622
|
+
|
|
623
|
+
const messages = await db.message.order('text');
|
|
624
|
+
checkMessages({
|
|
625
|
+
messages: messages.slice(0, 2),
|
|
626
|
+
userId: user[0].id,
|
|
627
|
+
chatId,
|
|
628
|
+
text1: 'message 1',
|
|
629
|
+
text2: 'message 2',
|
|
630
|
+
});
|
|
631
|
+
|
|
632
|
+
checkMessages({
|
|
633
|
+
messages: messages.slice(2, 4),
|
|
634
|
+
userId: user[1].id,
|
|
635
|
+
chatId,
|
|
636
|
+
text1: 'message 3',
|
|
637
|
+
text2: 'message 4',
|
|
638
|
+
});
|
|
639
|
+
});
|
|
640
|
+
|
|
641
|
+
it('should ignore empty connect list', async () => {
|
|
642
|
+
const user = await db.user.create({
|
|
643
|
+
...userData,
|
|
644
|
+
name: 'user 1',
|
|
645
|
+
messages: {
|
|
646
|
+
connect: [],
|
|
647
|
+
},
|
|
648
|
+
});
|
|
649
|
+
|
|
650
|
+
checkUser(user, 'user 1');
|
|
651
|
+
});
|
|
652
|
+
});
|
|
653
|
+
|
|
654
|
+
describe('connectOrCreate', () => {
|
|
655
|
+
it('should support connect or create', async () => {
|
|
656
|
+
const chatId = await db.chat.get('id').create(chatData);
|
|
657
|
+
const messageId = await db.message.get('id').create({
|
|
658
|
+
...messageData,
|
|
659
|
+
chatId,
|
|
660
|
+
user: { create: { ...userData, name: 'tmp' } },
|
|
661
|
+
text: 'message 1',
|
|
662
|
+
});
|
|
663
|
+
|
|
664
|
+
const user = await db.user.create({
|
|
665
|
+
...userData,
|
|
666
|
+
name: 'user 1',
|
|
667
|
+
messages: {
|
|
668
|
+
connectOrCreate: [
|
|
669
|
+
{
|
|
670
|
+
where: { text: 'message 1' },
|
|
671
|
+
create: { ...messageData, chatId, text: 'message 1' },
|
|
672
|
+
},
|
|
673
|
+
{
|
|
674
|
+
where: { text: 'message 2' },
|
|
675
|
+
create: { ...messageData, chatId, text: 'message 2' },
|
|
676
|
+
},
|
|
677
|
+
],
|
|
678
|
+
},
|
|
679
|
+
});
|
|
680
|
+
|
|
681
|
+
checkUser(user, 'user 1');
|
|
682
|
+
|
|
683
|
+
const messages = await db.message.order('text');
|
|
684
|
+
expect(messages[0].id).toBe(messageId);
|
|
685
|
+
|
|
686
|
+
checkMessages({
|
|
687
|
+
messages,
|
|
688
|
+
userId: user.id,
|
|
689
|
+
chatId,
|
|
690
|
+
text1: 'message 1',
|
|
691
|
+
text2: 'message 2',
|
|
692
|
+
});
|
|
693
|
+
});
|
|
694
|
+
|
|
695
|
+
it('should support connect or create in batch create', async () => {
|
|
696
|
+
const chatId = await db.chat.get('id').create(chatData);
|
|
697
|
+
const [{ id: message1Id }, { id: message4Id }] = await db.message
|
|
698
|
+
.select('id')
|
|
699
|
+
.createMany([
|
|
700
|
+
{
|
|
701
|
+
...messageData,
|
|
702
|
+
chatId,
|
|
703
|
+
user: { create: { ...userData, name: 'tmp' } },
|
|
704
|
+
text: 'message 1',
|
|
705
|
+
},
|
|
706
|
+
{
|
|
707
|
+
...messageData,
|
|
708
|
+
chatId,
|
|
709
|
+
user: { create: { ...userData, name: 'tmp' } },
|
|
710
|
+
text: 'message 4',
|
|
711
|
+
},
|
|
712
|
+
]);
|
|
713
|
+
|
|
714
|
+
const users = await db.user.createMany([
|
|
715
|
+
{
|
|
716
|
+
...userData,
|
|
717
|
+
name: 'user 1',
|
|
718
|
+
messages: {
|
|
719
|
+
connectOrCreate: [
|
|
720
|
+
{
|
|
721
|
+
where: { text: 'message 1' },
|
|
722
|
+
create: { ...messageData, chatId, text: 'message 1' },
|
|
723
|
+
},
|
|
724
|
+
{
|
|
725
|
+
where: { text: 'message 2' },
|
|
726
|
+
create: { ...messageData, chatId, text: 'message 2' },
|
|
727
|
+
},
|
|
728
|
+
],
|
|
729
|
+
},
|
|
730
|
+
},
|
|
731
|
+
{
|
|
732
|
+
...userData,
|
|
733
|
+
name: 'user 2',
|
|
734
|
+
messages: {
|
|
735
|
+
connectOrCreate: [
|
|
736
|
+
{
|
|
737
|
+
where: { text: 'message 3' },
|
|
738
|
+
create: { ...messageData, chatId, text: 'message 3' },
|
|
739
|
+
},
|
|
740
|
+
{
|
|
741
|
+
where: { text: 'message 4' },
|
|
742
|
+
create: { ...messageData, chatId, text: 'message 4' },
|
|
743
|
+
},
|
|
744
|
+
],
|
|
745
|
+
},
|
|
746
|
+
},
|
|
747
|
+
]);
|
|
748
|
+
|
|
749
|
+
checkUser(users[0], 'user 1');
|
|
750
|
+
checkUser(users[1], 'user 2');
|
|
751
|
+
|
|
752
|
+
const messages = await db.message.order('text');
|
|
753
|
+
expect(messages[0].id).toBe(message1Id);
|
|
754
|
+
expect(messages[3].id).toBe(message4Id);
|
|
755
|
+
|
|
756
|
+
checkMessages({
|
|
757
|
+
messages: messages.slice(0, 2),
|
|
758
|
+
userId: users[0].id,
|
|
759
|
+
chatId,
|
|
760
|
+
text1: 'message 1',
|
|
761
|
+
text2: 'message 2',
|
|
762
|
+
});
|
|
763
|
+
|
|
764
|
+
checkMessages({
|
|
765
|
+
messages: messages.slice(2, 4),
|
|
766
|
+
userId: users[1].id,
|
|
767
|
+
chatId,
|
|
768
|
+
text1: 'message 3',
|
|
769
|
+
text2: 'message 4',
|
|
770
|
+
});
|
|
771
|
+
});
|
|
772
|
+
|
|
773
|
+
it('should ignore empty connectOrCreate list', async () => {
|
|
774
|
+
const user = await db.user.create({
|
|
775
|
+
...userData,
|
|
776
|
+
name: 'user 1',
|
|
777
|
+
messages: {
|
|
778
|
+
connectOrCreate: [],
|
|
779
|
+
},
|
|
780
|
+
});
|
|
781
|
+
|
|
782
|
+
checkUser(user, 'user 1');
|
|
783
|
+
});
|
|
784
|
+
});
|
|
785
|
+
});
|
|
786
|
+
|
|
787
|
+
describe('update', () => {
|
|
788
|
+
describe('disconnect', () => {
|
|
789
|
+
it('should nullify foreignKey', async () => {
|
|
790
|
+
const chatId = await db.chat
|
|
791
|
+
.get('id')
|
|
792
|
+
.create({ ...chatData, title: 'chat 1' });
|
|
793
|
+
|
|
794
|
+
const userId = await db.user.get('id').create({
|
|
795
|
+
...userData,
|
|
796
|
+
messages: {
|
|
797
|
+
create: [
|
|
798
|
+
{ ...messageData, chatId: chatId, text: 'message 1' },
|
|
799
|
+
{ ...messageData, chatId: chatId, text: 'message 2' },
|
|
800
|
+
{ ...messageData, chatId: chatId, text: 'message 3' },
|
|
801
|
+
],
|
|
802
|
+
},
|
|
803
|
+
});
|
|
804
|
+
|
|
805
|
+
await db.user.find(userId).update({
|
|
806
|
+
messages: {
|
|
807
|
+
disconnect: [{ text: 'message 1' }, { text: 'message 2' }],
|
|
808
|
+
},
|
|
809
|
+
});
|
|
810
|
+
|
|
811
|
+
const messages = await db.message.order('text');
|
|
812
|
+
expect(messages[0].authorId).toBe(null);
|
|
813
|
+
expect(messages[1].authorId).toBe(null);
|
|
814
|
+
expect(messages[2].authorId).toBe(userId);
|
|
815
|
+
});
|
|
816
|
+
|
|
817
|
+
it('should nullify foreignKey in batch update', async () => {
|
|
818
|
+
const chatId = await db.chat
|
|
819
|
+
.get('id')
|
|
820
|
+
.create({ ...chatData, title: 'chat 1' });
|
|
821
|
+
|
|
822
|
+
const userIds = await db.user.pluck('id').createMany([
|
|
823
|
+
{
|
|
824
|
+
...userData,
|
|
825
|
+
messages: {
|
|
826
|
+
create: [{ ...messageData, chatId: chatId, text: 'message 1' }],
|
|
827
|
+
},
|
|
828
|
+
},
|
|
829
|
+
{
|
|
830
|
+
...userData,
|
|
831
|
+
messages: {
|
|
832
|
+
create: [
|
|
833
|
+
{ ...messageData, chatId: chatId, text: 'message 2' },
|
|
834
|
+
{ ...messageData, chatId: chatId, text: 'message 3' },
|
|
835
|
+
],
|
|
836
|
+
},
|
|
837
|
+
},
|
|
838
|
+
]);
|
|
839
|
+
|
|
840
|
+
await db.user.where({ id: { in: userIds } }).update({
|
|
841
|
+
messages: {
|
|
842
|
+
disconnect: [{ text: 'message 1' }, { text: 'message 2' }],
|
|
843
|
+
},
|
|
844
|
+
});
|
|
845
|
+
|
|
846
|
+
const messages = await db.message.order('text');
|
|
847
|
+
expect(messages[0].authorId).toBe(null);
|
|
848
|
+
expect(messages[1].authorId).toBe(null);
|
|
849
|
+
expect(messages[2].authorId).toBe(userIds[1]);
|
|
850
|
+
});
|
|
851
|
+
|
|
852
|
+
it('should ignore empty disconnect list', async () => {
|
|
853
|
+
const id = await db.user.get('id').create(userData);
|
|
854
|
+
|
|
855
|
+
await db.user.find(id).update({
|
|
856
|
+
messages: {
|
|
857
|
+
disconnect: [],
|
|
858
|
+
},
|
|
859
|
+
});
|
|
860
|
+
});
|
|
861
|
+
});
|
|
862
|
+
|
|
863
|
+
describe('set', () => {
|
|
864
|
+
it('should nullify foreignKey of previous related record and set foreignKey to new related record', async () => {
|
|
865
|
+
const chatId = await db.chat.get('id').create(chatData);
|
|
866
|
+
const id = await db.user.get('id').create({
|
|
867
|
+
...userData,
|
|
868
|
+
messages: {
|
|
869
|
+
create: [
|
|
870
|
+
{ ...messageData, chatId, text: 'message 1' },
|
|
871
|
+
{ ...messageData, chatId, text: 'message 2' },
|
|
872
|
+
],
|
|
873
|
+
},
|
|
874
|
+
});
|
|
875
|
+
|
|
876
|
+
await db.message.create({ ...messageData, chatId, text: 'message 3' });
|
|
877
|
+
|
|
878
|
+
await db.user.find(id).update({
|
|
879
|
+
messages: {
|
|
880
|
+
set: { text: { in: ['message 2', 'message 3'] } },
|
|
881
|
+
},
|
|
882
|
+
});
|
|
883
|
+
|
|
884
|
+
const [message1, message2, message3] = await db.message.order({
|
|
885
|
+
text: 'ASC',
|
|
886
|
+
});
|
|
887
|
+
|
|
888
|
+
expect(message1.authorId).toBe(null);
|
|
889
|
+
expect(message2.authorId).toBe(id);
|
|
890
|
+
expect(message3.authorId).toBe(id);
|
|
891
|
+
});
|
|
892
|
+
|
|
893
|
+
it('should throw in batch update', async () => {
|
|
894
|
+
const query = db.user.where({ id: { in: [1, 2, 3] } }).update({
|
|
895
|
+
messages: {
|
|
896
|
+
// @ts-expect-error not allows in batch update
|
|
897
|
+
set: { text: { in: ['message 2', 'message 3'] } },
|
|
898
|
+
},
|
|
899
|
+
});
|
|
900
|
+
|
|
901
|
+
await expect(query).rejects.toThrow();
|
|
902
|
+
});
|
|
903
|
+
});
|
|
904
|
+
|
|
905
|
+
describe('delete', () => {
|
|
906
|
+
it('should delete related records', async () => {
|
|
907
|
+
const chatId = await db.chat.get('id').create(chatData);
|
|
908
|
+
|
|
909
|
+
const id = await db.user.get('id').create({
|
|
910
|
+
...userData,
|
|
911
|
+
messages: {
|
|
912
|
+
create: [
|
|
913
|
+
{ ...messageData, chatId, text: 'message 1' },
|
|
914
|
+
{ ...messageData, chatId, text: 'message 2' },
|
|
915
|
+
{ ...messageData, chatId, text: 'message 3' },
|
|
916
|
+
],
|
|
917
|
+
},
|
|
918
|
+
});
|
|
919
|
+
|
|
920
|
+
await db.user.find(id).update({
|
|
921
|
+
messages: {
|
|
922
|
+
delete: {
|
|
923
|
+
text: { in: ['message 1', 'message 2'] },
|
|
924
|
+
},
|
|
925
|
+
},
|
|
926
|
+
});
|
|
927
|
+
|
|
928
|
+
expect(await db.message.count()).toBe(1);
|
|
929
|
+
|
|
930
|
+
const messages = await db.user.messages({ id }).select('text');
|
|
931
|
+
expect(messages).toEqual([{ text: 'message 3' }]);
|
|
932
|
+
});
|
|
933
|
+
|
|
934
|
+
it('should delete related records in batch update', async () => {
|
|
935
|
+
const chatId = await db.chat.get('id').create(chatData);
|
|
936
|
+
|
|
937
|
+
const userIds = await db.user.pluck('id').createMany([
|
|
938
|
+
{
|
|
939
|
+
...userData,
|
|
940
|
+
messages: {
|
|
941
|
+
create: [{ ...messageData, chatId, text: 'message 1' }],
|
|
942
|
+
},
|
|
943
|
+
},
|
|
944
|
+
{
|
|
945
|
+
...userData,
|
|
946
|
+
messages: {
|
|
947
|
+
create: [
|
|
948
|
+
{ ...messageData, chatId, text: 'message 2' },
|
|
949
|
+
{ ...messageData, chatId, text: 'message 3' },
|
|
950
|
+
],
|
|
951
|
+
},
|
|
952
|
+
},
|
|
953
|
+
]);
|
|
954
|
+
|
|
955
|
+
await db.user.where({ id: { in: userIds } }).update({
|
|
956
|
+
messages: {
|
|
957
|
+
delete: [{ text: 'message 1' }, { text: 'message 2' }],
|
|
958
|
+
},
|
|
959
|
+
});
|
|
960
|
+
|
|
961
|
+
expect(await db.message.count()).toBe(1);
|
|
962
|
+
|
|
963
|
+
const messages = await db.user
|
|
964
|
+
.messages({ id: userIds[1] })
|
|
965
|
+
.select('text');
|
|
966
|
+
expect(messages).toEqual([{ text: 'message 3' }]);
|
|
967
|
+
});
|
|
968
|
+
|
|
969
|
+
it('should ignore empty delete list', async () => {
|
|
970
|
+
const chatId = await db.chat.get('id').create(chatData);
|
|
971
|
+
|
|
972
|
+
const id = await db.user.get('id').create({
|
|
973
|
+
...userData,
|
|
974
|
+
messages: {
|
|
975
|
+
create: [{ ...messageData, chatId, text: 'message 1' }],
|
|
976
|
+
},
|
|
977
|
+
});
|
|
978
|
+
|
|
979
|
+
await db.user.find(id).update({
|
|
980
|
+
messages: {
|
|
981
|
+
delete: [],
|
|
982
|
+
},
|
|
983
|
+
});
|
|
984
|
+
|
|
985
|
+
const messages = await db.user.messages({ id }).pluck('text');
|
|
986
|
+
expect(messages).toEqual(['message 1']);
|
|
987
|
+
});
|
|
988
|
+
});
|
|
989
|
+
|
|
990
|
+
describe('nested update', () => {
|
|
991
|
+
it('should update related records', async () => {
|
|
992
|
+
const chatId = await db.chat.get('id').create(chatData);
|
|
993
|
+
|
|
994
|
+
const id = await db.user.get('id').create({
|
|
995
|
+
...userData,
|
|
996
|
+
messages: {
|
|
997
|
+
create: [
|
|
998
|
+
{ ...messageData, chatId, text: 'message 1' },
|
|
999
|
+
{ ...messageData, chatId, text: 'message 2' },
|
|
1000
|
+
{ ...messageData, chatId, text: 'message 3' },
|
|
1001
|
+
],
|
|
1002
|
+
},
|
|
1003
|
+
});
|
|
1004
|
+
|
|
1005
|
+
await db.user.find(id).update({
|
|
1006
|
+
messages: {
|
|
1007
|
+
update: {
|
|
1008
|
+
where: {
|
|
1009
|
+
text: { in: ['message 1', 'message 3'] },
|
|
1010
|
+
},
|
|
1011
|
+
data: {
|
|
1012
|
+
text: 'updated',
|
|
1013
|
+
},
|
|
1014
|
+
},
|
|
1015
|
+
},
|
|
1016
|
+
});
|
|
1017
|
+
|
|
1018
|
+
const messages = await db.user
|
|
1019
|
+
.messages({ id })
|
|
1020
|
+
.order('id')
|
|
1021
|
+
.pluck('text');
|
|
1022
|
+
expect(messages).toEqual(['updated', 'message 2', 'updated']);
|
|
1023
|
+
});
|
|
1024
|
+
|
|
1025
|
+
it('should update related records in batch update', async () => {
|
|
1026
|
+
const chatId = await db.chat.get('id').create(chatData);
|
|
1027
|
+
|
|
1028
|
+
const userIds = await db.user.pluck('id').createMany([
|
|
1029
|
+
{
|
|
1030
|
+
...userData,
|
|
1031
|
+
messages: {
|
|
1032
|
+
create: [{ ...messageData, chatId, text: 'message 1' }],
|
|
1033
|
+
},
|
|
1034
|
+
},
|
|
1035
|
+
{
|
|
1036
|
+
...userData,
|
|
1037
|
+
messages: {
|
|
1038
|
+
create: [
|
|
1039
|
+
{ ...messageData, chatId, text: 'message 2' },
|
|
1040
|
+
{ ...messageData, chatId, text: 'message 3' },
|
|
1041
|
+
],
|
|
1042
|
+
},
|
|
1043
|
+
},
|
|
1044
|
+
]);
|
|
1045
|
+
|
|
1046
|
+
await db.user.where({ id: { in: userIds } }).update({
|
|
1047
|
+
messages: {
|
|
1048
|
+
update: {
|
|
1049
|
+
where: {
|
|
1050
|
+
text: { in: ['message 1', 'message 3'] },
|
|
1051
|
+
},
|
|
1052
|
+
data: {
|
|
1053
|
+
text: 'updated',
|
|
1054
|
+
},
|
|
1055
|
+
},
|
|
1056
|
+
},
|
|
1057
|
+
});
|
|
1058
|
+
|
|
1059
|
+
const messages = await db.message.order('id').pluck('text');
|
|
1060
|
+
expect(messages).toEqual(['updated', 'message 2', 'updated']);
|
|
1061
|
+
});
|
|
1062
|
+
|
|
1063
|
+
it('should ignore empty update where list', async () => {
|
|
1064
|
+
const chatId = await db.chat.get('id').create(chatData);
|
|
1065
|
+
|
|
1066
|
+
const id = await db.user.get('id').create({
|
|
1067
|
+
...userData,
|
|
1068
|
+
messages: {
|
|
1069
|
+
create: [{ ...messageData, chatId, text: 'message 1' }],
|
|
1070
|
+
},
|
|
1071
|
+
});
|
|
1072
|
+
|
|
1073
|
+
await db.user.find(id).update({
|
|
1074
|
+
messages: {
|
|
1075
|
+
update: {
|
|
1076
|
+
where: [],
|
|
1077
|
+
data: {
|
|
1078
|
+
text: 'updated',
|
|
1079
|
+
},
|
|
1080
|
+
},
|
|
1081
|
+
},
|
|
1082
|
+
});
|
|
1083
|
+
|
|
1084
|
+
const messages = await db.user.messages({ id }).pluck('text');
|
|
1085
|
+
expect(messages).toEqual(['message 1']);
|
|
1086
|
+
});
|
|
1087
|
+
});
|
|
1088
|
+
|
|
1089
|
+
describe('nested create', () => {
|
|
1090
|
+
it('should create new related records', async () => {
|
|
1091
|
+
const chatId = await db.chat.get('id').create(chatData);
|
|
1092
|
+
const user = await db.user.create({ ...userData, age: 1 });
|
|
1093
|
+
|
|
1094
|
+
const updated = await db.user
|
|
1095
|
+
.select('age')
|
|
1096
|
+
.find(user.id)
|
|
1097
|
+
.increment('age')
|
|
1098
|
+
.update({
|
|
1099
|
+
messages: {
|
|
1100
|
+
create: [
|
|
1101
|
+
{ ...messageData, chatId, text: 'created 1' },
|
|
1102
|
+
{ ...messageData, chatId, text: 'created 2' },
|
|
1103
|
+
],
|
|
1104
|
+
},
|
|
1105
|
+
});
|
|
1106
|
+
|
|
1107
|
+
expect(updated.age).toBe(2);
|
|
1108
|
+
|
|
1109
|
+
const texts = await db.user.messages(user).order('text').pluck('text');
|
|
1110
|
+
expect(texts).toEqual(['created 1', 'created 2']);
|
|
1111
|
+
});
|
|
1112
|
+
|
|
1113
|
+
it('should throw in batch update', async () => {
|
|
1114
|
+
const query = db.user.where({ id: { in: [1, 2, 3] } }).update({
|
|
1115
|
+
messages: {
|
|
1116
|
+
// @ts-expect-error not allows in batch update
|
|
1117
|
+
create: [{ ...messageData, chatId: 1, text: 'created 1' }],
|
|
1118
|
+
},
|
|
1119
|
+
});
|
|
1120
|
+
|
|
1121
|
+
await expect(query).rejects.toThrow();
|
|
1122
|
+
});
|
|
1123
|
+
|
|
1124
|
+
it('should ignore empty create list', async () => {
|
|
1125
|
+
const id = await db.user.get('id').create(userData);
|
|
1126
|
+
|
|
1127
|
+
await db.user.find(id).update({
|
|
1128
|
+
messages: {
|
|
1129
|
+
create: [],
|
|
1130
|
+
},
|
|
1131
|
+
});
|
|
1132
|
+
|
|
1133
|
+
const messages = await db.user.messages({ id });
|
|
1134
|
+
expect(messages.length).toEqual(0);
|
|
1135
|
+
});
|
|
1136
|
+
});
|
|
1137
|
+
});
|
|
1138
|
+
});
|
|
1139
|
+
|
|
1140
|
+
describe('hasMany through', () => {
|
|
1141
|
+
it('should resolve recursive situation when both models depends on each other', () => {
|
|
1142
|
+
class Post extends Model {
|
|
1143
|
+
table = 'post';
|
|
1144
|
+
columns = this.setColumns((t) => ({
|
|
1145
|
+
id: t.serial().primaryKey(),
|
|
1146
|
+
}));
|
|
1147
|
+
|
|
1148
|
+
relations = {
|
|
1149
|
+
postTags: this.hasMany(() => PostTag, {
|
|
1150
|
+
primaryKey: 'id',
|
|
1151
|
+
foreignKey: 'postId',
|
|
1152
|
+
}),
|
|
1153
|
+
|
|
1154
|
+
tags: this.hasMany(() => Tag, {
|
|
1155
|
+
through: 'postTags',
|
|
1156
|
+
source: 'tag',
|
|
1157
|
+
}),
|
|
1158
|
+
};
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1161
|
+
class Tag extends Model {
|
|
1162
|
+
table = 'tag';
|
|
1163
|
+
columns = this.setColumns((t) => ({
|
|
1164
|
+
id: t.serial().primaryKey(),
|
|
1165
|
+
}));
|
|
1166
|
+
|
|
1167
|
+
relations = {
|
|
1168
|
+
postTags: this.hasMany(() => PostTag, {
|
|
1169
|
+
primaryKey: 'id',
|
|
1170
|
+
foreignKey: 'postId',
|
|
1171
|
+
}),
|
|
1172
|
+
|
|
1173
|
+
posts: this.hasMany(() => Post, {
|
|
1174
|
+
through: 'postTags',
|
|
1175
|
+
source: 'post',
|
|
1176
|
+
}),
|
|
1177
|
+
};
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1180
|
+
class PostTag extends Model {
|
|
1181
|
+
table = 'postTag';
|
|
1182
|
+
columns = this.setColumns((t) => ({
|
|
1183
|
+
postId: t.integer().foreignKey(() => Post, 'id'),
|
|
1184
|
+
tagId: t.integer().foreignKey(() => Tag, 'id'),
|
|
1185
|
+
}));
|
|
1186
|
+
|
|
1187
|
+
relations = {
|
|
1188
|
+
post: this.belongsTo(() => Post, {
|
|
1189
|
+
primaryKey: 'id',
|
|
1190
|
+
foreignKey: 'postId',
|
|
1191
|
+
}),
|
|
1192
|
+
|
|
1193
|
+
tag: this.belongsTo(() => Tag, {
|
|
1194
|
+
primaryKey: 'id',
|
|
1195
|
+
foreignKey: 'tagId',
|
|
1196
|
+
}),
|
|
1197
|
+
};
|
|
1198
|
+
}
|
|
1199
|
+
|
|
1200
|
+
const db = orchidORM(
|
|
1201
|
+
{
|
|
1202
|
+
...pgConfig,
|
|
1203
|
+
log: false,
|
|
1204
|
+
},
|
|
1205
|
+
{
|
|
1206
|
+
post: Post,
|
|
1207
|
+
tag: Tag,
|
|
1208
|
+
postTag: PostTag,
|
|
1209
|
+
},
|
|
1210
|
+
);
|
|
1211
|
+
|
|
1212
|
+
expect(Object.keys(db.post.relations)).toEqual(['postTags', 'tags']);
|
|
1213
|
+
expect(Object.keys(db.tag.relations)).toEqual(['postTags', 'posts']);
|
|
1214
|
+
});
|
|
1215
|
+
|
|
1216
|
+
describe('through hasMany', () => {
|
|
1217
|
+
it('should have method to query related data', async () => {
|
|
1218
|
+
const chatsQuery = db.chat.all();
|
|
1219
|
+
|
|
1220
|
+
assertType<
|
|
1221
|
+
typeof db.profile.chats,
|
|
1222
|
+
RelationQuery<
|
|
1223
|
+
'chats',
|
|
1224
|
+
{ userId: number | null },
|
|
1225
|
+
never,
|
|
1226
|
+
typeof chatsQuery,
|
|
1227
|
+
false,
|
|
1228
|
+
false,
|
|
1229
|
+
true
|
|
1230
|
+
>
|
|
1231
|
+
>();
|
|
1232
|
+
|
|
1233
|
+
const query = db.profile.chats({ userId: 1 });
|
|
1234
|
+
expectSql(
|
|
1235
|
+
query.toSql(),
|
|
1236
|
+
`
|
|
1237
|
+
SELECT * FROM "chat" AS "chats"
|
|
1238
|
+
WHERE EXISTS (
|
|
1239
|
+
SELECT 1 FROM "user"
|
|
1240
|
+
WHERE EXISTS (
|
|
1241
|
+
SELECT 1 FROM "chatUser"
|
|
1242
|
+
WHERE "chatUser"."chatId" = "chats"."id"
|
|
1243
|
+
AND "chatUser"."userId" = "user"."id"
|
|
1244
|
+
LIMIT 1
|
|
1245
|
+
)
|
|
1246
|
+
AND "user"."id" = $1
|
|
1247
|
+
LIMIT 1
|
|
1248
|
+
)
|
|
1249
|
+
`,
|
|
1250
|
+
[1],
|
|
1251
|
+
);
|
|
1252
|
+
});
|
|
1253
|
+
|
|
1254
|
+
it('should handle chained query', () => {
|
|
1255
|
+
const query = db.profile
|
|
1256
|
+
.where({ bio: 'bio' })
|
|
1257
|
+
.chats.where({ title: 'title' });
|
|
1258
|
+
|
|
1259
|
+
expectSql(
|
|
1260
|
+
query.toSql(),
|
|
1261
|
+
`
|
|
1262
|
+
SELECT * FROM "chat" AS "chats"
|
|
1263
|
+
WHERE EXISTS (
|
|
1264
|
+
SELECT 1 FROM "profile"
|
|
1265
|
+
WHERE "profile"."bio" = $1
|
|
1266
|
+
AND EXISTS (
|
|
1267
|
+
SELECT 1 FROM "user"
|
|
1268
|
+
WHERE EXISTS (
|
|
1269
|
+
SELECT 1 FROM "chatUser"
|
|
1270
|
+
WHERE "chatUser"."chatId" = "chats"."id"
|
|
1271
|
+
AND "chatUser"."userId" = "user"."id"
|
|
1272
|
+
LIMIT 1
|
|
1273
|
+
)
|
|
1274
|
+
AND "user"."id" = "profile"."userId"
|
|
1275
|
+
LIMIT 1
|
|
1276
|
+
)
|
|
1277
|
+
LIMIT 1
|
|
1278
|
+
)
|
|
1279
|
+
AND "chats"."title" = $2
|
|
1280
|
+
`,
|
|
1281
|
+
['bio', 'title'],
|
|
1282
|
+
);
|
|
1283
|
+
});
|
|
1284
|
+
|
|
1285
|
+
it('should have disabled create method', () => {
|
|
1286
|
+
// @ts-expect-error hasMany with through option should not have chained create
|
|
1287
|
+
db.profile.chats.create(chatData);
|
|
1288
|
+
});
|
|
1289
|
+
|
|
1290
|
+
describe('chained delete', () => {
|
|
1291
|
+
it('should have chained delete', () => {
|
|
1292
|
+
const query = db.profile
|
|
1293
|
+
.where({ bio: 'bio' })
|
|
1294
|
+
.chats.where({ title: 'title' })
|
|
1295
|
+
.delete();
|
|
1296
|
+
|
|
1297
|
+
expectSql(
|
|
1298
|
+
query.toSql(),
|
|
1299
|
+
`
|
|
1300
|
+
DELETE FROM "chat" AS "chats"
|
|
1301
|
+
WHERE EXISTS (
|
|
1302
|
+
SELECT 1 FROM "profile"
|
|
1303
|
+
WHERE "profile"."bio" = $1
|
|
1304
|
+
AND EXISTS (
|
|
1305
|
+
SELECT 1 FROM "user"
|
|
1306
|
+
WHERE EXISTS (
|
|
1307
|
+
SELECT 1 FROM "chatUser"
|
|
1308
|
+
WHERE "chatUser"."chatId" = "chats"."id"
|
|
1309
|
+
AND "chatUser"."userId" = "user"."id"
|
|
1310
|
+
LIMIT 1
|
|
1311
|
+
)
|
|
1312
|
+
AND "user"."id" = "profile"."userId"
|
|
1313
|
+
LIMIT 1
|
|
1314
|
+
)
|
|
1315
|
+
LIMIT 1
|
|
1316
|
+
)
|
|
1317
|
+
AND "chats"."title" = $2
|
|
1318
|
+
`,
|
|
1319
|
+
['bio', 'title'],
|
|
1320
|
+
);
|
|
1321
|
+
});
|
|
1322
|
+
});
|
|
1323
|
+
|
|
1324
|
+
it('should have proper joinQuery', () => {
|
|
1325
|
+
expectSql(
|
|
1326
|
+
db.profile.relations.chats
|
|
1327
|
+
.joinQuery(db.profile.as('p'), db.chat.as('c'))
|
|
1328
|
+
.toSql(),
|
|
1329
|
+
`
|
|
1330
|
+
SELECT * FROM "chat" AS "c"
|
|
1331
|
+
WHERE EXISTS (
|
|
1332
|
+
SELECT 1 FROM "user"
|
|
1333
|
+
WHERE EXISTS (
|
|
1334
|
+
SELECT 1 FROM "chatUser"
|
|
1335
|
+
WHERE "chatUser"."chatId" = "c"."id"
|
|
1336
|
+
AND "chatUser"."userId" = "user"."id"
|
|
1337
|
+
LIMIT 1
|
|
1338
|
+
)
|
|
1339
|
+
AND "user"."id" = "p"."userId"
|
|
1340
|
+
LIMIT 1
|
|
1341
|
+
)
|
|
1342
|
+
`,
|
|
1343
|
+
);
|
|
1344
|
+
});
|
|
1345
|
+
|
|
1346
|
+
it('should be supported in whereExists', () => {
|
|
1347
|
+
expectSql(
|
|
1348
|
+
db.profile.whereExists('chats').toSql(),
|
|
1349
|
+
`
|
|
1350
|
+
SELECT * FROM "profile"
|
|
1351
|
+
WHERE EXISTS (
|
|
1352
|
+
SELECT 1 FROM "chat" AS "chats"
|
|
1353
|
+
WHERE EXISTS (
|
|
1354
|
+
SELECT 1 FROM "user"
|
|
1355
|
+
WHERE EXISTS (
|
|
1356
|
+
SELECT 1 FROM "chatUser"
|
|
1357
|
+
WHERE "chatUser"."chatId" = "chats"."id"
|
|
1358
|
+
AND "chatUser"."userId" = "user"."id"
|
|
1359
|
+
LIMIT 1
|
|
1360
|
+
)
|
|
1361
|
+
AND "user"."id" = "profile"."userId"
|
|
1362
|
+
LIMIT 1
|
|
1363
|
+
)
|
|
1364
|
+
LIMIT 1
|
|
1365
|
+
)
|
|
1366
|
+
`,
|
|
1367
|
+
);
|
|
1368
|
+
|
|
1369
|
+
expectSql(
|
|
1370
|
+
db.profile
|
|
1371
|
+
.as('p')
|
|
1372
|
+
.whereExists('chats', (q) => q.where({ title: 'title' }))
|
|
1373
|
+
.toSql(),
|
|
1374
|
+
`
|
|
1375
|
+
SELECT * FROM "profile" AS "p"
|
|
1376
|
+
WHERE EXISTS (
|
|
1377
|
+
SELECT 1 FROM "chat" AS "chats"
|
|
1378
|
+
WHERE EXISTS (
|
|
1379
|
+
SELECT 1 FROM "user"
|
|
1380
|
+
WHERE EXISTS (
|
|
1381
|
+
SELECT 1 FROM "chatUser"
|
|
1382
|
+
WHERE "chatUser"."chatId" = "chats"."id"
|
|
1383
|
+
AND "chatUser"."userId" = "user"."id"
|
|
1384
|
+
LIMIT 1
|
|
1385
|
+
)
|
|
1386
|
+
AND "user"."id" = "p"."userId"
|
|
1387
|
+
LIMIT 1
|
|
1388
|
+
)
|
|
1389
|
+
AND "chats"."title" = $1
|
|
1390
|
+
LIMIT 1
|
|
1391
|
+
)
|
|
1392
|
+
`,
|
|
1393
|
+
['title'],
|
|
1394
|
+
);
|
|
1395
|
+
});
|
|
1396
|
+
|
|
1397
|
+
it('should be supported in join', () => {
|
|
1398
|
+
const query = db.profile
|
|
1399
|
+
.as('p')
|
|
1400
|
+
.join('chats', (q) => q.where({ title: 'title' }))
|
|
1401
|
+
.select('bio', 'chats.title');
|
|
1402
|
+
|
|
1403
|
+
assertType<
|
|
1404
|
+
Awaited<typeof query>,
|
|
1405
|
+
{ bio: string | null; title: string }[]
|
|
1406
|
+
>();
|
|
1407
|
+
|
|
1408
|
+
expectSql(
|
|
1409
|
+
query.toSql(),
|
|
1410
|
+
`
|
|
1411
|
+
SELECT "p"."bio", "chats"."title" FROM "profile" AS "p"
|
|
1412
|
+
JOIN "chat" AS "chats"
|
|
1413
|
+
ON EXISTS (
|
|
1414
|
+
SELECT 1 FROM "user"
|
|
1415
|
+
WHERE EXISTS (
|
|
1416
|
+
SELECT 1 FROM "chatUser"
|
|
1417
|
+
WHERE "chatUser"."chatId" = "chats"."id"
|
|
1418
|
+
AND "chatUser"."userId" = "user"."id"
|
|
1419
|
+
LIMIT 1
|
|
1420
|
+
)
|
|
1421
|
+
AND "user"."id" = "p"."userId"
|
|
1422
|
+
LIMIT 1
|
|
1423
|
+
)
|
|
1424
|
+
AND "chats"."title" = $1
|
|
1425
|
+
`,
|
|
1426
|
+
['title'],
|
|
1427
|
+
);
|
|
1428
|
+
});
|
|
1429
|
+
|
|
1430
|
+
describe('select', () => {
|
|
1431
|
+
it('should be selectable', () => {
|
|
1432
|
+
const query = db.profile.as('p').select('id', {
|
|
1433
|
+
chats: (q) => q.chats.where({ title: 'title' }),
|
|
1434
|
+
});
|
|
1435
|
+
|
|
1436
|
+
assertType<Awaited<typeof query>, { id: number; chats: Chat[] }[]>();
|
|
1437
|
+
|
|
1438
|
+
expectSql(
|
|
1439
|
+
query.toSql(),
|
|
1440
|
+
`
|
|
1441
|
+
SELECT
|
|
1442
|
+
"p"."id",
|
|
1443
|
+
(
|
|
1444
|
+
SELECT COALESCE(json_agg(row_to_json("t".*)), '[]')
|
|
1445
|
+
FROM (
|
|
1446
|
+
SELECT *
|
|
1447
|
+
FROM "chat" AS "chats"
|
|
1448
|
+
WHERE "chats"."title" = $1
|
|
1449
|
+
AND EXISTS (
|
|
1450
|
+
SELECT 1 FROM "user"
|
|
1451
|
+
WHERE EXISTS (
|
|
1452
|
+
SELECT 1 FROM "chatUser"
|
|
1453
|
+
WHERE "chatUser"."chatId" = "chats"."id"
|
|
1454
|
+
AND "chatUser"."userId" = "user"."id"
|
|
1455
|
+
LIMIT 1
|
|
1456
|
+
)
|
|
1457
|
+
AND "user"."id" = "p"."userId"
|
|
1458
|
+
LIMIT 1
|
|
1459
|
+
)
|
|
1460
|
+
) AS "t"
|
|
1461
|
+
) AS "chats"
|
|
1462
|
+
FROM "profile" AS "p"
|
|
1463
|
+
`,
|
|
1464
|
+
['title'],
|
|
1465
|
+
);
|
|
1466
|
+
});
|
|
1467
|
+
|
|
1468
|
+
it('should be selectable by relation name', () => {
|
|
1469
|
+
const query = db.profile.select('id', 'chats');
|
|
1470
|
+
|
|
1471
|
+
assertType<Awaited<typeof query>, { id: number; chats: Chat[] }[]>();
|
|
1472
|
+
|
|
1473
|
+
expectSql(
|
|
1474
|
+
query.toSql(),
|
|
1475
|
+
`
|
|
1476
|
+
SELECT
|
|
1477
|
+
"profile"."id",
|
|
1478
|
+
(
|
|
1479
|
+
SELECT COALESCE(json_agg(row_to_json("t".*)), '[]')
|
|
1480
|
+
FROM (
|
|
1481
|
+
SELECT *
|
|
1482
|
+
FROM "chat" AS "chats"
|
|
1483
|
+
WHERE EXISTS (
|
|
1484
|
+
SELECT 1 FROM "user"
|
|
1485
|
+
WHERE EXISTS (
|
|
1486
|
+
SELECT 1 FROM "chatUser"
|
|
1487
|
+
WHERE "chatUser"."chatId" = "chats"."id"
|
|
1488
|
+
AND "chatUser"."userId" = "user"."id"
|
|
1489
|
+
LIMIT 1
|
|
1490
|
+
)
|
|
1491
|
+
AND "user"."id" = "profile"."userId"
|
|
1492
|
+
LIMIT 1
|
|
1493
|
+
)
|
|
1494
|
+
) AS "t"
|
|
1495
|
+
) AS "chats"
|
|
1496
|
+
FROM "profile"
|
|
1497
|
+
`,
|
|
1498
|
+
[],
|
|
1499
|
+
);
|
|
1500
|
+
});
|
|
1501
|
+
});
|
|
1502
|
+
|
|
1503
|
+
it('should allow to select count', () => {
|
|
1504
|
+
const query = db.profile.as('p').select('id', {
|
|
1505
|
+
chatsCount: (q) => q.chats.count(),
|
|
1506
|
+
});
|
|
1507
|
+
|
|
1508
|
+
assertType<Awaited<typeof query>, { id: number; chatsCount: number }[]>();
|
|
1509
|
+
|
|
1510
|
+
expectSql(
|
|
1511
|
+
query.toSql(),
|
|
1512
|
+
`
|
|
1513
|
+
SELECT
|
|
1514
|
+
"p"."id",
|
|
1515
|
+
(
|
|
1516
|
+
SELECT count(*)
|
|
1517
|
+
FROM "chat" AS "chats"
|
|
1518
|
+
WHERE EXISTS (
|
|
1519
|
+
SELECT 1 FROM "user"
|
|
1520
|
+
WHERE EXISTS (
|
|
1521
|
+
SELECT 1 FROM "chatUser"
|
|
1522
|
+
WHERE "chatUser"."chatId" = "chats"."id"
|
|
1523
|
+
AND "chatUser"."userId" = "user"."id"
|
|
1524
|
+
LIMIT 1
|
|
1525
|
+
)
|
|
1526
|
+
AND "user"."id" = "p"."userId"
|
|
1527
|
+
LIMIT 1
|
|
1528
|
+
)
|
|
1529
|
+
) AS "chatsCount"
|
|
1530
|
+
FROM "profile" AS "p"
|
|
1531
|
+
`,
|
|
1532
|
+
);
|
|
1533
|
+
});
|
|
1534
|
+
|
|
1535
|
+
it('should allow to pluck values', () => {
|
|
1536
|
+
const query = db.profile.as('p').select('id', {
|
|
1537
|
+
titles: (q) => q.chats.pluck('title'),
|
|
1538
|
+
});
|
|
1539
|
+
|
|
1540
|
+
assertType<Awaited<typeof query>, { id: number; titles: string[] }[]>();
|
|
1541
|
+
|
|
1542
|
+
expectSql(
|
|
1543
|
+
query.toSql(),
|
|
1544
|
+
`
|
|
1545
|
+
SELECT
|
|
1546
|
+
"p"."id",
|
|
1547
|
+
(
|
|
1548
|
+
SELECT COALESCE(json_agg("c"), '[]')
|
|
1549
|
+
FROM (
|
|
1550
|
+
SELECT "chats"."title" AS "c"
|
|
1551
|
+
FROM "chat" AS "chats"
|
|
1552
|
+
WHERE EXISTS (
|
|
1553
|
+
SELECT 1 FROM "user"
|
|
1554
|
+
WHERE EXISTS (
|
|
1555
|
+
SELECT 1 FROM "chatUser"
|
|
1556
|
+
WHERE "chatUser"."chatId" = "chats"."id"
|
|
1557
|
+
AND "chatUser"."userId" = "user"."id"
|
|
1558
|
+
LIMIT 1
|
|
1559
|
+
)
|
|
1560
|
+
AND "user"."id" = "p"."userId"
|
|
1561
|
+
LIMIT 1
|
|
1562
|
+
)
|
|
1563
|
+
) AS "t"
|
|
1564
|
+
) AS "titles"
|
|
1565
|
+
FROM "profile" AS "p"
|
|
1566
|
+
`,
|
|
1567
|
+
);
|
|
1568
|
+
});
|
|
1569
|
+
|
|
1570
|
+
it('should handle exists sub query', () => {
|
|
1571
|
+
const query = db.profile.as('p').select('id', {
|
|
1572
|
+
hasChats: (q) => q.chats.exists(),
|
|
1573
|
+
});
|
|
1574
|
+
|
|
1575
|
+
assertType<Awaited<typeof query>, { id: number; hasChats: boolean }[]>();
|
|
1576
|
+
|
|
1577
|
+
expectSql(
|
|
1578
|
+
query.toSql(),
|
|
1579
|
+
`
|
|
1580
|
+
SELECT
|
|
1581
|
+
"p"."id",
|
|
1582
|
+
COALESCE((
|
|
1583
|
+
SELECT true
|
|
1584
|
+
FROM "chat" AS "chats"
|
|
1585
|
+
WHERE EXISTS (
|
|
1586
|
+
SELECT 1 FROM "user"
|
|
1587
|
+
WHERE EXISTS (
|
|
1588
|
+
SELECT 1 FROM "chatUser"
|
|
1589
|
+
WHERE "chatUser"."chatId" = "chats"."id"
|
|
1590
|
+
AND "chatUser"."userId" = "user"."id"
|
|
1591
|
+
LIMIT 1
|
|
1592
|
+
)
|
|
1593
|
+
AND "user"."id" = "p"."userId"
|
|
1594
|
+
LIMIT 1
|
|
1595
|
+
)
|
|
1596
|
+
), false) AS "hasChats"
|
|
1597
|
+
FROM "profile" AS "p"
|
|
1598
|
+
`,
|
|
1599
|
+
);
|
|
1600
|
+
});
|
|
1601
|
+
});
|
|
1602
|
+
|
|
1603
|
+
describe('through hasOne', () => {
|
|
1604
|
+
it('should have method to query related data', () => {
|
|
1605
|
+
const profilesQuery = db.profile.all();
|
|
1606
|
+
|
|
1607
|
+
assertType<
|
|
1608
|
+
typeof db.chat.profiles,
|
|
1609
|
+
RelationQuery<
|
|
1610
|
+
'profiles',
|
|
1611
|
+
{ id: number },
|
|
1612
|
+
never,
|
|
1613
|
+
typeof profilesQuery,
|
|
1614
|
+
false,
|
|
1615
|
+
false,
|
|
1616
|
+
true
|
|
1617
|
+
>
|
|
1618
|
+
>();
|
|
1619
|
+
|
|
1620
|
+
const query = db.chat.profiles({ id: 1 });
|
|
1621
|
+
expectSql(
|
|
1622
|
+
query.toSql(),
|
|
1623
|
+
`
|
|
1624
|
+
SELECT * FROM "profile" AS "profiles"
|
|
1625
|
+
WHERE EXISTS (
|
|
1626
|
+
SELECT 1 FROM "user" AS "users"
|
|
1627
|
+
WHERE "profiles"."userId" = "users"."id"
|
|
1628
|
+
AND EXISTS (
|
|
1629
|
+
SELECT 1 FROM "chatUser"
|
|
1630
|
+
WHERE "chatUser"."userId" = "users"."id"
|
|
1631
|
+
AND "chatUser"."chatId" = $1
|
|
1632
|
+
LIMIT 1
|
|
1633
|
+
)
|
|
1634
|
+
LIMIT 1
|
|
1635
|
+
)
|
|
1636
|
+
`,
|
|
1637
|
+
[1],
|
|
1638
|
+
);
|
|
1639
|
+
});
|
|
1640
|
+
|
|
1641
|
+
it('should handle chained query', () => {
|
|
1642
|
+
const query = db.chat
|
|
1643
|
+
.where({ title: 'title' })
|
|
1644
|
+
.profiles.where({ bio: 'bio' });
|
|
1645
|
+
|
|
1646
|
+
expectSql(
|
|
1647
|
+
query.toSql(),
|
|
1648
|
+
`
|
|
1649
|
+
SELECT * FROM "profile" AS "profiles"
|
|
1650
|
+
WHERE EXISTS (
|
|
1651
|
+
SELECT 1 FROM "chat"
|
|
1652
|
+
WHERE "chat"."title" = $1
|
|
1653
|
+
AND EXISTS (
|
|
1654
|
+
SELECT 1 FROM "user" AS "users"
|
|
1655
|
+
WHERE "profiles"."userId" = "users"."id"
|
|
1656
|
+
AND EXISTS (
|
|
1657
|
+
SELECT 1 FROM "chatUser"
|
|
1658
|
+
WHERE "chatUser"."userId" = "users"."id"
|
|
1659
|
+
AND "chatUser"."chatId" = "chat"."id"
|
|
1660
|
+
LIMIT 1
|
|
1661
|
+
)
|
|
1662
|
+
LIMIT 1
|
|
1663
|
+
)
|
|
1664
|
+
LIMIT 1
|
|
1665
|
+
)
|
|
1666
|
+
AND "profiles"."bio" = $2
|
|
1667
|
+
`,
|
|
1668
|
+
['title', 'bio'],
|
|
1669
|
+
);
|
|
1670
|
+
});
|
|
1671
|
+
|
|
1672
|
+
it('should have disabled create method', () => {
|
|
1673
|
+
// @ts-expect-error hasMany with through option should not have chained create
|
|
1674
|
+
db.chat.profiles.create(chatData);
|
|
1675
|
+
});
|
|
1676
|
+
|
|
1677
|
+
it('should have chained delete', () => {
|
|
1678
|
+
const query = db.chat
|
|
1679
|
+
.where({ title: 'title' })
|
|
1680
|
+
.profiles.where({ bio: 'bio' })
|
|
1681
|
+
.delete();
|
|
1682
|
+
|
|
1683
|
+
expectSql(
|
|
1684
|
+
query.toSql(),
|
|
1685
|
+
`
|
|
1686
|
+
DELETE FROM "profile" AS "profiles"
|
|
1687
|
+
WHERE EXISTS (
|
|
1688
|
+
SELECT 1 FROM "chat"
|
|
1689
|
+
WHERE "chat"."title" = $1
|
|
1690
|
+
AND EXISTS (
|
|
1691
|
+
SELECT 1 FROM "user" AS "users"
|
|
1692
|
+
WHERE "profiles"."userId" = "users"."id"
|
|
1693
|
+
AND EXISTS (
|
|
1694
|
+
SELECT 1 FROM "chatUser"
|
|
1695
|
+
WHERE "chatUser"."userId" = "users"."id"
|
|
1696
|
+
AND "chatUser"."chatId" = "chat"."id"
|
|
1697
|
+
LIMIT 1
|
|
1698
|
+
)
|
|
1699
|
+
LIMIT 1
|
|
1700
|
+
)
|
|
1701
|
+
LIMIT 1
|
|
1702
|
+
)
|
|
1703
|
+
AND "profiles"."bio" = $2
|
|
1704
|
+
`,
|
|
1705
|
+
['title', 'bio'],
|
|
1706
|
+
);
|
|
1707
|
+
});
|
|
1708
|
+
|
|
1709
|
+
it('should have proper joinQuery', () => {
|
|
1710
|
+
expectSql(
|
|
1711
|
+
db.chat.relations.profiles
|
|
1712
|
+
.joinQuery(db.chat.as('c'), db.profile.as('p'))
|
|
1713
|
+
.toSql(),
|
|
1714
|
+
`
|
|
1715
|
+
SELECT * FROM "profile" AS "p"
|
|
1716
|
+
WHERE EXISTS (
|
|
1717
|
+
SELECT 1 FROM "user" AS "users"
|
|
1718
|
+
WHERE "p"."userId" = "users"."id"
|
|
1719
|
+
AND EXISTS (
|
|
1720
|
+
SELECT 1 FROM "chatUser"
|
|
1721
|
+
WHERE "chatUser"."userId" = "users"."id"
|
|
1722
|
+
AND "chatUser"."chatId" = "c"."id"
|
|
1723
|
+
LIMIT 1
|
|
1724
|
+
)
|
|
1725
|
+
LIMIT 1
|
|
1726
|
+
)
|
|
1727
|
+
`,
|
|
1728
|
+
);
|
|
1729
|
+
});
|
|
1730
|
+
|
|
1731
|
+
it('should be supported in whereExists', () => {
|
|
1732
|
+
expectSql(
|
|
1733
|
+
db.chat.whereExists('profiles').toSql(),
|
|
1734
|
+
`
|
|
1735
|
+
SELECT * FROM "chat"
|
|
1736
|
+
WHERE EXISTS (
|
|
1737
|
+
SELECT 1 FROM "profile" AS "profiles"
|
|
1738
|
+
WHERE EXISTS (
|
|
1739
|
+
SELECT 1 FROM "user" AS "users"
|
|
1740
|
+
WHERE "profiles"."userId" = "users"."id"
|
|
1741
|
+
AND EXISTS (
|
|
1742
|
+
SELECT 1 FROM "chatUser"
|
|
1743
|
+
WHERE "chatUser"."userId" = "users"."id"
|
|
1744
|
+
AND "chatUser"."chatId" = "chat"."id"
|
|
1745
|
+
LIMIT 1
|
|
1746
|
+
)
|
|
1747
|
+
LIMIT 1
|
|
1748
|
+
)
|
|
1749
|
+
LIMIT 1
|
|
1750
|
+
)
|
|
1751
|
+
`,
|
|
1752
|
+
);
|
|
1753
|
+
|
|
1754
|
+
expectSql(
|
|
1755
|
+
db.chat
|
|
1756
|
+
.as('c')
|
|
1757
|
+
.whereExists('profiles', (q) => q.where({ bio: 'bio' }))
|
|
1758
|
+
.toSql(),
|
|
1759
|
+
`
|
|
1760
|
+
SELECT * FROM "chat" AS "c"
|
|
1761
|
+
WHERE EXISTS (
|
|
1762
|
+
SELECT 1 FROM "profile" AS "profiles"
|
|
1763
|
+
WHERE EXISTS (
|
|
1764
|
+
SELECT 1 FROM "user" AS "users"
|
|
1765
|
+
WHERE "profiles"."userId" = "users"."id"
|
|
1766
|
+
AND EXISTS (
|
|
1767
|
+
SELECT 1 FROM "chatUser"
|
|
1768
|
+
WHERE "chatUser"."userId" = "users"."id"
|
|
1769
|
+
AND "chatUser"."chatId" = "c"."id"
|
|
1770
|
+
LIMIT 1
|
|
1771
|
+
)
|
|
1772
|
+
LIMIT 1
|
|
1773
|
+
)
|
|
1774
|
+
AND "profiles"."bio" = $1
|
|
1775
|
+
LIMIT 1
|
|
1776
|
+
)
|
|
1777
|
+
`,
|
|
1778
|
+
['bio'],
|
|
1779
|
+
);
|
|
1780
|
+
});
|
|
1781
|
+
|
|
1782
|
+
it('should be supported in join', () => {
|
|
1783
|
+
const query = db.chat
|
|
1784
|
+
.as('c')
|
|
1785
|
+
.join('profiles', (q) => q.where({ bio: 'bio' }))
|
|
1786
|
+
.select('title', 'profiles.bio');
|
|
1787
|
+
|
|
1788
|
+
assertType<
|
|
1789
|
+
Awaited<typeof query>,
|
|
1790
|
+
{ title: string; bio: string | null }[]
|
|
1791
|
+
>();
|
|
1792
|
+
|
|
1793
|
+
expectSql(
|
|
1794
|
+
query.toSql(),
|
|
1795
|
+
`
|
|
1796
|
+
SELECT "c"."title", "profiles"."bio" FROM "chat" AS "c"
|
|
1797
|
+
JOIN "profile" AS "profiles"
|
|
1798
|
+
ON EXISTS (
|
|
1799
|
+
SELECT 1 FROM "user" AS "users"
|
|
1800
|
+
WHERE "profiles"."userId" = "users"."id"
|
|
1801
|
+
AND EXISTS (
|
|
1802
|
+
SELECT 1 FROM "chatUser"
|
|
1803
|
+
WHERE "chatUser"."userId" = "users"."id"
|
|
1804
|
+
AND "chatUser"."chatId" = "c"."id"
|
|
1805
|
+
LIMIT 1
|
|
1806
|
+
)
|
|
1807
|
+
LIMIT 1
|
|
1808
|
+
)
|
|
1809
|
+
AND "profiles"."bio" = $1
|
|
1810
|
+
`,
|
|
1811
|
+
['bio'],
|
|
1812
|
+
);
|
|
1813
|
+
});
|
|
1814
|
+
|
|
1815
|
+
describe('select', () => {
|
|
1816
|
+
it('should be selectable', () => {
|
|
1817
|
+
const query = db.chat.as('c').select('id', {
|
|
1818
|
+
profiles: (q) => q.profiles.where({ bio: 'bio' }),
|
|
1819
|
+
});
|
|
1820
|
+
|
|
1821
|
+
assertType<
|
|
1822
|
+
Awaited<typeof query>,
|
|
1823
|
+
{ id: number; profiles: Profile[] }[]
|
|
1824
|
+
>();
|
|
1825
|
+
|
|
1826
|
+
expectSql(
|
|
1827
|
+
query.toSql(),
|
|
1828
|
+
`
|
|
1829
|
+
SELECT
|
|
1830
|
+
"c"."id",
|
|
1831
|
+
(
|
|
1832
|
+
SELECT COALESCE(json_agg(row_to_json("t".*)), '[]')
|
|
1833
|
+
FROM (
|
|
1834
|
+
SELECT *
|
|
1835
|
+
FROM "profile" AS "profiles"
|
|
1836
|
+
WHERE "profiles"."bio" = $1
|
|
1837
|
+
AND EXISTS (
|
|
1838
|
+
SELECT 1 FROM "user" AS "users"
|
|
1839
|
+
WHERE "profiles"."userId" = "users"."id"
|
|
1840
|
+
AND EXISTS (
|
|
1841
|
+
SELECT 1 FROM "chatUser"
|
|
1842
|
+
WHERE "chatUser"."userId" = "users"."id"
|
|
1843
|
+
AND "chatUser"."chatId" = "c"."id"
|
|
1844
|
+
LIMIT 1
|
|
1845
|
+
)
|
|
1846
|
+
LIMIT 1
|
|
1847
|
+
)
|
|
1848
|
+
) AS "t"
|
|
1849
|
+
) AS "profiles"
|
|
1850
|
+
FROM "chat" AS "c"
|
|
1851
|
+
`,
|
|
1852
|
+
['bio'],
|
|
1853
|
+
);
|
|
1854
|
+
});
|
|
1855
|
+
|
|
1856
|
+
it('should be selectable by relation name', () => {
|
|
1857
|
+
const query = db.chat.select('id', 'profiles');
|
|
1858
|
+
|
|
1859
|
+
assertType<
|
|
1860
|
+
Awaited<typeof query>,
|
|
1861
|
+
{ id: number; profiles: Profile[] }[]
|
|
1862
|
+
>();
|
|
1863
|
+
|
|
1864
|
+
expectSql(
|
|
1865
|
+
query.toSql(),
|
|
1866
|
+
`
|
|
1867
|
+
SELECT
|
|
1868
|
+
"chat"."id",
|
|
1869
|
+
(
|
|
1870
|
+
SELECT COALESCE(json_agg(row_to_json("t".*)), '[]')
|
|
1871
|
+
FROM (
|
|
1872
|
+
SELECT *
|
|
1873
|
+
FROM "profile" AS "profiles"
|
|
1874
|
+
WHERE EXISTS (
|
|
1875
|
+
SELECT 1 FROM "user" AS "users"
|
|
1876
|
+
WHERE "profiles"."userId" = "users"."id"
|
|
1877
|
+
AND EXISTS (
|
|
1878
|
+
SELECT 1 FROM "chatUser"
|
|
1879
|
+
WHERE "chatUser"."userId" = "users"."id"
|
|
1880
|
+
AND "chatUser"."chatId" = "chat"."id"
|
|
1881
|
+
LIMIT 1
|
|
1882
|
+
)
|
|
1883
|
+
LIMIT 1
|
|
1884
|
+
)
|
|
1885
|
+
) AS "t"
|
|
1886
|
+
) AS "profiles"
|
|
1887
|
+
FROM "chat"
|
|
1888
|
+
`,
|
|
1889
|
+
[],
|
|
1890
|
+
);
|
|
1891
|
+
});
|
|
1892
|
+
|
|
1893
|
+
it('should allow to select count', () => {
|
|
1894
|
+
const query = db.chat.as('c').select('id', {
|
|
1895
|
+
profilesCount: (q) => q.profiles.count(),
|
|
1896
|
+
});
|
|
1897
|
+
|
|
1898
|
+
assertType<
|
|
1899
|
+
Awaited<typeof query>,
|
|
1900
|
+
{ id: number; profilesCount: number }[]
|
|
1901
|
+
>();
|
|
1902
|
+
|
|
1903
|
+
expectSql(
|
|
1904
|
+
query.toSql(),
|
|
1905
|
+
`
|
|
1906
|
+
SELECT
|
|
1907
|
+
"c"."id",
|
|
1908
|
+
(
|
|
1909
|
+
SELECT count(*)
|
|
1910
|
+
FROM "profile" AS "profiles"
|
|
1911
|
+
WHERE EXISTS (
|
|
1912
|
+
SELECT 1 FROM "user" AS "users"
|
|
1913
|
+
WHERE "profiles"."userId" = "users"."id"
|
|
1914
|
+
AND EXISTS (
|
|
1915
|
+
SELECT 1 FROM "chatUser"
|
|
1916
|
+
WHERE "chatUser"."userId" = "users"."id"
|
|
1917
|
+
AND "chatUser"."chatId" = "c"."id"
|
|
1918
|
+
LIMIT 1
|
|
1919
|
+
)
|
|
1920
|
+
LIMIT 1
|
|
1921
|
+
)
|
|
1922
|
+
) AS "profilesCount"
|
|
1923
|
+
FROM "chat" AS "c"
|
|
1924
|
+
`,
|
|
1925
|
+
[],
|
|
1926
|
+
);
|
|
1927
|
+
});
|
|
1928
|
+
|
|
1929
|
+
it('should allow to pluck values', () => {
|
|
1930
|
+
const query = db.chat.as('c').select('id', {
|
|
1931
|
+
bios: (q) => q.profiles.pluck('bio'),
|
|
1932
|
+
});
|
|
1933
|
+
|
|
1934
|
+
assertType<
|
|
1935
|
+
Awaited<typeof query>,
|
|
1936
|
+
{ id: number; bios: (string | null)[] }[]
|
|
1937
|
+
>();
|
|
1938
|
+
|
|
1939
|
+
expectSql(
|
|
1940
|
+
query.toSql(),
|
|
1941
|
+
`
|
|
1942
|
+
SELECT
|
|
1943
|
+
"c"."id",
|
|
1944
|
+
(
|
|
1945
|
+
SELECT COALESCE(json_agg("c"), '[]')
|
|
1946
|
+
FROM (
|
|
1947
|
+
SELECT "profiles"."bio" AS "c"
|
|
1948
|
+
FROM "profile" AS "profiles"
|
|
1949
|
+
WHERE EXISTS (
|
|
1950
|
+
SELECT 1 FROM "user" AS "users"
|
|
1951
|
+
WHERE "profiles"."userId" = "users"."id"
|
|
1952
|
+
AND EXISTS (
|
|
1953
|
+
SELECT 1 FROM "chatUser"
|
|
1954
|
+
WHERE "chatUser"."userId" = "users"."id"
|
|
1955
|
+
AND "chatUser"."chatId" = "c"."id"
|
|
1956
|
+
LIMIT 1
|
|
1957
|
+
)
|
|
1958
|
+
LIMIT 1
|
|
1959
|
+
)
|
|
1960
|
+
) AS "t"
|
|
1961
|
+
) AS "bios"
|
|
1962
|
+
FROM "chat" AS "c"
|
|
1963
|
+
`,
|
|
1964
|
+
);
|
|
1965
|
+
});
|
|
1966
|
+
|
|
1967
|
+
it('should handle exists sub query', () => {
|
|
1968
|
+
const query = db.chat.as('c').select('id', {
|
|
1969
|
+
hasProfiles: (q) => q.profiles.exists(),
|
|
1970
|
+
});
|
|
1971
|
+
|
|
1972
|
+
assertType<
|
|
1973
|
+
Awaited<typeof query>,
|
|
1974
|
+
{ id: number; hasProfiles: boolean }[]
|
|
1975
|
+
>();
|
|
1976
|
+
|
|
1977
|
+
expectSql(
|
|
1978
|
+
query.toSql(),
|
|
1979
|
+
`
|
|
1980
|
+
SELECT
|
|
1981
|
+
"c"."id",
|
|
1982
|
+
COALESCE((
|
|
1983
|
+
SELECT true
|
|
1984
|
+
FROM "profile" AS "profiles"
|
|
1985
|
+
WHERE EXISTS (
|
|
1986
|
+
SELECT 1 FROM "user" AS "users"
|
|
1987
|
+
WHERE "profiles"."userId" = "users"."id"
|
|
1988
|
+
AND EXISTS (
|
|
1989
|
+
SELECT 1 FROM "chatUser"
|
|
1990
|
+
WHERE "chatUser"."userId" = "users"."id"
|
|
1991
|
+
AND "chatUser"."chatId" = "c"."id"
|
|
1992
|
+
LIMIT 1
|
|
1993
|
+
)
|
|
1994
|
+
LIMIT 1
|
|
1995
|
+
)
|
|
1996
|
+
), false) AS "hasProfiles"
|
|
1997
|
+
FROM "chat" AS "c"
|
|
1998
|
+
`,
|
|
1999
|
+
);
|
|
2000
|
+
});
|
|
2001
|
+
});
|
|
2002
|
+
});
|
|
2003
|
+
});
|