@apisr/drizzle-model 2.0.0 → 2.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +26 -0
- package/README.md +471 -0
- package/TODO.md +5 -5
- package/package.json +4 -3
- package/src/core/query/joins.ts +70 -3
- package/src/core/result.ts +12 -6
- package/src/core/runtime.ts +39 -15
- package/src/model/query/operations.ts +77 -39
- package/src/model/result.ts +5 -2
- package/tests/base/esc-chainable.test.ts +159 -0
- package/tests/base/relations.test.ts +593 -0
- package/tests/base/upsert.test.ts +3 -3
- package/tests/snippets/x-2.ts +28 -0
|
@@ -0,0 +1,593 @@
|
|
|
1
|
+
import { beforeAll, describe, expect, test } from "bun:test";
|
|
2
|
+
import { model } from "tests/base";
|
|
3
|
+
import { db } from "tests/db";
|
|
4
|
+
import { esc } from "@/model";
|
|
5
|
+
|
|
6
|
+
const userModel = model("user", {});
|
|
7
|
+
const postModel = model("userPosts", {});
|
|
8
|
+
const commentModel = model("postComments", {});
|
|
9
|
+
const ideaModel = model("userIdeas", {});
|
|
10
|
+
|
|
11
|
+
function uid(): string {
|
|
12
|
+
return `${Date.now()}-${Math.random()}`;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
describe("relations", () => {
|
|
16
|
+
let testUserId: number;
|
|
17
|
+
let testPostId: number;
|
|
18
|
+
let testCommentId: number;
|
|
19
|
+
let inviteeUserId: number;
|
|
20
|
+
|
|
21
|
+
beforeAll(async () => {
|
|
22
|
+
const user = await userModel
|
|
23
|
+
.insert({
|
|
24
|
+
name: "Relation Test User",
|
|
25
|
+
email: `${uid()}@relations.com`,
|
|
26
|
+
age: 25,
|
|
27
|
+
})
|
|
28
|
+
.returnFirst();
|
|
29
|
+
testUserId = user.id;
|
|
30
|
+
|
|
31
|
+
const invitee = await userModel
|
|
32
|
+
.insert({
|
|
33
|
+
name: "Invitee User",
|
|
34
|
+
email: `${uid()}@relations.com`,
|
|
35
|
+
age: 22,
|
|
36
|
+
invitedBy: testUserId,
|
|
37
|
+
})
|
|
38
|
+
.returnFirst();
|
|
39
|
+
inviteeUserId = invitee.id;
|
|
40
|
+
|
|
41
|
+
const post = await postModel
|
|
42
|
+
.insert({
|
|
43
|
+
title: "Test Post for Relations",
|
|
44
|
+
description: "Testing relations",
|
|
45
|
+
userId: testUserId,
|
|
46
|
+
})
|
|
47
|
+
.returnFirst();
|
|
48
|
+
testPostId = post.id;
|
|
49
|
+
|
|
50
|
+
const comment = await commentModel
|
|
51
|
+
.insert({
|
|
52
|
+
content: "Test comment",
|
|
53
|
+
authorId: testUserId,
|
|
54
|
+
postId: testPostId,
|
|
55
|
+
})
|
|
56
|
+
.returnFirst();
|
|
57
|
+
testCommentId = comment.id;
|
|
58
|
+
|
|
59
|
+
await ideaModel.insert({
|
|
60
|
+
content: "Test idea",
|
|
61
|
+
userId: testUserId,
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
// -----------------------------------------------------------------------
|
|
66
|
+
// Basic .with() - loading relations
|
|
67
|
+
// -----------------------------------------------------------------------
|
|
68
|
+
|
|
69
|
+
describe(".with() - basic relation loading", () => {
|
|
70
|
+
test("loads one-to-many relation (posts)", async () => {
|
|
71
|
+
const users = await userModel
|
|
72
|
+
.where({ id: esc(testUserId) })
|
|
73
|
+
.findMany()
|
|
74
|
+
.with({ posts: true });
|
|
75
|
+
|
|
76
|
+
expect(users).toBeArray();
|
|
77
|
+
const user = users[0]!;
|
|
78
|
+
expect(user).toBeDefined();
|
|
79
|
+
expect(user.posts).toBeArray();
|
|
80
|
+
expect(user.posts.length).toBeGreaterThan(0);
|
|
81
|
+
expect(user.posts[0]?.title).toBeDefined();
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
test("loads many-to-one relation (user from post)", async () => {
|
|
85
|
+
const posts = await postModel
|
|
86
|
+
.where({ id: esc(testPostId) })
|
|
87
|
+
.findMany()
|
|
88
|
+
.with({ user: true });
|
|
89
|
+
|
|
90
|
+
expect(posts).toBeArray();
|
|
91
|
+
const post = posts[0]!;
|
|
92
|
+
expect(post).toBeDefined();
|
|
93
|
+
expect(post.user).toBeDefined();
|
|
94
|
+
expect(post.user.name).toBe("Relation Test User");
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
test("loads self-referencing relation (invitee)", async () => {
|
|
98
|
+
const users = await userModel
|
|
99
|
+
.where({ id: esc(inviteeUserId) })
|
|
100
|
+
.findMany()
|
|
101
|
+
.with({ invitee: true });
|
|
102
|
+
|
|
103
|
+
expect(users).toBeArray();
|
|
104
|
+
const user = users[0]!;
|
|
105
|
+
expect(user).toBeDefined();
|
|
106
|
+
expect(user.invitee).toBeDefined();
|
|
107
|
+
expect(user.invitee?.id).toBe(testUserId);
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
// -----------------------------------------------------------------------
|
|
112
|
+
// Nested relations
|
|
113
|
+
// -----------------------------------------------------------------------
|
|
114
|
+
|
|
115
|
+
describe(".with() - nested relations", () => {
|
|
116
|
+
test("loads nested relations (user -> posts -> comments)", async () => {
|
|
117
|
+
const users = await userModel
|
|
118
|
+
.where({ id: esc(testUserId) })
|
|
119
|
+
.findMany()
|
|
120
|
+
.with({
|
|
121
|
+
posts: {
|
|
122
|
+
comments: true,
|
|
123
|
+
},
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
expect(users).toBeArray();
|
|
127
|
+
const user = users[0]!;
|
|
128
|
+
expect(user).toBeDefined();
|
|
129
|
+
expect(user.posts).toBeArray();
|
|
130
|
+
const post = user.posts[0]!;
|
|
131
|
+
expect(post).toBeDefined();
|
|
132
|
+
expect(post.comments).toBeArray();
|
|
133
|
+
expect(post.comments.length).toBeGreaterThan(0);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
test("loads deeply nested relations (post -> comments -> author)", async () => {
|
|
137
|
+
const posts = await postModel
|
|
138
|
+
.where({ id: esc(testPostId) })
|
|
139
|
+
.findMany()
|
|
140
|
+
.with({
|
|
141
|
+
comments: {
|
|
142
|
+
author: true,
|
|
143
|
+
},
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
expect(posts).toBeArray();
|
|
147
|
+
const post = posts[0]!;
|
|
148
|
+
expect(post).toBeDefined();
|
|
149
|
+
expect(post.comments).toBeArray();
|
|
150
|
+
const comment = post.comments[0]!;
|
|
151
|
+
expect(comment).toBeDefined();
|
|
152
|
+
expect(comment.author).toBeDefined();
|
|
153
|
+
expect(comment.author.name).toBe("Relation Test User");
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
// -----------------------------------------------------------------------
|
|
158
|
+
// Multiple relations
|
|
159
|
+
// -----------------------------------------------------------------------
|
|
160
|
+
|
|
161
|
+
describe(".with() - multiple relations", () => {
|
|
162
|
+
test("loads multiple relations at once", async () => {
|
|
163
|
+
const users = await userModel
|
|
164
|
+
.where({ id: esc(testUserId) })
|
|
165
|
+
.findMany()
|
|
166
|
+
.with({
|
|
167
|
+
posts: true,
|
|
168
|
+
invitee: true,
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
expect(users).toBeArray();
|
|
172
|
+
const user = users[0]!;
|
|
173
|
+
expect(user).toBeDefined();
|
|
174
|
+
expect(user.posts).toBeArray();
|
|
175
|
+
expect(user.invitee).toBeDefined();
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
test("loads multiple relations with nested relations", async () => {
|
|
179
|
+
const posts = await postModel
|
|
180
|
+
.where({ id: esc(testPostId) })
|
|
181
|
+
.findMany()
|
|
182
|
+
.with({
|
|
183
|
+
user: true,
|
|
184
|
+
comments: {
|
|
185
|
+
author: true,
|
|
186
|
+
},
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
expect(posts).toBeArray();
|
|
190
|
+
const post = posts[0]!;
|
|
191
|
+
expect(post).toBeDefined();
|
|
192
|
+
expect(post.user).toBeDefined();
|
|
193
|
+
expect(post.comments).toBeArray();
|
|
194
|
+
const comment = post.comments[0]!;
|
|
195
|
+
expect(comment).toBeDefined();
|
|
196
|
+
expect(comment.author).toBeDefined();
|
|
197
|
+
});
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
// -----------------------------------------------------------------------
|
|
201
|
+
// Query where relations
|
|
202
|
+
// -----------------------------------------------------------------------
|
|
203
|
+
|
|
204
|
+
describe(".with() - filtered relations", () => {
|
|
205
|
+
test("filters relation with where clause", async () => {
|
|
206
|
+
await postModel.insert({
|
|
207
|
+
title: "New Post Title",
|
|
208
|
+
description: "Another post",
|
|
209
|
+
userId: testUserId,
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
const users = await userModel
|
|
213
|
+
.where({ id: esc(testUserId) })
|
|
214
|
+
.findMany()
|
|
215
|
+
.with({
|
|
216
|
+
posts: postModel.where({
|
|
217
|
+
title: {
|
|
218
|
+
like: "New%",
|
|
219
|
+
},
|
|
220
|
+
}),
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
expect(users).toBeArray();
|
|
224
|
+
const user = users[0]!;
|
|
225
|
+
expect(user).toBeDefined();
|
|
226
|
+
expect(user.posts).toBeArray();
|
|
227
|
+
for (const post of user.posts) {
|
|
228
|
+
expect(post.title.startsWith("New")).toBe(true);
|
|
229
|
+
}
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
test("filters nested relations", async () => {
|
|
233
|
+
const users = await userModel
|
|
234
|
+
.where({ id: esc(testUserId) })
|
|
235
|
+
.findMany()
|
|
236
|
+
.with({
|
|
237
|
+
posts: postModel.where({ likes: { gte: esc(0) } }).include({
|
|
238
|
+
comments: true,
|
|
239
|
+
}),
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
expect(users).toBeArray();
|
|
243
|
+
const user = users[0]!;
|
|
244
|
+
expect(user).toBeDefined();
|
|
245
|
+
expect(user.posts).toBeArray();
|
|
246
|
+
});
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
// -----------------------------------------------------------------------
|
|
250
|
+
// .include() for type-safe relation values
|
|
251
|
+
// -----------------------------------------------------------------------
|
|
252
|
+
|
|
253
|
+
describe(".include() - type-safe relation selection", () => {
|
|
254
|
+
test("uses .include() to pass nested relations to .with()", async () => {
|
|
255
|
+
const users = await userModel
|
|
256
|
+
.where({ id: esc(testUserId) })
|
|
257
|
+
.findMany()
|
|
258
|
+
.with({
|
|
259
|
+
posts: postModel
|
|
260
|
+
.where({
|
|
261
|
+
title: {
|
|
262
|
+
like: "%",
|
|
263
|
+
},
|
|
264
|
+
})
|
|
265
|
+
.include({
|
|
266
|
+
comments: true,
|
|
267
|
+
}),
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
expect(users).toBeArray();
|
|
271
|
+
const user = users[0]!;
|
|
272
|
+
expect(user).toBeDefined();
|
|
273
|
+
expect(user.posts).toBeArray();
|
|
274
|
+
if (user.posts.length > 0) {
|
|
275
|
+
const post = user.posts[0]!;
|
|
276
|
+
expect(post).toBeDefined();
|
|
277
|
+
expect(post.comments).toBeDefined();
|
|
278
|
+
}
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
test("chains .include() with filtered relations", async () => {
|
|
282
|
+
const posts = await postModel
|
|
283
|
+
.where({ id: esc(testPostId) })
|
|
284
|
+
.findMany()
|
|
285
|
+
.with({
|
|
286
|
+
comments: commentModel
|
|
287
|
+
.where({ content: { like: "%test%" } })
|
|
288
|
+
.include({
|
|
289
|
+
author: true,
|
|
290
|
+
}),
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
expect(posts).toBeArray();
|
|
294
|
+
const post = posts[0]!;
|
|
295
|
+
expect(post).toBeDefined();
|
|
296
|
+
expect(post.comments).toBeArray();
|
|
297
|
+
});
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
// -----------------------------------------------------------------------
|
|
301
|
+
// Combining relations with select/exclude
|
|
302
|
+
// -----------------------------------------------------------------------
|
|
303
|
+
|
|
304
|
+
describe("relations with .select() and .exclude()", () => {
|
|
305
|
+
test("combines .with() and .select()", async () => {
|
|
306
|
+
const users = await userModel
|
|
307
|
+
.where({ id: esc(testUserId) })
|
|
308
|
+
.findMany()
|
|
309
|
+
.with({ posts: true })
|
|
310
|
+
.select({ id: true, name: true });
|
|
311
|
+
|
|
312
|
+
expect(users).toBeArray();
|
|
313
|
+
const user = users[0] as (typeof users)[0] & { posts: unknown[] };
|
|
314
|
+
expect(user).toBeDefined();
|
|
315
|
+
expect(user.id).toBeDefined();
|
|
316
|
+
expect(user.name).toBeDefined();
|
|
317
|
+
// @ts-expect-error
|
|
318
|
+
expect(user.email).toBeUndefined();
|
|
319
|
+
expect(user.posts).toBeArray();
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
test("combines .with() and .exclude()", async () => {
|
|
323
|
+
const users = await userModel
|
|
324
|
+
.where({ id: esc(testUserId) })
|
|
325
|
+
.findMany()
|
|
326
|
+
.with({ posts: true })
|
|
327
|
+
.exclude({ email: true, secretField: true });
|
|
328
|
+
|
|
329
|
+
expect(users).toBeArray();
|
|
330
|
+
const user = users[0]!;
|
|
331
|
+
expect(user).toBeDefined();
|
|
332
|
+
expect(user.id).toBeDefined();
|
|
333
|
+
expect(user.name).toBeDefined();
|
|
334
|
+
// @ts-expect-error
|
|
335
|
+
expect(user.email).toBeUndefined();
|
|
336
|
+
// @ts-expect-error
|
|
337
|
+
expect(user.secretField).toBeUndefined();
|
|
338
|
+
expect(user.posts).toBeArray();
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
test("combines .with() and .exclude() removes specific columns", async () => {
|
|
342
|
+
const users = await userModel
|
|
343
|
+
.where({ id: esc(testUserId) })
|
|
344
|
+
.findMany()
|
|
345
|
+
.with({ posts: true })
|
|
346
|
+
.exclude({ email: true, age: true });
|
|
347
|
+
|
|
348
|
+
expect(users).toBeArray();
|
|
349
|
+
const user = users[0]!;
|
|
350
|
+
expect(user).toBeDefined();
|
|
351
|
+
expect(user.id).toBeDefined();
|
|
352
|
+
expect(user.name).toBeDefined();
|
|
353
|
+
// @ts-expect-error
|
|
354
|
+
expect(user.email).toBeUndefined();
|
|
355
|
+
// @ts-expect-error
|
|
356
|
+
expect(user.age).toBeUndefined();
|
|
357
|
+
expect(user.posts).toBeArray();
|
|
358
|
+
});
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
// -----------------------------------------------------------------------
|
|
362
|
+
// Relations with findFirst
|
|
363
|
+
// -----------------------------------------------------------------------
|
|
364
|
+
|
|
365
|
+
describe("relations with .findFirst()", () => {
|
|
366
|
+
test("loads relations with findFirst", async () => {
|
|
367
|
+
const user = await userModel
|
|
368
|
+
.where({ id: esc(testUserId) })
|
|
369
|
+
.findFirst()
|
|
370
|
+
.with({ posts: true });
|
|
371
|
+
|
|
372
|
+
expect(user).toBeDefined();
|
|
373
|
+
if (user) {
|
|
374
|
+
expect(user.posts).toBeArray();
|
|
375
|
+
}
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
test("loads nested relations with findFirst", async () => {
|
|
379
|
+
const user = await userModel
|
|
380
|
+
.where({ id: esc(testUserId) })
|
|
381
|
+
.findFirst()
|
|
382
|
+
.with({
|
|
383
|
+
posts: {
|
|
384
|
+
comments: true,
|
|
385
|
+
},
|
|
386
|
+
});
|
|
387
|
+
|
|
388
|
+
expect(user).toBeDefined();
|
|
389
|
+
if (user) {
|
|
390
|
+
expect(user.posts).toBeArray();
|
|
391
|
+
if (user.posts.length > 0) {
|
|
392
|
+
const post = user.posts[0]!;
|
|
393
|
+
expect(post).toBeDefined();
|
|
394
|
+
expect(post.comments).toBeDefined();
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
});
|
|
398
|
+
});
|
|
399
|
+
|
|
400
|
+
// -----------------------------------------------------------------------
|
|
401
|
+
// Relations with .safe()
|
|
402
|
+
// -----------------------------------------------------------------------
|
|
403
|
+
|
|
404
|
+
describe("relations with .safe()", () => {
|
|
405
|
+
test("wraps relation query in safe result", async () => {
|
|
406
|
+
const result = await userModel
|
|
407
|
+
.where({ id: esc(testUserId) })
|
|
408
|
+
.findMany()
|
|
409
|
+
.with({ posts: true })
|
|
410
|
+
.safe();
|
|
411
|
+
|
|
412
|
+
expect(result.error).toBeUndefined();
|
|
413
|
+
expect(result.data).toBeDefined();
|
|
414
|
+
if (result.data) {
|
|
415
|
+
expect(result.data).toBeArray();
|
|
416
|
+
const user = result.data[0]!;
|
|
417
|
+
expect(user).toBeDefined();
|
|
418
|
+
expect(user.posts).toBeArray();
|
|
419
|
+
}
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
test("safe with findFirst and relations", async () => {
|
|
423
|
+
const result = await userModel
|
|
424
|
+
.where({ id: esc(testUserId) })
|
|
425
|
+
.findFirst()
|
|
426
|
+
.with({ posts: true })
|
|
427
|
+
.safe();
|
|
428
|
+
|
|
429
|
+
expect(result.error).toBeUndefined();
|
|
430
|
+
expect(result.data).toBeDefined();
|
|
431
|
+
if (result.data) {
|
|
432
|
+
expect(result.data.posts).toBeArray();
|
|
433
|
+
}
|
|
434
|
+
});
|
|
435
|
+
});
|
|
436
|
+
|
|
437
|
+
// -----------------------------------------------------------------------
|
|
438
|
+
// Relations with .raw()
|
|
439
|
+
// -----------------------------------------------------------------------
|
|
440
|
+
|
|
441
|
+
describe("relations with .raw()", () => {
|
|
442
|
+
test("skips format function with relations", async () => {
|
|
443
|
+
const userModelWithFormat = model("user", {
|
|
444
|
+
format({ secretField, ...rest }) {
|
|
445
|
+
return rest;
|
|
446
|
+
},
|
|
447
|
+
});
|
|
448
|
+
|
|
449
|
+
const users = await userModelWithFormat
|
|
450
|
+
.where({ id: esc(testUserId) })
|
|
451
|
+
.findMany()
|
|
452
|
+
.with({ posts: true })
|
|
453
|
+
.raw();
|
|
454
|
+
|
|
455
|
+
expect(users).toBeArray();
|
|
456
|
+
const user = users[0]!;
|
|
457
|
+
expect(user).toBeDefined();
|
|
458
|
+
expect(user.secretField).toBeDefined();
|
|
459
|
+
expect(user.posts).toBeArray();
|
|
460
|
+
});
|
|
461
|
+
});
|
|
462
|
+
|
|
463
|
+
// -----------------------------------------------------------------------
|
|
464
|
+
// Edge cases
|
|
465
|
+
// -----------------------------------------------------------------------
|
|
466
|
+
|
|
467
|
+
describe("edge cases", () => {
|
|
468
|
+
test("handles empty relation arrays", async () => {
|
|
469
|
+
const newUser = await userModel
|
|
470
|
+
.insert({
|
|
471
|
+
name: "No Posts User",
|
|
472
|
+
email: `${uid()}@relations.com`,
|
|
473
|
+
age: 30,
|
|
474
|
+
})
|
|
475
|
+
.returnFirst();
|
|
476
|
+
|
|
477
|
+
const users = await userModel
|
|
478
|
+
.where({ id: esc(newUser.id) })
|
|
479
|
+
.findMany()
|
|
480
|
+
.with({ posts: true });
|
|
481
|
+
|
|
482
|
+
expect(users).toBeArray();
|
|
483
|
+
const user = users[0]!;
|
|
484
|
+
expect(user).toBeDefined();
|
|
485
|
+
expect(user.posts).toBeArray();
|
|
486
|
+
expect(user.posts).toHaveLength(0);
|
|
487
|
+
});
|
|
488
|
+
|
|
489
|
+
test("handles null one-to-one relations", async () => {
|
|
490
|
+
const newUser = await userModel
|
|
491
|
+
.insert({
|
|
492
|
+
name: "No Inviter User",
|
|
493
|
+
email: `${uid()}@relations.com`,
|
|
494
|
+
age: 28,
|
|
495
|
+
})
|
|
496
|
+
.returnFirst();
|
|
497
|
+
|
|
498
|
+
const users = await userModel
|
|
499
|
+
.where({ id: esc(newUser.id) })
|
|
500
|
+
.findMany()
|
|
501
|
+
.with({ invitee: true });
|
|
502
|
+
|
|
503
|
+
expect(users).toBeArray();
|
|
504
|
+
const user = users[0]!;
|
|
505
|
+
expect(user).toBeDefined();
|
|
506
|
+
expect(user.invitee).toBeNull();
|
|
507
|
+
});
|
|
508
|
+
|
|
509
|
+
test("loads multiple levels of nested relations", async () => {
|
|
510
|
+
const users = await userModel
|
|
511
|
+
.where({ id: esc(testUserId) })
|
|
512
|
+
.findMany()
|
|
513
|
+
.with({
|
|
514
|
+
posts: {
|
|
515
|
+
comments: {
|
|
516
|
+
author: true,
|
|
517
|
+
},
|
|
518
|
+
},
|
|
519
|
+
});
|
|
520
|
+
|
|
521
|
+
expect(users).toBeArray();
|
|
522
|
+
const user = users[0]!;
|
|
523
|
+
expect(user).toBeDefined();
|
|
524
|
+
expect(user.posts).toBeArray();
|
|
525
|
+
if (user.posts.length > 0) {
|
|
526
|
+
const post = user.posts[0]!;
|
|
527
|
+
if (post && post.comments.length > 0) {
|
|
528
|
+
const comment = post.comments[0]!;
|
|
529
|
+
expect(comment).toBeDefined();
|
|
530
|
+
expect(comment.author).toBeDefined();
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
});
|
|
534
|
+
});
|
|
535
|
+
|
|
536
|
+
// -----------------------------------------------------------------------
|
|
537
|
+
// Comparison with raw Drizzle queries
|
|
538
|
+
// -----------------------------------------------------------------------
|
|
539
|
+
|
|
540
|
+
describe("comparison with raw drizzle", () => {
|
|
541
|
+
test("matches drizzle query.findFirst with relations", async () => {
|
|
542
|
+
const modelResult = await userModel
|
|
543
|
+
.where({ id: esc(testUserId) })
|
|
544
|
+
.findFirst()
|
|
545
|
+
.with({ posts: true });
|
|
546
|
+
|
|
547
|
+
const drizzleAll = await db.query.user.findMany({
|
|
548
|
+
with: {
|
|
549
|
+
posts: true,
|
|
550
|
+
},
|
|
551
|
+
});
|
|
552
|
+
const drizzleResult = drizzleAll.find((u) => u.id === testUserId);
|
|
553
|
+
|
|
554
|
+
expect(modelResult).toBeDefined();
|
|
555
|
+
expect(drizzleResult).toBeDefined();
|
|
556
|
+
if (modelResult && drizzleResult) {
|
|
557
|
+
expect(modelResult.id).toBe(drizzleResult.id);
|
|
558
|
+
expect(modelResult.posts).toBeArray();
|
|
559
|
+
expect(drizzleResult.posts).toBeArray();
|
|
560
|
+
}
|
|
561
|
+
});
|
|
562
|
+
|
|
563
|
+
test("matches drizzle query.findMany with nested relations", async () => {
|
|
564
|
+
const modelResult = await userModel
|
|
565
|
+
.where({ id: esc(testUserId) })
|
|
566
|
+
.findMany()
|
|
567
|
+
.with({
|
|
568
|
+
posts: {
|
|
569
|
+
comments: true,
|
|
570
|
+
},
|
|
571
|
+
});
|
|
572
|
+
|
|
573
|
+
const drizzleAll = await db.query.user.findMany({
|
|
574
|
+
with: {
|
|
575
|
+
posts: {
|
|
576
|
+
with: {
|
|
577
|
+
comments: true,
|
|
578
|
+
},
|
|
579
|
+
},
|
|
580
|
+
},
|
|
581
|
+
});
|
|
582
|
+
const drizzleResult = drizzleAll.find((u) => u.id === testUserId);
|
|
583
|
+
|
|
584
|
+
expect(modelResult.length).toBeGreaterThan(0);
|
|
585
|
+
expect(drizzleResult).toBeDefined();
|
|
586
|
+
if (modelResult.length > 0 && drizzleResult) {
|
|
587
|
+
const modelUser = modelResult[0]!;
|
|
588
|
+
expect(modelUser).toBeDefined();
|
|
589
|
+
expect(modelUser.posts.length).toBe(drizzleResult.posts.length);
|
|
590
|
+
}
|
|
591
|
+
});
|
|
592
|
+
});
|
|
593
|
+
});
|
|
@@ -18,7 +18,7 @@ describe("upsert", () => {
|
|
|
18
18
|
.upsert({
|
|
19
19
|
insert: { name: "Upsert new", email, age: 25 },
|
|
20
20
|
update: { name: "Should not apply" },
|
|
21
|
-
target: schema.user.email
|
|
21
|
+
target: schema.user.email,
|
|
22
22
|
})
|
|
23
23
|
.return()) as any[];
|
|
24
24
|
|
|
@@ -88,14 +88,14 @@ describe("upsert", () => {
|
|
|
88
88
|
test("return > omit — removes specified fields", async () => {
|
|
89
89
|
const email = `upsert-omit-${uid()}@test.com`;
|
|
90
90
|
|
|
91
|
-
const [row] =
|
|
91
|
+
const [row] = await userModel
|
|
92
92
|
.upsert({
|
|
93
93
|
insert: { name: "Omit upsert", email, age: 40, secretField: 99 },
|
|
94
94
|
update: { name: "Omit updated" },
|
|
95
95
|
target: schema.user.email as any,
|
|
96
96
|
})
|
|
97
97
|
.return()
|
|
98
|
-
.omit({ age: true, secretField: true })
|
|
98
|
+
.omit({ age: true, secretField: true });
|
|
99
99
|
|
|
100
100
|
expect(row.age).toBeUndefined();
|
|
101
101
|
expect(row.secretField).toBeUndefined();
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { esc, modelBuilder } from "src/model";
|
|
2
|
+
import { db } from "../db";
|
|
3
|
+
import { relations } from "../relations";
|
|
4
|
+
import * as schema from "../schema";
|
|
5
|
+
|
|
6
|
+
const model = modelBuilder({
|
|
7
|
+
schema,
|
|
8
|
+
db,
|
|
9
|
+
relations,
|
|
10
|
+
dialect: "PostgreSQL",
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
// create model
|
|
14
|
+
const userModel = model("user", {});
|
|
15
|
+
|
|
16
|
+
await userModel
|
|
17
|
+
.where({
|
|
18
|
+
name: {
|
|
19
|
+
like: "A%",
|
|
20
|
+
},
|
|
21
|
+
})
|
|
22
|
+
.findFirst();
|
|
23
|
+
|
|
24
|
+
await userModel
|
|
25
|
+
.where({
|
|
26
|
+
name: esc.like("A%"),
|
|
27
|
+
})
|
|
28
|
+
.findFirst();
|