@teamkeel/functions-runtime 0.334.0 → 0.335.0
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/package.json +2 -2
- package/src/ModelAPI.js +52 -17
- package/src/ModelAPI.test.js +243 -37
- package/src/QueryBuilder.js +50 -5
- package/src/applyAdditionalQueryConstraints.js +22 -0
- package/src/database.js +8 -0
- package/src/tracing.js +5 -0
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@teamkeel/functions-runtime",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.335.0",
|
|
4
4
|
"description": "Internal package used by @teamkeel/sdk",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"scripts": {
|
|
7
|
-
"test": "vitest run --reporter verbose --threads false",
|
|
7
|
+
"test": "DEBUG=true vitest run --reporter verbose --threads false",
|
|
8
8
|
"format": "npx prettier --write src/**/*.js"
|
|
9
9
|
},
|
|
10
10
|
"keywords": [],
|
package/src/ModelAPI.js
CHANGED
|
@@ -3,6 +3,11 @@ const { QueryBuilder } = require("./QueryBuilder");
|
|
|
3
3
|
const { QueryContext } = require("./QueryContext");
|
|
4
4
|
const { applyWhereConditions } = require("./applyWhereConditions");
|
|
5
5
|
const { applyJoins } = require("./applyJoins");
|
|
6
|
+
const {
|
|
7
|
+
applyLimit,
|
|
8
|
+
applyOffset,
|
|
9
|
+
applyOrderBy,
|
|
10
|
+
} = require("./applyAdditionalQueryConstraints");
|
|
6
11
|
const {
|
|
7
12
|
camelCaseObject,
|
|
8
13
|
snakeCaseObject,
|
|
@@ -50,7 +55,7 @@ class ModelAPI {
|
|
|
50
55
|
}
|
|
51
56
|
|
|
52
57
|
async create(values) {
|
|
53
|
-
const name =
|
|
58
|
+
const name = tracing.spanNameForModelAPI(this._modelName, "create");
|
|
54
59
|
const db = getDatabase();
|
|
55
60
|
|
|
56
61
|
return tracing.withSpan(name, async (span) => {
|
|
@@ -77,7 +82,7 @@ class ModelAPI {
|
|
|
77
82
|
}
|
|
78
83
|
|
|
79
84
|
async findOne(where = {}) {
|
|
80
|
-
const name =
|
|
85
|
+
const name = tracing.spanNameForModelAPI(this._modelName, "findOne");
|
|
81
86
|
const db = getDatabase();
|
|
82
87
|
|
|
83
88
|
return tracing.withSpan(name, async (span) => {
|
|
@@ -101,21 +106,55 @@ class ModelAPI {
|
|
|
101
106
|
});
|
|
102
107
|
}
|
|
103
108
|
|
|
104
|
-
async findMany(
|
|
105
|
-
const name =
|
|
109
|
+
async findMany(params) {
|
|
110
|
+
const name = tracing.spanNameForModelAPI(this._modelName, "findMany");
|
|
106
111
|
const db = getDatabase();
|
|
112
|
+
const where = params?.where || {};
|
|
107
113
|
|
|
108
114
|
return tracing.withSpan(name, async (span) => {
|
|
115
|
+
const context = new QueryContext([this._tableName], this._tableConfigMap);
|
|
116
|
+
|
|
109
117
|
let builder = db
|
|
110
|
-
.selectFrom(
|
|
111
|
-
|
|
112
|
-
|
|
118
|
+
.selectFrom((qb) => {
|
|
119
|
+
// We need to wrap this query as a sub query in the selectFrom because you cannot apply a different order by column when using distinct(id)
|
|
120
|
+
let builder = qb
|
|
121
|
+
.selectFrom(this._tableName)
|
|
122
|
+
.distinctOn(`${this._tableName}.id`)
|
|
123
|
+
.selectAll(this._tableName);
|
|
113
124
|
|
|
114
|
-
|
|
125
|
+
builder = applyJoins(context, builder, where);
|
|
126
|
+
builder = applyWhereConditions(context, builder, where);
|
|
115
127
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
128
|
+
builder = builder.as(this._tableName);
|
|
129
|
+
|
|
130
|
+
return builder;
|
|
131
|
+
})
|
|
132
|
+
.selectAll();
|
|
133
|
+
|
|
134
|
+
// The only constraints added to the main query are the orderBy, limit and offset as they are performed on the "outer" set
|
|
135
|
+
if (params?.limit) {
|
|
136
|
+
builder = applyLimit(context, builder, params.limit);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (params?.offset) {
|
|
140
|
+
builder = applyOffset(context, builder, params.offset);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (
|
|
144
|
+
params?.orderBy !== undefined &&
|
|
145
|
+
Object.keys(params?.orderBy).length > 0
|
|
146
|
+
) {
|
|
147
|
+
builder = applyOrderBy(
|
|
148
|
+
context,
|
|
149
|
+
builder,
|
|
150
|
+
this._tableName,
|
|
151
|
+
params.orderBy
|
|
152
|
+
);
|
|
153
|
+
} else {
|
|
154
|
+
builder = builder.orderBy(`${this._tableName}.id`);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const query = builder;
|
|
119
158
|
|
|
120
159
|
span.setAttribute("sql", query.compile().sql);
|
|
121
160
|
const rows = await builder.execute();
|
|
@@ -124,7 +163,7 @@ class ModelAPI {
|
|
|
124
163
|
}
|
|
125
164
|
|
|
126
165
|
async update(where, values) {
|
|
127
|
-
const name =
|
|
166
|
+
const name = tracing.spanNameForModelAPI(this._modelName, "update");
|
|
128
167
|
const db = getDatabase();
|
|
129
168
|
|
|
130
169
|
return tracing.withSpan(name, async (span) => {
|
|
@@ -149,7 +188,7 @@ class ModelAPI {
|
|
|
149
188
|
}
|
|
150
189
|
|
|
151
190
|
async delete(where) {
|
|
152
|
-
const name =
|
|
191
|
+
const name = tracing.spanNameForModelAPI(this._modelName, "delete");
|
|
153
192
|
const db = getDatabase();
|
|
154
193
|
|
|
155
194
|
return tracing.withSpan(name, async (span) => {
|
|
@@ -187,10 +226,6 @@ class ModelAPI {
|
|
|
187
226
|
}
|
|
188
227
|
}
|
|
189
228
|
|
|
190
|
-
function spanName(modelName, action) {
|
|
191
|
-
return `Database ${modelName}.${action}`;
|
|
192
|
-
}
|
|
193
|
-
|
|
194
229
|
module.exports = {
|
|
195
230
|
ModelAPI,
|
|
196
231
|
DatabaseError,
|
package/src/ModelAPI.test.js
CHANGED
|
@@ -163,7 +163,7 @@ test("ModelAPI.findOne - return null if not found", async () => {
|
|
|
163
163
|
});
|
|
164
164
|
|
|
165
165
|
test("ModelAPI.findMany", async () => {
|
|
166
|
-
|
|
166
|
+
await personAPI.create({
|
|
167
167
|
name: "Jim",
|
|
168
168
|
married: false,
|
|
169
169
|
favouriteNumber: 10,
|
|
@@ -179,14 +179,16 @@ test("ModelAPI.findMany", async () => {
|
|
|
179
179
|
favouriteNumber: 12,
|
|
180
180
|
});
|
|
181
181
|
const rows = await personAPI.findMany({
|
|
182
|
-
|
|
182
|
+
where: {
|
|
183
|
+
married: true,
|
|
184
|
+
},
|
|
183
185
|
});
|
|
184
186
|
expect(rows.length).toEqual(2);
|
|
185
187
|
expect(rows.map((x) => x.id).sort()).toEqual([bob.id, sally.id].sort());
|
|
186
188
|
});
|
|
187
189
|
|
|
188
190
|
test("ModelAPI.findMany - no where conditions", async () => {
|
|
189
|
-
|
|
191
|
+
await personAPI.create({
|
|
190
192
|
name: "Jim",
|
|
191
193
|
});
|
|
192
194
|
await personAPI.create({
|
|
@@ -206,8 +208,10 @@ test("ModelAPI.findMany - startsWith", async () => {
|
|
|
206
208
|
name: "Bob",
|
|
207
209
|
});
|
|
208
210
|
const rows = await personAPI.findMany({
|
|
209
|
-
|
|
210
|
-
|
|
211
|
+
where: {
|
|
212
|
+
name: {
|
|
213
|
+
startsWith: "Ji",
|
|
214
|
+
},
|
|
211
215
|
},
|
|
212
216
|
});
|
|
213
217
|
expect(rows.length).toEqual(1);
|
|
@@ -222,8 +226,10 @@ test("ModelAPI.findMany - endsWith", async () => {
|
|
|
222
226
|
name: "Bob",
|
|
223
227
|
});
|
|
224
228
|
const rows = await personAPI.findMany({
|
|
225
|
-
|
|
226
|
-
|
|
229
|
+
where: {
|
|
230
|
+
name: {
|
|
231
|
+
endsWith: "im",
|
|
232
|
+
},
|
|
227
233
|
},
|
|
228
234
|
});
|
|
229
235
|
expect(rows.length).toEqual(1);
|
|
@@ -241,8 +247,10 @@ test("ModelAPI.findMany - contains", async () => {
|
|
|
241
247
|
name: "Jim",
|
|
242
248
|
});
|
|
243
249
|
const rows = await personAPI.findMany({
|
|
244
|
-
|
|
245
|
-
|
|
250
|
+
where: {
|
|
251
|
+
name: {
|
|
252
|
+
contains: "ll",
|
|
253
|
+
},
|
|
246
254
|
},
|
|
247
255
|
});
|
|
248
256
|
expect(rows.length).toEqual(2);
|
|
@@ -260,8 +268,10 @@ test("ModelAPI.findMany - oneOf", async () => {
|
|
|
260
268
|
name: "Jim",
|
|
261
269
|
});
|
|
262
270
|
const rows = await personAPI.findMany({
|
|
263
|
-
|
|
264
|
-
|
|
271
|
+
where: {
|
|
272
|
+
name: {
|
|
273
|
+
oneOf: ["Billy", "Sally"],
|
|
274
|
+
},
|
|
265
275
|
},
|
|
266
276
|
});
|
|
267
277
|
expect(rows.length).toEqual(2);
|
|
@@ -276,8 +286,10 @@ test("ModelAPI.findMany - greaterThan", async () => {
|
|
|
276
286
|
favouriteNumber: 2,
|
|
277
287
|
});
|
|
278
288
|
const rows = await personAPI.findMany({
|
|
279
|
-
|
|
280
|
-
|
|
289
|
+
where: {
|
|
290
|
+
favouriteNumber: {
|
|
291
|
+
greaterThan: 1,
|
|
292
|
+
},
|
|
281
293
|
},
|
|
282
294
|
});
|
|
283
295
|
expect(rows.length).toEqual(1);
|
|
@@ -295,8 +307,10 @@ test("ModelAPI.findMany - greaterThanOrEquals", async () => {
|
|
|
295
307
|
favouriteNumber: 3,
|
|
296
308
|
});
|
|
297
309
|
const rows = await personAPI.findMany({
|
|
298
|
-
|
|
299
|
-
|
|
310
|
+
where: {
|
|
311
|
+
favouriteNumber: {
|
|
312
|
+
greaterThanOrEquals: 2,
|
|
313
|
+
},
|
|
300
314
|
},
|
|
301
315
|
});
|
|
302
316
|
expect(rows.length).toEqual(2);
|
|
@@ -311,8 +325,10 @@ test("ModelAPI.findMany - lessThan", async () => {
|
|
|
311
325
|
favouriteNumber: 2,
|
|
312
326
|
});
|
|
313
327
|
const rows = await personAPI.findMany({
|
|
314
|
-
|
|
315
|
-
|
|
328
|
+
where: {
|
|
329
|
+
favouriteNumber: {
|
|
330
|
+
lessThan: 2,
|
|
331
|
+
},
|
|
316
332
|
},
|
|
317
333
|
});
|
|
318
334
|
expect(rows.length).toEqual(1);
|
|
@@ -330,8 +346,10 @@ test("ModelAPI.findMany - lessThanOrEquals", async () => {
|
|
|
330
346
|
favouriteNumber: 3,
|
|
331
347
|
});
|
|
332
348
|
const rows = await personAPI.findMany({
|
|
333
|
-
|
|
334
|
-
|
|
349
|
+
where: {
|
|
350
|
+
favouriteNumber: {
|
|
351
|
+
lessThanOrEquals: 2,
|
|
352
|
+
},
|
|
335
353
|
},
|
|
336
354
|
});
|
|
337
355
|
expect(rows.length).toEqual(2);
|
|
@@ -346,8 +364,10 @@ test("ModelAPI.findMany - before", async () => {
|
|
|
346
364
|
date: new Date("2022-01-02"),
|
|
347
365
|
});
|
|
348
366
|
const rows = await personAPI.findMany({
|
|
349
|
-
|
|
350
|
-
|
|
367
|
+
where: {
|
|
368
|
+
date: {
|
|
369
|
+
before: new Date("2022-01-02"),
|
|
370
|
+
},
|
|
351
371
|
},
|
|
352
372
|
});
|
|
353
373
|
expect(rows.length).toEqual(1);
|
|
@@ -387,14 +407,156 @@ test("ModelAPI.findMany - onOrBefore", async () => {
|
|
|
387
407
|
date: new Date("2022-01-03"),
|
|
388
408
|
});
|
|
389
409
|
const rows = await personAPI.findMany({
|
|
390
|
-
|
|
391
|
-
|
|
410
|
+
where: {
|
|
411
|
+
date: {
|
|
412
|
+
onOrBefore: new Date("2022-01-02"),
|
|
413
|
+
},
|
|
392
414
|
},
|
|
393
415
|
});
|
|
394
416
|
expect(rows.length).toEqual(2);
|
|
395
417
|
expect(rows.map((x) => x.id).sort()).toEqual([p.id, p2.id].sort());
|
|
396
418
|
});
|
|
397
419
|
|
|
420
|
+
test("ModelAPI.findMany - limit", async () => {
|
|
421
|
+
await personAPI.create({
|
|
422
|
+
id: "1",
|
|
423
|
+
name: "Jim",
|
|
424
|
+
married: false,
|
|
425
|
+
favouriteNumber: 10,
|
|
426
|
+
});
|
|
427
|
+
await personAPI.create({
|
|
428
|
+
id: "2",
|
|
429
|
+
name: "Bob",
|
|
430
|
+
married: true,
|
|
431
|
+
favouriteNumber: 11,
|
|
432
|
+
});
|
|
433
|
+
await personAPI.create({
|
|
434
|
+
id: "3",
|
|
435
|
+
name: "Sally",
|
|
436
|
+
married: true,
|
|
437
|
+
favouriteNumber: 12,
|
|
438
|
+
});
|
|
439
|
+
|
|
440
|
+
const rows = await personAPI.findMany({
|
|
441
|
+
limit: 2,
|
|
442
|
+
});
|
|
443
|
+
|
|
444
|
+
expect(rows.map((r) => r.name)).toEqual(["Jim", "Bob"]);
|
|
445
|
+
});
|
|
446
|
+
|
|
447
|
+
test("ModelAPI.findMany - orderBy", async () => {
|
|
448
|
+
await personAPI.create({
|
|
449
|
+
id: "1",
|
|
450
|
+
name: "Jim",
|
|
451
|
+
married: false,
|
|
452
|
+
favouriteNumber: 10,
|
|
453
|
+
date: new Date(2023, 12, 29),
|
|
454
|
+
});
|
|
455
|
+
await personAPI.create({
|
|
456
|
+
id: "2",
|
|
457
|
+
name: "Bob",
|
|
458
|
+
married: true,
|
|
459
|
+
favouriteNumber: 11,
|
|
460
|
+
date: new Date(2023, 12, 30),
|
|
461
|
+
});
|
|
462
|
+
await personAPI.create({
|
|
463
|
+
id: "3",
|
|
464
|
+
name: "Sally",
|
|
465
|
+
married: true,
|
|
466
|
+
favouriteNumber: 12,
|
|
467
|
+
date: new Date(2023, 12, 31),
|
|
468
|
+
});
|
|
469
|
+
|
|
470
|
+
const ascendingNames = await personAPI.findMany({
|
|
471
|
+
orderBy: {
|
|
472
|
+
name: "asc",
|
|
473
|
+
},
|
|
474
|
+
});
|
|
475
|
+
|
|
476
|
+
expect(ascendingNames.map((r) => r.name)).toEqual(["Bob", "Jim", "Sally"]);
|
|
477
|
+
|
|
478
|
+
const descendingNames = await personAPI.findMany({
|
|
479
|
+
orderBy: {
|
|
480
|
+
name: "desc",
|
|
481
|
+
},
|
|
482
|
+
});
|
|
483
|
+
|
|
484
|
+
expect(descendingNames.map((r) => r.name)).toEqual(["Sally", "Jim", "Bob"]);
|
|
485
|
+
|
|
486
|
+
const ascendingFavouriteNumbers = await personAPI.findMany({
|
|
487
|
+
orderBy: {
|
|
488
|
+
favouriteNumber: "asc",
|
|
489
|
+
},
|
|
490
|
+
});
|
|
491
|
+
|
|
492
|
+
expect(ascendingFavouriteNumbers.map((r) => r.name)).toEqual([
|
|
493
|
+
"Jim",
|
|
494
|
+
"Bob",
|
|
495
|
+
"Sally",
|
|
496
|
+
]);
|
|
497
|
+
|
|
498
|
+
const descendingDates = await personAPI.findMany({
|
|
499
|
+
orderBy: {
|
|
500
|
+
date: "desc",
|
|
501
|
+
},
|
|
502
|
+
});
|
|
503
|
+
|
|
504
|
+
expect(descendingDates.map((r) => r.name)).toEqual(["Sally", "Bob", "Jim"]);
|
|
505
|
+
});
|
|
506
|
+
|
|
507
|
+
test("ModelAPI.findMany - offset", async () => {
|
|
508
|
+
await personAPI.create({
|
|
509
|
+
id: "1",
|
|
510
|
+
name: "Jim",
|
|
511
|
+
married: false,
|
|
512
|
+
favouriteNumber: 10,
|
|
513
|
+
date: new Date(2023, 12, 29),
|
|
514
|
+
});
|
|
515
|
+
await personAPI.create({
|
|
516
|
+
id: "2",
|
|
517
|
+
name: "Bob",
|
|
518
|
+
married: true,
|
|
519
|
+
favouriteNumber: 11,
|
|
520
|
+
date: new Date(2023, 12, 30),
|
|
521
|
+
});
|
|
522
|
+
await personAPI.create({
|
|
523
|
+
id: "3",
|
|
524
|
+
name: "Sally",
|
|
525
|
+
married: true,
|
|
526
|
+
favouriteNumber: 12,
|
|
527
|
+
date: new Date(2023, 12, 31),
|
|
528
|
+
});
|
|
529
|
+
|
|
530
|
+
const rows = await personAPI.findMany({
|
|
531
|
+
offset: 1,
|
|
532
|
+
limit: 2,
|
|
533
|
+
orderBy: {
|
|
534
|
+
name: "asc",
|
|
535
|
+
},
|
|
536
|
+
});
|
|
537
|
+
|
|
538
|
+
expect(rows.map((r) => r.name)).toEqual(["Jim", "Sally"]);
|
|
539
|
+
|
|
540
|
+
const rows2 = await personAPI.findMany({
|
|
541
|
+
offset: 2,
|
|
542
|
+
orderBy: {
|
|
543
|
+
name: "asc",
|
|
544
|
+
},
|
|
545
|
+
});
|
|
546
|
+
|
|
547
|
+
expect(rows2.map((r) => r.name)).toEqual(["Sally"]);
|
|
548
|
+
|
|
549
|
+
const rows3 = await personAPI.findMany({
|
|
550
|
+
offset: 1,
|
|
551
|
+
orderBy: {
|
|
552
|
+
name: "asc",
|
|
553
|
+
},
|
|
554
|
+
limit: 1,
|
|
555
|
+
});
|
|
556
|
+
|
|
557
|
+
expect(rows3.map((r) => r.name)).toEqual(["Jim"]);
|
|
558
|
+
});
|
|
559
|
+
|
|
398
560
|
test("ModelAPI.findMany - after", async () => {
|
|
399
561
|
await personAPI.create({
|
|
400
562
|
date: new Date("2022-01-01"),
|
|
@@ -403,8 +565,10 @@ test("ModelAPI.findMany - after", async () => {
|
|
|
403
565
|
date: new Date("2022-01-02"),
|
|
404
566
|
});
|
|
405
567
|
const rows = await personAPI.findMany({
|
|
406
|
-
|
|
407
|
-
|
|
568
|
+
where: {
|
|
569
|
+
date: {
|
|
570
|
+
after: new Date("2022-01-01"),
|
|
571
|
+
},
|
|
408
572
|
},
|
|
409
573
|
});
|
|
410
574
|
expect(rows.length).toEqual(1);
|
|
@@ -422,8 +586,10 @@ test("ModelAPI.findMany - onOrAfter", async () => {
|
|
|
422
586
|
date: new Date("2022-01-03"),
|
|
423
587
|
});
|
|
424
588
|
const rows = await personAPI.findMany({
|
|
425
|
-
|
|
426
|
-
|
|
589
|
+
where: {
|
|
590
|
+
date: {
|
|
591
|
+
onOrAfter: new Date("2022-01-02"),
|
|
592
|
+
},
|
|
427
593
|
},
|
|
428
594
|
});
|
|
429
595
|
expect(rows.length).toEqual(2);
|
|
@@ -438,8 +604,10 @@ test("ModelAPI.findMany - equals", async () => {
|
|
|
438
604
|
name: "Sally",
|
|
439
605
|
});
|
|
440
606
|
const rows = await personAPI.findMany({
|
|
441
|
-
|
|
442
|
-
|
|
607
|
+
where: {
|
|
608
|
+
name: {
|
|
609
|
+
equals: "Jim",
|
|
610
|
+
},
|
|
443
611
|
},
|
|
444
612
|
});
|
|
445
613
|
expect(rows.length).toEqual(1);
|
|
@@ -454,8 +622,10 @@ test("ModelAPI.findMany - notEquals", async () => {
|
|
|
454
622
|
name: "Sally",
|
|
455
623
|
});
|
|
456
624
|
const rows = await personAPI.findMany({
|
|
457
|
-
|
|
458
|
-
|
|
625
|
+
where: {
|
|
626
|
+
name: {
|
|
627
|
+
notEquals: "Sally",
|
|
628
|
+
},
|
|
459
629
|
},
|
|
460
630
|
});
|
|
461
631
|
expect(rows.length).toEqual(1);
|
|
@@ -523,8 +693,10 @@ test("ModelAPI.findMany - relationships - one to many", async () => {
|
|
|
523
693
|
});
|
|
524
694
|
|
|
525
695
|
const posts = await postAPI.findMany({
|
|
526
|
-
|
|
527
|
-
|
|
696
|
+
where: {
|
|
697
|
+
author: {
|
|
698
|
+
name: "Jim",
|
|
699
|
+
},
|
|
528
700
|
},
|
|
529
701
|
});
|
|
530
702
|
expect(posts.length).toEqual(2);
|
|
@@ -549,10 +721,12 @@ test("ModelAPI.findMany - relationships - many to one", async () => {
|
|
|
549
721
|
});
|
|
550
722
|
|
|
551
723
|
const people = await personAPI.findMany({
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
724
|
+
where: {
|
|
725
|
+
posts: {
|
|
726
|
+
title: {
|
|
727
|
+
startsWith: "My ",
|
|
728
|
+
endsWith: " Post",
|
|
729
|
+
},
|
|
556
730
|
},
|
|
557
731
|
},
|
|
558
732
|
});
|
|
@@ -666,3 +840,35 @@ test("ModelAPI.delete", async () => {
|
|
|
666
840
|
expect(deletedId).toEqual(id);
|
|
667
841
|
await expect(personAPI.findOne({ id })).resolves.toEqual(null);
|
|
668
842
|
});
|
|
843
|
+
|
|
844
|
+
test("ModelAPI chained findMany with offset/limit/order by", async () => {
|
|
845
|
+
await postAPI.create({
|
|
846
|
+
title: "adam",
|
|
847
|
+
});
|
|
848
|
+
await postAPI.create({
|
|
849
|
+
title: "dave",
|
|
850
|
+
});
|
|
851
|
+
const three = await postAPI.create({
|
|
852
|
+
title: "jon",
|
|
853
|
+
});
|
|
854
|
+
const four = await postAPI.create({
|
|
855
|
+
title: "jon bretman",
|
|
856
|
+
});
|
|
857
|
+
|
|
858
|
+
const results = await postAPI
|
|
859
|
+
.where({ title: { equals: "adam" } })
|
|
860
|
+
.orWhere({
|
|
861
|
+
title: { startsWith: "jon" },
|
|
862
|
+
})
|
|
863
|
+
.findMany({
|
|
864
|
+
limit: 3,
|
|
865
|
+
offset: 1,
|
|
866
|
+
orderBy: {
|
|
867
|
+
title: "asc",
|
|
868
|
+
},
|
|
869
|
+
});
|
|
870
|
+
|
|
871
|
+
// because we've offset by 1, adam should not appear in the results even though
|
|
872
|
+
// the query constraints match adam
|
|
873
|
+
expect(results).toEqual([three, four]);
|
|
874
|
+
});
|
package/src/QueryBuilder.js
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
const { applyWhereConditions } = require("./applyWhereConditions");
|
|
2
|
+
const {
|
|
3
|
+
applyLimit,
|
|
4
|
+
applyOffset,
|
|
5
|
+
applyOrderBy,
|
|
6
|
+
} = require("./applyAdditionalQueryConstraints");
|
|
2
7
|
const { applyJoins } = require("./applyJoins");
|
|
3
8
|
const { camelCaseObject, upperCamelCase } = require("./casing");
|
|
9
|
+
const { getDatabase } = require("./database");
|
|
10
|
+
const { QueryContext } = require("./QueryContext");
|
|
4
11
|
const tracing = require("./tracing");
|
|
5
12
|
|
|
6
13
|
class QueryBuilder {
|
|
@@ -36,12 +43,50 @@ class QueryBuilder {
|
|
|
36
43
|
return new QueryBuilder(this._tableName, context, builder);
|
|
37
44
|
}
|
|
38
45
|
|
|
39
|
-
async findMany() {
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
46
|
+
async findMany(params) {
|
|
47
|
+
const name = tracing.spanNameForModelAPI(this._modelName, "findMany");
|
|
48
|
+
const db = getDatabase();
|
|
49
|
+
|
|
50
|
+
return tracing.withSpan(name, async (span) => {
|
|
51
|
+
const context = new QueryContext([this._tableName], this._tableConfigMap);
|
|
52
|
+
|
|
53
|
+
let builder = db
|
|
54
|
+
.selectFrom((qb) => {
|
|
55
|
+
// this._db contains all of the where constraints and joins
|
|
56
|
+
// we want to include that in the sub query in the same way we
|
|
57
|
+
// add all of this information into the sub query in the ModelAPI's
|
|
58
|
+
// implementation of findMany
|
|
59
|
+
return this._db.as(this._tableName);
|
|
60
|
+
})
|
|
61
|
+
.selectAll();
|
|
62
|
+
|
|
63
|
+
// The only constraints added to the main query are the orderBy, limit and offset as they are performed on the "outer" set
|
|
64
|
+
if (params?.limit) {
|
|
65
|
+
builder = applyLimit(context, builder, params.limit);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (params?.offset) {
|
|
69
|
+
builder = applyOffset(context, builder, params.offset);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (
|
|
73
|
+
params?.orderBy !== undefined &&
|
|
74
|
+
Object.keys(params?.orderBy).length > 0
|
|
75
|
+
) {
|
|
76
|
+
builder = applyOrderBy(
|
|
77
|
+
context,
|
|
78
|
+
builder,
|
|
79
|
+
this._tableName,
|
|
80
|
+
params.orderBy
|
|
81
|
+
);
|
|
82
|
+
} else {
|
|
83
|
+
builder = builder.orderBy(`${this._tableName}.id`);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const query = builder;
|
|
87
|
+
|
|
43
88
|
span.setAttribute("sql", query.compile().sql);
|
|
44
|
-
const rows = await
|
|
89
|
+
const rows = await builder.execute();
|
|
45
90
|
return rows.map((x) => camelCaseObject(x));
|
|
46
91
|
});
|
|
47
92
|
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
const { snakeCase } = require("change-case");
|
|
2
|
+
|
|
3
|
+
function applyLimit(context, qb, limit) {
|
|
4
|
+
return qb.limit(limit);
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
function applyOffset(context, qb, offset) {
|
|
8
|
+
return qb.offset(offset);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function applyOrderBy(context, qb, tableName, orderBy = {}) {
|
|
12
|
+
Object.entries(orderBy).forEach(([key, sortOrder]) => {
|
|
13
|
+
qb = qb.orderBy(`${tableName}.${snakeCase(key)}`, sortOrder);
|
|
14
|
+
});
|
|
15
|
+
return qb;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
module.exports = {
|
|
19
|
+
applyLimit,
|
|
20
|
+
applyOffset,
|
|
21
|
+
applyOrderBy,
|
|
22
|
+
};
|
package/src/database.js
CHANGED
|
@@ -63,6 +63,14 @@ function getDatabase() {
|
|
|
63
63
|
|
|
64
64
|
db = new Kysely({
|
|
65
65
|
dialect: getDialect(),
|
|
66
|
+
log(event) {
|
|
67
|
+
if ("DEBUG" in process.env) {
|
|
68
|
+
if (event.level === "query") {
|
|
69
|
+
console.log(event.query.sql);
|
|
70
|
+
console.log(event.query.parameters);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
},
|
|
66
74
|
});
|
|
67
75
|
|
|
68
76
|
return db;
|
package/src/tracing.js
CHANGED
|
@@ -73,8 +73,13 @@ function getTracer() {
|
|
|
73
73
|
return opentelemetry.trace.getTracer("functions");
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
+
function spanNameForModelAPI(modelName, action) {
|
|
77
|
+
return `Database ${modelName}.${action}`;
|
|
78
|
+
}
|
|
79
|
+
|
|
76
80
|
module.exports = {
|
|
77
81
|
getTracer,
|
|
78
82
|
withSpan,
|
|
79
83
|
init,
|
|
84
|
+
spanNameForModelAPI,
|
|
80
85
|
};
|