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,1219 @@
|
|
|
1
|
+
import { db, pgConfig } from '../test-utils/test-db';
|
|
2
|
+
import {
|
|
3
|
+
assertType,
|
|
4
|
+
chatData,
|
|
5
|
+
expectSql,
|
|
6
|
+
profileData,
|
|
7
|
+
userData,
|
|
8
|
+
useTestDatabase,
|
|
9
|
+
} from '../test-utils/test-utils';
|
|
10
|
+
import { User, Profile, Model } from '../test-utils/test-models';
|
|
11
|
+
import { RelationQuery } from 'pqb';
|
|
12
|
+
import { orchidORM } from '../orm';
|
|
13
|
+
|
|
14
|
+
describe('hasOne', () => {
|
|
15
|
+
useTestDatabase();
|
|
16
|
+
|
|
17
|
+
describe('querying', () => {
|
|
18
|
+
it('should have method to query related data', async () => {
|
|
19
|
+
const profileQuery = db.profile.take();
|
|
20
|
+
|
|
21
|
+
assertType<
|
|
22
|
+
typeof db.user.profile,
|
|
23
|
+
RelationQuery<
|
|
24
|
+
'profile',
|
|
25
|
+
{ id: number },
|
|
26
|
+
'userId',
|
|
27
|
+
typeof profileQuery,
|
|
28
|
+
true,
|
|
29
|
+
true,
|
|
30
|
+
true
|
|
31
|
+
>
|
|
32
|
+
>();
|
|
33
|
+
|
|
34
|
+
const userId = await db.user.get('id').create(userData);
|
|
35
|
+
|
|
36
|
+
await db.profile.create({ ...profileData, userId });
|
|
37
|
+
|
|
38
|
+
const user = await db.user.find(userId);
|
|
39
|
+
const query = db.user.profile(user);
|
|
40
|
+
|
|
41
|
+
expectSql(
|
|
42
|
+
query.toSql(),
|
|
43
|
+
`
|
|
44
|
+
SELECT * FROM "profile"
|
|
45
|
+
WHERE "profile"."userId" = $1
|
|
46
|
+
LIMIT $2
|
|
47
|
+
`,
|
|
48
|
+
[userId, 1],
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
const profile = await query;
|
|
52
|
+
|
|
53
|
+
expect(profile).toMatchObject(profileData);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('should handle chained query', () => {
|
|
57
|
+
const query = db.user
|
|
58
|
+
.where({ name: 'name' })
|
|
59
|
+
.profile.where({ bio: 'bio' });
|
|
60
|
+
|
|
61
|
+
expectSql(
|
|
62
|
+
query.toSql(),
|
|
63
|
+
`
|
|
64
|
+
SELECT * FROM "profile"
|
|
65
|
+
WHERE EXISTS (
|
|
66
|
+
SELECT 1 FROM "user"
|
|
67
|
+
WHERE "user"."name" = $1
|
|
68
|
+
AND "user"."id" = "profile"."userId"
|
|
69
|
+
LIMIT 1
|
|
70
|
+
)
|
|
71
|
+
AND "profile"."bio" = $2
|
|
72
|
+
LIMIT $3
|
|
73
|
+
`,
|
|
74
|
+
['name', 'bio', 1],
|
|
75
|
+
);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('should have create with defaults of provided id', () => {
|
|
79
|
+
const user = { id: 1 };
|
|
80
|
+
const now = new Date();
|
|
81
|
+
|
|
82
|
+
const query = db.user.profile(user).count().create({
|
|
83
|
+
bio: 'bio',
|
|
84
|
+
updatedAt: now,
|
|
85
|
+
createdAt: now,
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
expectSql(
|
|
89
|
+
query.toSql(),
|
|
90
|
+
`
|
|
91
|
+
INSERT INTO "profile"("userId", "bio", "updatedAt", "createdAt")
|
|
92
|
+
VALUES ($1, $2, $3, $4)
|
|
93
|
+
`,
|
|
94
|
+
[1, 'bio', now, now],
|
|
95
|
+
);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it('can create after calling method', async () => {
|
|
99
|
+
const id = await db.user.get('id').create(userData);
|
|
100
|
+
const now = new Date();
|
|
101
|
+
await db.user.profile({ id }).create({
|
|
102
|
+
userId: id,
|
|
103
|
+
bio: 'bio',
|
|
104
|
+
updatedAt: now,
|
|
105
|
+
createdAt: now,
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
describe('chained create', () => {
|
|
110
|
+
it('should have create based on find query', () => {
|
|
111
|
+
const query = db.user.find(1).profile.create({
|
|
112
|
+
bio: 'bio',
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
expectSql(
|
|
116
|
+
query.toSql(),
|
|
117
|
+
`
|
|
118
|
+
INSERT INTO "profile"("userId", "bio")
|
|
119
|
+
SELECT "user"."id" AS "userId", $1
|
|
120
|
+
FROM "user"
|
|
121
|
+
WHERE "user"."id" = $2
|
|
122
|
+
LIMIT $3
|
|
123
|
+
RETURNING *
|
|
124
|
+
`,
|
|
125
|
+
['bio', 1, 1],
|
|
126
|
+
);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it('should throw when the main query returns many records', async () => {
|
|
130
|
+
await expect(
|
|
131
|
+
async () =>
|
|
132
|
+
await db.user.profile.create({
|
|
133
|
+
bio: 'bio',
|
|
134
|
+
}),
|
|
135
|
+
).rejects.toThrow(
|
|
136
|
+
'Cannot create based on a query which returns multiple records',
|
|
137
|
+
);
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it('should throw when main record is not found', async () => {
|
|
141
|
+
await expect(
|
|
142
|
+
async () =>
|
|
143
|
+
await db.user.find(1).profile.create({
|
|
144
|
+
bio: 'bio',
|
|
145
|
+
}),
|
|
146
|
+
).rejects.toThrow('Record is not found');
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it('should not throw when searching with findOptional', async () => {
|
|
150
|
+
await db.user.findOptional(1).profile.takeOptional().create({
|
|
151
|
+
bio: 'bio',
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
describe('chained delete', () => {
|
|
157
|
+
it('should delete relation records', () => {
|
|
158
|
+
const query = db.user
|
|
159
|
+
.where({ name: 'name' })
|
|
160
|
+
.profile.where({ bio: 'bio' })
|
|
161
|
+
.delete();
|
|
162
|
+
|
|
163
|
+
expectSql(
|
|
164
|
+
query.toSql(),
|
|
165
|
+
`
|
|
166
|
+
DELETE FROM "profile"
|
|
167
|
+
WHERE EXISTS (
|
|
168
|
+
SELECT 1 FROM "user"
|
|
169
|
+
WHERE "user"."name" = $1
|
|
170
|
+
AND "user"."id" = "profile"."userId"
|
|
171
|
+
LIMIT 1
|
|
172
|
+
)
|
|
173
|
+
AND "profile"."bio" = $2
|
|
174
|
+
`,
|
|
175
|
+
['name', 'bio'],
|
|
176
|
+
);
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
it('should have proper joinQuery', () => {
|
|
181
|
+
expectSql(
|
|
182
|
+
db.user.relations.profile
|
|
183
|
+
.joinQuery(db.user.as('u'), db.profile.as('p'))
|
|
184
|
+
.toSql(),
|
|
185
|
+
`
|
|
186
|
+
SELECT * FROM "profile" AS "p"
|
|
187
|
+
WHERE "p"."userId" = "u"."id"
|
|
188
|
+
`,
|
|
189
|
+
);
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
it('should be supported in whereExists', () => {
|
|
193
|
+
expectSql(
|
|
194
|
+
db.user.as('u').whereExists('profile').toSql(),
|
|
195
|
+
`
|
|
196
|
+
SELECT * FROM "user" AS "u"
|
|
197
|
+
WHERE EXISTS (
|
|
198
|
+
SELECT 1 FROM "profile"
|
|
199
|
+
WHERE "profile"."userId" = "u"."id"
|
|
200
|
+
LIMIT 1
|
|
201
|
+
)
|
|
202
|
+
`,
|
|
203
|
+
);
|
|
204
|
+
|
|
205
|
+
expectSql(
|
|
206
|
+
db.user
|
|
207
|
+
.as('u')
|
|
208
|
+
.whereExists('profile', (q) => q.where({ bio: 'bio' }))
|
|
209
|
+
.toSql(),
|
|
210
|
+
`
|
|
211
|
+
SELECT * FROM "user" AS "u"
|
|
212
|
+
WHERE EXISTS (
|
|
213
|
+
SELECT 1 FROM "profile"
|
|
214
|
+
WHERE "profile"."userId" = "u"."id"
|
|
215
|
+
AND "profile"."bio" = $1
|
|
216
|
+
LIMIT 1
|
|
217
|
+
)
|
|
218
|
+
`,
|
|
219
|
+
['bio'],
|
|
220
|
+
);
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
it('should be supported in join', () => {
|
|
224
|
+
const query = db.user
|
|
225
|
+
.as('u')
|
|
226
|
+
.join('profile', (q) => q.where({ bio: 'bio' }))
|
|
227
|
+
.select('name', 'profile.bio');
|
|
228
|
+
|
|
229
|
+
assertType<
|
|
230
|
+
Awaited<typeof query>,
|
|
231
|
+
{ name: string; bio: string | null }[]
|
|
232
|
+
>();
|
|
233
|
+
|
|
234
|
+
expectSql(
|
|
235
|
+
query.toSql(),
|
|
236
|
+
`
|
|
237
|
+
SELECT "u"."name", "profile"."bio" FROM "user" AS "u"
|
|
238
|
+
JOIN "profile"
|
|
239
|
+
ON "profile"."userId" = "u"."id"
|
|
240
|
+
AND "profile"."bio" = $1
|
|
241
|
+
`,
|
|
242
|
+
['bio'],
|
|
243
|
+
);
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
describe('select', () => {
|
|
247
|
+
it('should be selectable', () => {
|
|
248
|
+
const query = db.user.as('u').select('id', {
|
|
249
|
+
profile: (q) => q.profile.where({ bio: 'bio' }),
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
assertType<Awaited<typeof query>, { id: number; profile: Profile }[]>();
|
|
253
|
+
|
|
254
|
+
expectSql(
|
|
255
|
+
query.toSql(),
|
|
256
|
+
`
|
|
257
|
+
SELECT
|
|
258
|
+
"u"."id",
|
|
259
|
+
(
|
|
260
|
+
SELECT row_to_json("t".*)
|
|
261
|
+
FROM (
|
|
262
|
+
SELECT * FROM "profile"
|
|
263
|
+
WHERE "profile"."bio" = $1
|
|
264
|
+
AND "profile"."userId" = "u"."id"
|
|
265
|
+
LIMIT $2
|
|
266
|
+
) AS "t"
|
|
267
|
+
) AS "profile"
|
|
268
|
+
FROM "user" AS "u"
|
|
269
|
+
`,
|
|
270
|
+
['bio', 1],
|
|
271
|
+
);
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
it('should be selectable by relation name', () => {
|
|
275
|
+
const query = db.user.select('id', 'profile');
|
|
276
|
+
|
|
277
|
+
assertType<Awaited<typeof query>, { id: number; profile: Profile }[]>();
|
|
278
|
+
|
|
279
|
+
expectSql(
|
|
280
|
+
query.toSql(),
|
|
281
|
+
`
|
|
282
|
+
SELECT
|
|
283
|
+
"user"."id",
|
|
284
|
+
(
|
|
285
|
+
SELECT row_to_json("t".*)
|
|
286
|
+
FROM (
|
|
287
|
+
SELECT * FROM "profile"
|
|
288
|
+
WHERE "profile"."userId" = "user"."id"
|
|
289
|
+
LIMIT $1
|
|
290
|
+
) AS "t"
|
|
291
|
+
) AS "profile"
|
|
292
|
+
FROM "user"
|
|
293
|
+
`,
|
|
294
|
+
[1],
|
|
295
|
+
);
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
it('should handle exists sub query', () => {
|
|
299
|
+
const query = db.user.as('u').select('id', {
|
|
300
|
+
hasProfile: (q) => q.profile.exists(),
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
assertType<
|
|
304
|
+
Awaited<typeof query>,
|
|
305
|
+
{ id: number; hasProfile: boolean }[]
|
|
306
|
+
>();
|
|
307
|
+
|
|
308
|
+
expectSql(
|
|
309
|
+
query.toSql(),
|
|
310
|
+
`
|
|
311
|
+
SELECT
|
|
312
|
+
"u"."id",
|
|
313
|
+
COALESCE((
|
|
314
|
+
SELECT true
|
|
315
|
+
FROM "profile"
|
|
316
|
+
WHERE "profile"."userId" = "u"."id"
|
|
317
|
+
), false) AS "hasProfile"
|
|
318
|
+
FROM "user" AS "u"
|
|
319
|
+
`,
|
|
320
|
+
);
|
|
321
|
+
});
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
describe('create', () => {
|
|
325
|
+
const checkUserAndProfile = ({
|
|
326
|
+
user,
|
|
327
|
+
profile,
|
|
328
|
+
name,
|
|
329
|
+
bio,
|
|
330
|
+
}: {
|
|
331
|
+
user: User;
|
|
332
|
+
profile: Profile;
|
|
333
|
+
name: string;
|
|
334
|
+
bio: string;
|
|
335
|
+
}) => {
|
|
336
|
+
expect(user).toEqual({
|
|
337
|
+
...userData,
|
|
338
|
+
id: user.id,
|
|
339
|
+
name,
|
|
340
|
+
active: null,
|
|
341
|
+
age: null,
|
|
342
|
+
data: null,
|
|
343
|
+
picture: null,
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
expect(profile).toMatchObject({
|
|
347
|
+
id: profile.id,
|
|
348
|
+
bio,
|
|
349
|
+
userId: user.id,
|
|
350
|
+
});
|
|
351
|
+
};
|
|
352
|
+
|
|
353
|
+
describe('nested create', () => {
|
|
354
|
+
it('should support create', async () => {
|
|
355
|
+
const query = db.user.create({
|
|
356
|
+
...userData,
|
|
357
|
+
name: 'user',
|
|
358
|
+
profile: {
|
|
359
|
+
create: {
|
|
360
|
+
...profileData,
|
|
361
|
+
bio: 'profile',
|
|
362
|
+
},
|
|
363
|
+
},
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
const user = await query;
|
|
367
|
+
const profile = await db.profile.findBy({ userId: user.id });
|
|
368
|
+
|
|
369
|
+
checkUserAndProfile({ user, profile, name: 'user', bio: 'profile' });
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
it('should support create many', async () => {
|
|
373
|
+
const query = db.user.createMany([
|
|
374
|
+
{
|
|
375
|
+
...userData,
|
|
376
|
+
name: 'user 1',
|
|
377
|
+
profile: {
|
|
378
|
+
create: {
|
|
379
|
+
...profileData,
|
|
380
|
+
bio: 'profile 1',
|
|
381
|
+
},
|
|
382
|
+
},
|
|
383
|
+
},
|
|
384
|
+
{
|
|
385
|
+
...userData,
|
|
386
|
+
name: 'user 2',
|
|
387
|
+
profile: {
|
|
388
|
+
create: {
|
|
389
|
+
...profileData,
|
|
390
|
+
bio: 'profile 2',
|
|
391
|
+
},
|
|
392
|
+
},
|
|
393
|
+
},
|
|
394
|
+
]);
|
|
395
|
+
|
|
396
|
+
const users = await query;
|
|
397
|
+
const profiles = await db.profile
|
|
398
|
+
.where({
|
|
399
|
+
userId: { in: users.map((user) => user.id) },
|
|
400
|
+
})
|
|
401
|
+
.order('id');
|
|
402
|
+
|
|
403
|
+
checkUserAndProfile({
|
|
404
|
+
user: users[0],
|
|
405
|
+
profile: profiles[0],
|
|
406
|
+
name: 'user 1',
|
|
407
|
+
bio: 'profile 1',
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
checkUserAndProfile({
|
|
411
|
+
user: users[1],
|
|
412
|
+
profile: profiles[1],
|
|
413
|
+
name: 'user 2',
|
|
414
|
+
bio: 'profile 2',
|
|
415
|
+
});
|
|
416
|
+
});
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
describe('nested connect', () => {
|
|
420
|
+
it('should support connect', async () => {
|
|
421
|
+
await db.profile.create({
|
|
422
|
+
...profileData,
|
|
423
|
+
bio: 'profile',
|
|
424
|
+
user: {
|
|
425
|
+
create: {
|
|
426
|
+
...userData,
|
|
427
|
+
name: 'tmp',
|
|
428
|
+
},
|
|
429
|
+
},
|
|
430
|
+
});
|
|
431
|
+
|
|
432
|
+
const query = db.user.create({
|
|
433
|
+
...userData,
|
|
434
|
+
name: 'user',
|
|
435
|
+
profile: {
|
|
436
|
+
connect: { bio: 'profile' },
|
|
437
|
+
},
|
|
438
|
+
});
|
|
439
|
+
|
|
440
|
+
const user = await query;
|
|
441
|
+
const profile = await db.user.profile(user);
|
|
442
|
+
|
|
443
|
+
checkUserAndProfile({ user, profile, name: 'user', bio: 'profile' });
|
|
444
|
+
});
|
|
445
|
+
|
|
446
|
+
it('should support connect many', async () => {
|
|
447
|
+
await db.profile.createMany([
|
|
448
|
+
{
|
|
449
|
+
...profileData,
|
|
450
|
+
bio: 'profile 1',
|
|
451
|
+
user: {
|
|
452
|
+
create: {
|
|
453
|
+
...userData,
|
|
454
|
+
name: 'tmp',
|
|
455
|
+
},
|
|
456
|
+
},
|
|
457
|
+
},
|
|
458
|
+
{
|
|
459
|
+
...profileData,
|
|
460
|
+
bio: 'profile 2',
|
|
461
|
+
user: {
|
|
462
|
+
connect: { name: 'tmp' },
|
|
463
|
+
},
|
|
464
|
+
},
|
|
465
|
+
]);
|
|
466
|
+
|
|
467
|
+
const query = db.user.createMany([
|
|
468
|
+
{
|
|
469
|
+
...userData,
|
|
470
|
+
name: 'user 1',
|
|
471
|
+
profile: {
|
|
472
|
+
connect: { bio: 'profile 1' },
|
|
473
|
+
},
|
|
474
|
+
},
|
|
475
|
+
{
|
|
476
|
+
...userData,
|
|
477
|
+
name: 'user 2',
|
|
478
|
+
profile: {
|
|
479
|
+
connect: { bio: 'profile 2' },
|
|
480
|
+
},
|
|
481
|
+
},
|
|
482
|
+
]);
|
|
483
|
+
|
|
484
|
+
const users = await query;
|
|
485
|
+
const profiles = await db.profile
|
|
486
|
+
.where({
|
|
487
|
+
userId: { in: users.map((user) => user.id) },
|
|
488
|
+
})
|
|
489
|
+
.order('id');
|
|
490
|
+
|
|
491
|
+
checkUserAndProfile({
|
|
492
|
+
user: users[0],
|
|
493
|
+
profile: profiles[0],
|
|
494
|
+
name: 'user 1',
|
|
495
|
+
bio: 'profile 1',
|
|
496
|
+
});
|
|
497
|
+
|
|
498
|
+
checkUserAndProfile({
|
|
499
|
+
user: users[1],
|
|
500
|
+
profile: profiles[1],
|
|
501
|
+
name: 'user 2',
|
|
502
|
+
bio: 'profile 2',
|
|
503
|
+
});
|
|
504
|
+
});
|
|
505
|
+
});
|
|
506
|
+
|
|
507
|
+
describe('connect or create', () => {
|
|
508
|
+
it('should support connect or create', async () => {
|
|
509
|
+
const profileId = await db.profile.get('id').create({
|
|
510
|
+
...profileData,
|
|
511
|
+
bio: 'profile 1',
|
|
512
|
+
user: {
|
|
513
|
+
create: {
|
|
514
|
+
...userData,
|
|
515
|
+
name: 'tmp',
|
|
516
|
+
},
|
|
517
|
+
},
|
|
518
|
+
});
|
|
519
|
+
|
|
520
|
+
const user1 = await db.user.create({
|
|
521
|
+
...userData,
|
|
522
|
+
name: 'user 1',
|
|
523
|
+
profile: {
|
|
524
|
+
connectOrCreate: {
|
|
525
|
+
where: { bio: 'profile 1' },
|
|
526
|
+
create: { ...profileData, bio: 'profile 1' },
|
|
527
|
+
},
|
|
528
|
+
},
|
|
529
|
+
});
|
|
530
|
+
|
|
531
|
+
const user2 = await db.user.create({
|
|
532
|
+
...userData,
|
|
533
|
+
name: 'user 2',
|
|
534
|
+
profile: {
|
|
535
|
+
connectOrCreate: {
|
|
536
|
+
where: { bio: 'profile 2' },
|
|
537
|
+
create: { ...profileData, bio: 'profile 2' },
|
|
538
|
+
},
|
|
539
|
+
},
|
|
540
|
+
});
|
|
541
|
+
|
|
542
|
+
const profile1 = await db.user.profile(user1);
|
|
543
|
+
expect(profile1.id).toBe(profileId);
|
|
544
|
+
checkUserAndProfile({
|
|
545
|
+
user: user1,
|
|
546
|
+
profile: profile1,
|
|
547
|
+
name: 'user 1',
|
|
548
|
+
bio: 'profile 1',
|
|
549
|
+
});
|
|
550
|
+
|
|
551
|
+
const profile2 = await db.user.profile(user2);
|
|
552
|
+
checkUserAndProfile({
|
|
553
|
+
user: user2,
|
|
554
|
+
profile: profile2,
|
|
555
|
+
name: 'user 2',
|
|
556
|
+
bio: 'profile 2',
|
|
557
|
+
});
|
|
558
|
+
});
|
|
559
|
+
|
|
560
|
+
it('should support connect or create many', async () => {
|
|
561
|
+
const profileId = await db.profile.get('id').create({
|
|
562
|
+
...profileData,
|
|
563
|
+
bio: 'profile 1',
|
|
564
|
+
user: {
|
|
565
|
+
create: {
|
|
566
|
+
...userData,
|
|
567
|
+
name: 'tmp',
|
|
568
|
+
},
|
|
569
|
+
},
|
|
570
|
+
});
|
|
571
|
+
|
|
572
|
+
const [user1, user2] = await db.user.createMany([
|
|
573
|
+
{
|
|
574
|
+
...userData,
|
|
575
|
+
name: 'user 1',
|
|
576
|
+
profile: {
|
|
577
|
+
connectOrCreate: {
|
|
578
|
+
where: { bio: 'profile 1' },
|
|
579
|
+
create: { ...profileData, bio: 'profile 1' },
|
|
580
|
+
},
|
|
581
|
+
},
|
|
582
|
+
},
|
|
583
|
+
{
|
|
584
|
+
...userData,
|
|
585
|
+
name: 'user 2',
|
|
586
|
+
profile: {
|
|
587
|
+
connectOrCreate: {
|
|
588
|
+
where: { bio: 'profile 2' },
|
|
589
|
+
create: { ...profileData, bio: 'profile 2' },
|
|
590
|
+
},
|
|
591
|
+
},
|
|
592
|
+
},
|
|
593
|
+
]);
|
|
594
|
+
|
|
595
|
+
const profile1 = await db.user.profile(user1);
|
|
596
|
+
expect(profile1.id).toBe(profileId);
|
|
597
|
+
checkUserAndProfile({
|
|
598
|
+
user: user1,
|
|
599
|
+
profile: profile1,
|
|
600
|
+
name: 'user 1',
|
|
601
|
+
bio: 'profile 1',
|
|
602
|
+
});
|
|
603
|
+
|
|
604
|
+
const profile2 = await db.user.profile(user2);
|
|
605
|
+
checkUserAndProfile({
|
|
606
|
+
user: user2,
|
|
607
|
+
profile: profile2,
|
|
608
|
+
name: 'user 2',
|
|
609
|
+
bio: 'profile 2',
|
|
610
|
+
});
|
|
611
|
+
});
|
|
612
|
+
});
|
|
613
|
+
});
|
|
614
|
+
|
|
615
|
+
describe('update', () => {
|
|
616
|
+
describe('disconnect', () => {
|
|
617
|
+
it('should nullify foreignKey', async () => {
|
|
618
|
+
const user = await db.user
|
|
619
|
+
.selectAll()
|
|
620
|
+
.create({ ...userData, profile: { create: profileData } });
|
|
621
|
+
const { id: profileId } = await db.user.profile(user);
|
|
622
|
+
|
|
623
|
+
const id = await db.user
|
|
624
|
+
.get('id')
|
|
625
|
+
.where(user)
|
|
626
|
+
.update({
|
|
627
|
+
profile: {
|
|
628
|
+
disconnect: true,
|
|
629
|
+
},
|
|
630
|
+
});
|
|
631
|
+
|
|
632
|
+
expect(id).toBe(user.id);
|
|
633
|
+
|
|
634
|
+
const profile = await db.profile.find(profileId);
|
|
635
|
+
expect(profile.userId).toBe(null);
|
|
636
|
+
});
|
|
637
|
+
|
|
638
|
+
it('should nullify foreignKey in batch update', async () => {
|
|
639
|
+
const userIds = await db.user.pluck('id').createMany([
|
|
640
|
+
{ ...userData, profile: { create: profileData } },
|
|
641
|
+
{ ...userData, profile: { create: profileData } },
|
|
642
|
+
]);
|
|
643
|
+
|
|
644
|
+
const profileIds = await db.profile.pluck('id').where({
|
|
645
|
+
userId: { in: userIds },
|
|
646
|
+
});
|
|
647
|
+
|
|
648
|
+
await db.user.where({ id: { in: userIds } }).update({
|
|
649
|
+
profile: {
|
|
650
|
+
disconnect: true,
|
|
651
|
+
},
|
|
652
|
+
});
|
|
653
|
+
|
|
654
|
+
const updatedUserIds = await db.profile
|
|
655
|
+
.pluck('userId')
|
|
656
|
+
.where({ id: { in: profileIds } });
|
|
657
|
+
expect(updatedUserIds).toEqual([null, null]);
|
|
658
|
+
});
|
|
659
|
+
});
|
|
660
|
+
|
|
661
|
+
describe('set', () => {
|
|
662
|
+
it('should nullify foreignKey of previous related record and set foreignKey to new related record', async () => {
|
|
663
|
+
const id = await db.user.get('id').create(userData);
|
|
664
|
+
|
|
665
|
+
const [{ id: profile1Id }, { id: profile2Id }] = await db.profile
|
|
666
|
+
.select('id')
|
|
667
|
+
.createMany([{ ...profileData, userId: id }, { ...profileData }]);
|
|
668
|
+
|
|
669
|
+
await db.user.find(id).update({
|
|
670
|
+
profile: {
|
|
671
|
+
set: { id: profile2Id },
|
|
672
|
+
},
|
|
673
|
+
});
|
|
674
|
+
|
|
675
|
+
const profile1 = await db.profile.find(profile1Id);
|
|
676
|
+
expect(profile1.userId).toBe(null);
|
|
677
|
+
|
|
678
|
+
const profile2 = await db.profile.find(profile2Id);
|
|
679
|
+
expect(profile2.userId).toBe(id);
|
|
680
|
+
});
|
|
681
|
+
|
|
682
|
+
it('should throw in batch update', async () => {
|
|
683
|
+
const query = db.user.where({ id: { in: [1, 2, 3] } }).update({
|
|
684
|
+
profile: {
|
|
685
|
+
// @ts-expect-error not allows in batch update
|
|
686
|
+
set: { id: 1 },
|
|
687
|
+
},
|
|
688
|
+
});
|
|
689
|
+
|
|
690
|
+
await expect(query).rejects.toThrow();
|
|
691
|
+
});
|
|
692
|
+
});
|
|
693
|
+
|
|
694
|
+
describe('delete', () => {
|
|
695
|
+
it('should delete related record', async () => {
|
|
696
|
+
const id = await db.user
|
|
697
|
+
.get('id')
|
|
698
|
+
.create({ ...userData, profile: { create: profileData } });
|
|
699
|
+
|
|
700
|
+
const { id: profileId } = await db.user
|
|
701
|
+
.profile({ id })
|
|
702
|
+
.select('id')
|
|
703
|
+
.take();
|
|
704
|
+
|
|
705
|
+
await db.user.find(id).update({
|
|
706
|
+
profile: {
|
|
707
|
+
delete: true,
|
|
708
|
+
},
|
|
709
|
+
});
|
|
710
|
+
|
|
711
|
+
const profile = await db.profile.findByOptional({ id: profileId });
|
|
712
|
+
expect(profile).toBe(undefined);
|
|
713
|
+
});
|
|
714
|
+
|
|
715
|
+
it('should delete related record in batch update', async () => {
|
|
716
|
+
const userIds = await db.user.pluck('id').createMany([
|
|
717
|
+
{ ...userData, profile: { create: profileData } },
|
|
718
|
+
{ ...userData, profile: { create: profileData } },
|
|
719
|
+
]);
|
|
720
|
+
|
|
721
|
+
await db.user.where({ id: { in: userIds } }).update({
|
|
722
|
+
profile: {
|
|
723
|
+
delete: true,
|
|
724
|
+
},
|
|
725
|
+
});
|
|
726
|
+
|
|
727
|
+
const count = await db.profile.count();
|
|
728
|
+
expect(count).toBe(0);
|
|
729
|
+
});
|
|
730
|
+
});
|
|
731
|
+
|
|
732
|
+
describe('nested update', () => {
|
|
733
|
+
it('should update related record', async () => {
|
|
734
|
+
const id = await db.user
|
|
735
|
+
.get('id')
|
|
736
|
+
.create({ ...userData, profile: { create: profileData } });
|
|
737
|
+
|
|
738
|
+
await db.user.find(id).update({
|
|
739
|
+
profile: {
|
|
740
|
+
update: {
|
|
741
|
+
bio: 'updated',
|
|
742
|
+
},
|
|
743
|
+
},
|
|
744
|
+
});
|
|
745
|
+
|
|
746
|
+
const profile = await db.user.profile({ id }).take();
|
|
747
|
+
expect(profile.bio).toBe('updated');
|
|
748
|
+
});
|
|
749
|
+
|
|
750
|
+
it('should update related record in batch update', async () => {
|
|
751
|
+
const userIds = await db.user.pluck('id').createMany([
|
|
752
|
+
{ ...userData, profile: { create: profileData } },
|
|
753
|
+
{ ...userData, profile: { create: profileData } },
|
|
754
|
+
]);
|
|
755
|
+
|
|
756
|
+
await db.user.where({ id: { in: userIds } }).update({
|
|
757
|
+
profile: {
|
|
758
|
+
update: {
|
|
759
|
+
bio: 'updated',
|
|
760
|
+
},
|
|
761
|
+
},
|
|
762
|
+
});
|
|
763
|
+
|
|
764
|
+
const bios = await db.profile.pluck('bio');
|
|
765
|
+
expect(bios).toEqual(['updated', 'updated']);
|
|
766
|
+
});
|
|
767
|
+
});
|
|
768
|
+
|
|
769
|
+
describe('nested upsert', () => {
|
|
770
|
+
it('should update related record if it exists', async () => {
|
|
771
|
+
const user = await db.user.create({
|
|
772
|
+
...userData,
|
|
773
|
+
profile: { create: profileData },
|
|
774
|
+
});
|
|
775
|
+
|
|
776
|
+
await db.user.find(user.id).update({
|
|
777
|
+
profile: {
|
|
778
|
+
upsert: {
|
|
779
|
+
update: {
|
|
780
|
+
bio: 'updated',
|
|
781
|
+
},
|
|
782
|
+
create: profileData,
|
|
783
|
+
},
|
|
784
|
+
},
|
|
785
|
+
});
|
|
786
|
+
|
|
787
|
+
const profile = await db.user.profile(user);
|
|
788
|
+
expect(profile.bio).toBe('updated');
|
|
789
|
+
});
|
|
790
|
+
|
|
791
|
+
it('should create related record if it does not exists', async () => {
|
|
792
|
+
const user = await db.user.create(userData);
|
|
793
|
+
|
|
794
|
+
await db.user.find(user.id).update({
|
|
795
|
+
profile: {
|
|
796
|
+
upsert: {
|
|
797
|
+
update: {
|
|
798
|
+
bio: 'updated',
|
|
799
|
+
},
|
|
800
|
+
create: {
|
|
801
|
+
...profileData,
|
|
802
|
+
bio: 'created',
|
|
803
|
+
},
|
|
804
|
+
},
|
|
805
|
+
},
|
|
806
|
+
});
|
|
807
|
+
|
|
808
|
+
const profile = await db.user.profile(user);
|
|
809
|
+
expect(profile.bio).toBe('created');
|
|
810
|
+
});
|
|
811
|
+
|
|
812
|
+
it('should throw in batch update', async () => {
|
|
813
|
+
const query = db.user.where({ id: { in: [1, 2, 3] } }).update({
|
|
814
|
+
profile: {
|
|
815
|
+
// @ts-expect-error not allows in batch update
|
|
816
|
+
upsert: {
|
|
817
|
+
update: {
|
|
818
|
+
bio: 'updated',
|
|
819
|
+
},
|
|
820
|
+
create: {
|
|
821
|
+
...profileData,
|
|
822
|
+
bio: 'created',
|
|
823
|
+
},
|
|
824
|
+
},
|
|
825
|
+
},
|
|
826
|
+
});
|
|
827
|
+
|
|
828
|
+
await expect(query).rejects.toThrow();
|
|
829
|
+
});
|
|
830
|
+
});
|
|
831
|
+
|
|
832
|
+
describe('nested create', () => {
|
|
833
|
+
it('should create new related record', async () => {
|
|
834
|
+
const userId = await db.user
|
|
835
|
+
.get('id')
|
|
836
|
+
.create({ ...userData, profile: { create: profileData } });
|
|
837
|
+
|
|
838
|
+
const previousProfileId = await db.user
|
|
839
|
+
.profile({ id: userId })
|
|
840
|
+
.get('id');
|
|
841
|
+
|
|
842
|
+
const updated = await db.user
|
|
843
|
+
.selectAll()
|
|
844
|
+
.find(userId)
|
|
845
|
+
.update({
|
|
846
|
+
profile: {
|
|
847
|
+
create: { ...profileData, bio: 'created' },
|
|
848
|
+
},
|
|
849
|
+
});
|
|
850
|
+
|
|
851
|
+
const previousProfile = await db.profile.find(previousProfileId);
|
|
852
|
+
expect(previousProfile.userId).toBe(null);
|
|
853
|
+
|
|
854
|
+
const profile = await db.user.profile(updated);
|
|
855
|
+
expect(profile.bio).toBe('created');
|
|
856
|
+
});
|
|
857
|
+
|
|
858
|
+
it('should throw in batch update', async () => {
|
|
859
|
+
const query = db.user.where({ id: { in: [1, 2, 3] } }).update({
|
|
860
|
+
profile: {
|
|
861
|
+
// @ts-expect-error not allows in batch update
|
|
862
|
+
create: {
|
|
863
|
+
...profileData,
|
|
864
|
+
bio: 'created',
|
|
865
|
+
},
|
|
866
|
+
},
|
|
867
|
+
});
|
|
868
|
+
|
|
869
|
+
await expect(query).rejects.toThrow();
|
|
870
|
+
});
|
|
871
|
+
});
|
|
872
|
+
});
|
|
873
|
+
});
|
|
874
|
+
});
|
|
875
|
+
|
|
876
|
+
describe('hasOne through', () => {
|
|
877
|
+
it('should resolve recursive situation when both models depends on each other', () => {
|
|
878
|
+
class Post extends Model {
|
|
879
|
+
table = 'post';
|
|
880
|
+
columns = this.setColumns((t) => ({
|
|
881
|
+
id: t.serial().primaryKey(),
|
|
882
|
+
}));
|
|
883
|
+
|
|
884
|
+
relations = {
|
|
885
|
+
postTag: this.hasOne(() => PostTag, {
|
|
886
|
+
primaryKey: 'id',
|
|
887
|
+
foreignKey: 'postId',
|
|
888
|
+
}),
|
|
889
|
+
|
|
890
|
+
tag: this.hasOne(() => Tag, {
|
|
891
|
+
through: 'postTag',
|
|
892
|
+
source: 'tag',
|
|
893
|
+
}),
|
|
894
|
+
};
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
class Tag extends Model {
|
|
898
|
+
table = 'tag';
|
|
899
|
+
columns = this.setColumns((t) => ({
|
|
900
|
+
id: t.serial().primaryKey(),
|
|
901
|
+
}));
|
|
902
|
+
|
|
903
|
+
relations = {
|
|
904
|
+
postTag: this.hasOne(() => PostTag, {
|
|
905
|
+
primaryKey: 'id',
|
|
906
|
+
foreignKey: 'postId',
|
|
907
|
+
}),
|
|
908
|
+
|
|
909
|
+
post: this.hasOne(() => Post, {
|
|
910
|
+
through: 'postTag',
|
|
911
|
+
source: 'post',
|
|
912
|
+
}),
|
|
913
|
+
};
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
class PostTag extends Model {
|
|
917
|
+
table = 'postTag';
|
|
918
|
+
columns = this.setColumns((t) => ({
|
|
919
|
+
postId: t.integer().foreignKey(() => Post, 'id'),
|
|
920
|
+
tagId: t.integer().foreignKey(() => Tag, 'id'),
|
|
921
|
+
}));
|
|
922
|
+
|
|
923
|
+
relations = {
|
|
924
|
+
post: this.belongsTo(() => Post, {
|
|
925
|
+
primaryKey: 'id',
|
|
926
|
+
foreignKey: 'postId',
|
|
927
|
+
}),
|
|
928
|
+
|
|
929
|
+
tag: this.belongsTo(() => Tag, {
|
|
930
|
+
primaryKey: 'id',
|
|
931
|
+
foreignKey: 'tagId',
|
|
932
|
+
}),
|
|
933
|
+
};
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
const db = orchidORM(
|
|
937
|
+
{
|
|
938
|
+
...pgConfig,
|
|
939
|
+
log: false,
|
|
940
|
+
},
|
|
941
|
+
{
|
|
942
|
+
post: Post,
|
|
943
|
+
tag: Tag,
|
|
944
|
+
postTag: PostTag,
|
|
945
|
+
},
|
|
946
|
+
);
|
|
947
|
+
|
|
948
|
+
expect(Object.keys(db.post.relations)).toEqual(['postTag', 'tag']);
|
|
949
|
+
expect(Object.keys(db.tag.relations)).toEqual(['postTag', 'post']);
|
|
950
|
+
});
|
|
951
|
+
|
|
952
|
+
it('should have method to query related data', async () => {
|
|
953
|
+
const profileQuery = db.profile.take();
|
|
954
|
+
|
|
955
|
+
assertType<
|
|
956
|
+
typeof db.message.profile,
|
|
957
|
+
RelationQuery<
|
|
958
|
+
'profile',
|
|
959
|
+
{ authorId: number | null },
|
|
960
|
+
never,
|
|
961
|
+
typeof profileQuery,
|
|
962
|
+
true,
|
|
963
|
+
false,
|
|
964
|
+
true
|
|
965
|
+
>
|
|
966
|
+
>();
|
|
967
|
+
|
|
968
|
+
const query = db.message.profile({ authorId: 1 });
|
|
969
|
+
expectSql(
|
|
970
|
+
query.toSql(),
|
|
971
|
+
`
|
|
972
|
+
SELECT * FROM "profile"
|
|
973
|
+
WHERE EXISTS (
|
|
974
|
+
SELECT 1 FROM "user"
|
|
975
|
+
WHERE "profile"."userId" = "user"."id"
|
|
976
|
+
AND "user"."id" = $1
|
|
977
|
+
LIMIT 1
|
|
978
|
+
)
|
|
979
|
+
LIMIT $2
|
|
980
|
+
`,
|
|
981
|
+
[1, 1],
|
|
982
|
+
);
|
|
983
|
+
});
|
|
984
|
+
|
|
985
|
+
it('should handle chained query', () => {
|
|
986
|
+
const query = db.message
|
|
987
|
+
.where({ text: 'text' })
|
|
988
|
+
.profile.where({ bio: 'bio' });
|
|
989
|
+
|
|
990
|
+
expectSql(
|
|
991
|
+
query.toSql(),
|
|
992
|
+
`
|
|
993
|
+
SELECT * FROM "profile"
|
|
994
|
+
WHERE EXISTS (
|
|
995
|
+
SELECT 1 FROM "message"
|
|
996
|
+
WHERE "message"."text" = $1
|
|
997
|
+
AND EXISTS (
|
|
998
|
+
SELECT 1 FROM "user"
|
|
999
|
+
WHERE "profile"."userId" = "user"."id"
|
|
1000
|
+
AND "user"."id" = "message"."authorId"
|
|
1001
|
+
LIMIT 1
|
|
1002
|
+
)
|
|
1003
|
+
LIMIT 1
|
|
1004
|
+
)
|
|
1005
|
+
AND "profile"."bio" = $2
|
|
1006
|
+
LIMIT $3
|
|
1007
|
+
`,
|
|
1008
|
+
['text', 'bio', 1],
|
|
1009
|
+
);
|
|
1010
|
+
});
|
|
1011
|
+
|
|
1012
|
+
it('should have disabled create method', () => {
|
|
1013
|
+
// @ts-expect-error hasOne with through option should not have chained create
|
|
1014
|
+
db.message.profile.create(chatData);
|
|
1015
|
+
});
|
|
1016
|
+
|
|
1017
|
+
it('should have chained delete method', () => {
|
|
1018
|
+
const query = db.message
|
|
1019
|
+
.where({ text: 'text' })
|
|
1020
|
+
.profile.where({ bio: 'bio' })
|
|
1021
|
+
.delete();
|
|
1022
|
+
|
|
1023
|
+
expectSql(
|
|
1024
|
+
query.toSql(),
|
|
1025
|
+
`
|
|
1026
|
+
DELETE FROM "profile"
|
|
1027
|
+
WHERE EXISTS (
|
|
1028
|
+
SELECT 1 FROM "message"
|
|
1029
|
+
WHERE "message"."text" = $1
|
|
1030
|
+
AND EXISTS (
|
|
1031
|
+
SELECT 1 FROM "user"
|
|
1032
|
+
WHERE "profile"."userId" = "user"."id"
|
|
1033
|
+
AND "user"."id" = "message"."authorId"
|
|
1034
|
+
LIMIT 1
|
|
1035
|
+
)
|
|
1036
|
+
LIMIT 1
|
|
1037
|
+
)
|
|
1038
|
+
AND "profile"."bio" = $2
|
|
1039
|
+
`,
|
|
1040
|
+
['text', 'bio'],
|
|
1041
|
+
);
|
|
1042
|
+
});
|
|
1043
|
+
|
|
1044
|
+
it('should have proper joinQuery', () => {
|
|
1045
|
+
expectSql(
|
|
1046
|
+
db.message.relations.profile
|
|
1047
|
+
.joinQuery(db.message.as('m'), db.profile.as('p'))
|
|
1048
|
+
.toSql(),
|
|
1049
|
+
`
|
|
1050
|
+
SELECT * FROM "profile" AS "p"
|
|
1051
|
+
WHERE EXISTS (
|
|
1052
|
+
SELECT 1 FROM "user"
|
|
1053
|
+
WHERE "p"."userId" = "user"."id"
|
|
1054
|
+
AND "user"."id" = "m"."authorId"
|
|
1055
|
+
LIMIT 1
|
|
1056
|
+
)
|
|
1057
|
+
`,
|
|
1058
|
+
);
|
|
1059
|
+
});
|
|
1060
|
+
|
|
1061
|
+
it('should be supported in whereExists', () => {
|
|
1062
|
+
expectSql(
|
|
1063
|
+
db.message.whereExists('profile').toSql(),
|
|
1064
|
+
`
|
|
1065
|
+
SELECT * FROM "message"
|
|
1066
|
+
WHERE EXISTS (
|
|
1067
|
+
SELECT 1 FROM "profile"
|
|
1068
|
+
WHERE EXISTS (
|
|
1069
|
+
SELECT 1 FROM "user"
|
|
1070
|
+
WHERE "profile"."userId" = "user"."id"
|
|
1071
|
+
AND "user"."id" = "message"."authorId"
|
|
1072
|
+
LIMIT 1
|
|
1073
|
+
)
|
|
1074
|
+
LIMIT 1
|
|
1075
|
+
)
|
|
1076
|
+
`,
|
|
1077
|
+
);
|
|
1078
|
+
|
|
1079
|
+
expectSql(
|
|
1080
|
+
db.message
|
|
1081
|
+
.as('m')
|
|
1082
|
+
.whereExists('profile', (q) => q.where({ bio: 'bio' }))
|
|
1083
|
+
.toSql(),
|
|
1084
|
+
`
|
|
1085
|
+
SELECT * FROM "message" AS "m"
|
|
1086
|
+
WHERE EXISTS (
|
|
1087
|
+
SELECT 1 FROM "profile"
|
|
1088
|
+
WHERE EXISTS (
|
|
1089
|
+
SELECT 1 FROM "user"
|
|
1090
|
+
WHERE "profile"."userId" = "user"."id"
|
|
1091
|
+
AND "user"."id" = "m"."authorId"
|
|
1092
|
+
LIMIT 1
|
|
1093
|
+
)
|
|
1094
|
+
AND "profile"."bio" = $1
|
|
1095
|
+
LIMIT 1
|
|
1096
|
+
)
|
|
1097
|
+
`,
|
|
1098
|
+
['bio'],
|
|
1099
|
+
);
|
|
1100
|
+
});
|
|
1101
|
+
|
|
1102
|
+
it('should be supported in join', () => {
|
|
1103
|
+
const query = db.message
|
|
1104
|
+
.as('m')
|
|
1105
|
+
.join('profile', (q) => q.where({ bio: 'bio' }))
|
|
1106
|
+
.select('text', 'profile.bio');
|
|
1107
|
+
|
|
1108
|
+
assertType<Awaited<typeof query>, { text: string; bio: string | null }[]>();
|
|
1109
|
+
|
|
1110
|
+
expectSql(
|
|
1111
|
+
query.toSql(),
|
|
1112
|
+
`
|
|
1113
|
+
SELECT "m"."text", "profile"."bio" FROM "message" AS "m"
|
|
1114
|
+
JOIN "profile"
|
|
1115
|
+
ON EXISTS (
|
|
1116
|
+
SELECT 1 FROM "user"
|
|
1117
|
+
WHERE "profile"."userId" = "user"."id"
|
|
1118
|
+
AND "user"."id" = "m"."authorId"
|
|
1119
|
+
LIMIT 1
|
|
1120
|
+
)
|
|
1121
|
+
AND "profile"."bio" = $1
|
|
1122
|
+
`,
|
|
1123
|
+
['bio'],
|
|
1124
|
+
);
|
|
1125
|
+
});
|
|
1126
|
+
|
|
1127
|
+
describe('select', () => {
|
|
1128
|
+
it('should be selectable', () => {
|
|
1129
|
+
const query = db.message.as('m').select('id', {
|
|
1130
|
+
profile: (q) => q.profile.where({ bio: 'bio' }),
|
|
1131
|
+
});
|
|
1132
|
+
|
|
1133
|
+
assertType<Awaited<typeof query>, { id: number; profile: Profile }[]>();
|
|
1134
|
+
|
|
1135
|
+
expectSql(
|
|
1136
|
+
query.toSql(),
|
|
1137
|
+
`
|
|
1138
|
+
SELECT
|
|
1139
|
+
"m"."id",
|
|
1140
|
+
(
|
|
1141
|
+
SELECT row_to_json("t".*)
|
|
1142
|
+
FROM (
|
|
1143
|
+
SELECT * FROM "profile"
|
|
1144
|
+
WHERE "profile"."bio" = $1
|
|
1145
|
+
AND EXISTS (
|
|
1146
|
+
SELECT 1 FROM "user"
|
|
1147
|
+
WHERE "profile"."userId" = "user"."id"
|
|
1148
|
+
AND "user"."id" = "m"."authorId"
|
|
1149
|
+
LIMIT 1
|
|
1150
|
+
)
|
|
1151
|
+
LIMIT $2
|
|
1152
|
+
) AS "t"
|
|
1153
|
+
) AS "profile"
|
|
1154
|
+
FROM "message" AS "m"
|
|
1155
|
+
`,
|
|
1156
|
+
['bio', 1],
|
|
1157
|
+
);
|
|
1158
|
+
});
|
|
1159
|
+
|
|
1160
|
+
it('should be selectable by relation name', () => {
|
|
1161
|
+
const query = db.message.select('id', 'profile');
|
|
1162
|
+
|
|
1163
|
+
assertType<Awaited<typeof query>, { id: number; profile: Profile }[]>();
|
|
1164
|
+
|
|
1165
|
+
expectSql(
|
|
1166
|
+
query.toSql(),
|
|
1167
|
+
`
|
|
1168
|
+
SELECT
|
|
1169
|
+
"message"."id",
|
|
1170
|
+
(
|
|
1171
|
+
SELECT row_to_json("t".*)
|
|
1172
|
+
FROM (
|
|
1173
|
+
SELECT * FROM "profile"
|
|
1174
|
+
WHERE EXISTS (
|
|
1175
|
+
SELECT 1 FROM "user"
|
|
1176
|
+
WHERE "profile"."userId" = "user"."id"
|
|
1177
|
+
AND "user"."id" = "message"."authorId"
|
|
1178
|
+
LIMIT 1
|
|
1179
|
+
)
|
|
1180
|
+
LIMIT $1
|
|
1181
|
+
) AS "t"
|
|
1182
|
+
) AS "profile"
|
|
1183
|
+
FROM "message"
|
|
1184
|
+
`,
|
|
1185
|
+
[1],
|
|
1186
|
+
);
|
|
1187
|
+
});
|
|
1188
|
+
|
|
1189
|
+
it('should handle exists sub query', () => {
|
|
1190
|
+
const query = db.message.as('m').select('id', {
|
|
1191
|
+
hasProfile: (q) => q.profile.exists(),
|
|
1192
|
+
});
|
|
1193
|
+
|
|
1194
|
+
assertType<
|
|
1195
|
+
Awaited<typeof query>,
|
|
1196
|
+
{ id: number; hasProfile: boolean }[]
|
|
1197
|
+
>();
|
|
1198
|
+
|
|
1199
|
+
expectSql(
|
|
1200
|
+
query.toSql(),
|
|
1201
|
+
`
|
|
1202
|
+
SELECT
|
|
1203
|
+
"m"."id",
|
|
1204
|
+
COALESCE((
|
|
1205
|
+
SELECT true
|
|
1206
|
+
FROM "profile"
|
|
1207
|
+
WHERE EXISTS (
|
|
1208
|
+
SELECT 1 FROM "user"
|
|
1209
|
+
WHERE "profile"."userId" = "user"."id"
|
|
1210
|
+
AND "user"."id" = "m"."authorId"
|
|
1211
|
+
LIMIT 1
|
|
1212
|
+
)
|
|
1213
|
+
), false) AS "hasProfile"
|
|
1214
|
+
FROM "message" AS "m"
|
|
1215
|
+
`,
|
|
1216
|
+
);
|
|
1217
|
+
});
|
|
1218
|
+
});
|
|
1219
|
+
});
|