prostgles-server 4.2.8 → 4.2.10

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.
@@ -1,1194 +0,0 @@
1
- import { strict as assert } from 'assert';
2
- import * as fs from "fs";
3
- import { DBOFullyTyped } from "../dist/DBSchemaBuilder";
4
- import type { DBHandlerClient } from "./client/index";
5
-
6
- export async function tryRun(desc: string, func: () => any, log?: Function){
7
- try {
8
- await func();
9
- } catch(err) {
10
- console.error(desc + " FAILED:", err);
11
- log?.("FAIL: ", err);
12
- console.trace(err)
13
- await tout(50);
14
- throw err;
15
- }
16
- }
17
- export function tryRunP(desc: string, func: (resolve: any, reject: any) => any, opts?: { log?: Function; timeout?: number; }){
18
- return new Promise(async (rv, rj) => {
19
- const testTimeout = Number.isFinite(opts?.timeout)? setTimeout(() => {
20
- const errMsg = `${desc} failed. Reason: Timout reached: ${opts!.timeout}ms`;
21
- opts?.log?.(errMsg);
22
- rj(errMsg);
23
- }, opts!.timeout) : undefined
24
- try {
25
- await func(rv, rj);
26
- clearTimeout(testTimeout);
27
- } catch(err: any){
28
- opts?.log?.(`${desc} failed: ` + JSON.stringify(err));
29
- rj(err);
30
- await tout(50);
31
- throw err;
32
- }
33
- });
34
- }
35
-
36
- export default async function isomorphic(db: DBOFullyTyped | DBHandlerClient, log: (msg: string, extra?: any) => void) {
37
- log("Starting isomorphic queries");
38
-
39
- const itemsCount = await db.items.count!()
40
- if(itemsCount){
41
- log("DELETING items");
42
-
43
- /* Access controlled */
44
- await db.items4.delete!({ });
45
-
46
- await db.items4_pub.delete!({ });
47
- await db.items3.delete!({ });
48
- await db.items2.delete!({ });
49
- await db.items.delete!({ });
50
- }
51
- await db.sql!(`TRUNCATE items RESTART IDENTITY CASCADE;`)
52
-
53
-
54
- await tryRun("Prepare data", async () => {
55
- if(!db.sql) throw "db.sql missing";
56
- const res = await db.items.insert!([{ name: "a" }, { name: "a" }, { name: "b" }], { returning: "*" });
57
- assert.equal(res.length, 3);
58
- const added1 = '04 Dec 1995 00:12:00';
59
- const added2 = '04 Dec 1996 00:12:00';
60
- const added3 = '04 Dec 1997 00:12:00';
61
-
62
- await db.items2.insert!([{ name: "a", items_id: res[0]!.id }]);
63
- await db.items3.insert!([{ name: "a" }, { name: "za123" }]);
64
- await db.items4.insert!([
65
- { name: "abc1", public: "public data", added: added1 },
66
- { name: "abc2", public: "public data", added: added1 },
67
- { name: "abcd", public: "public data d", added: added2 }
68
- ]);
69
- await db[`prostgles_test.basic1`].insert!({
70
- id_basic: { txt: "basic" },
71
- txt: "basic1"
72
- });
73
- await db.sql(`REFRESH MATERIALIZED VIEW prostgles_test.mv_basic1;`);
74
- assert.deepStrictEqual(await db["prostgles_test.mv_basic1"].find!(), await db["prostgles_test.basic1"].find!())
75
-
76
- /* Ensure */
77
- await db[`"*"`].insert!([{ "*": "a" }, { "*": "a" }, { "*": "b" }]);
78
- await db[`"""*"""`].insert!([{ [`"*"`]: "a" }, { [`"*"`]: "a" }, { [`"*"`]: "b" }]);
79
-
80
- await db.various.insert!([
81
- { name: "abc9", added: added1, jsn: { "a": { "b": 2 } } },
82
- { name: "abc1", added: added2, jsn: { "a": { "b": 3 } } },
83
- { name: "abc81 here", added: added3, jsn: { "a": { "b": 2 } } }
84
- ]);
85
-
86
- await db.sql("TRUNCATE files CASCADE");
87
- });
88
- const json = { a: true, arr: "2", arr1: 3, arr2: [1], arrStr: ["1123.string"] }
89
- await tryRun("merge json", async () => {
90
- const inserted = await db.tjson.insert!({ colOneOf: "a", json }, { returning: "*" });
91
- const res = await db.tjson.update!({ colOneOf: "a" },{ json: { $merge: [{ a: false }] } }, { returning: "*" });
92
- assert.deepStrictEqual(res[0].json, { ...json, a: false });
93
- });
94
-
95
- await tryRun("onConflict do update", async () => {
96
- const initial = await db.items4.insert!({ id: -99, name: "onConflict", public: "onConflict" }, { returning: "*" });
97
- const updated = await db.items4.insert!({ id: -99, name: "onConflict", public: "onConflict2" }, { onConflict: "DoUpdate", returning: "*" });
98
- assert.equal(initial.id, -99);
99
- assert.equal(initial.public, "onConflict");
100
- assert.equal(updated.id, -99);
101
- assert.equal(updated.public, "onConflict2");
102
- await db.items4.delete!({ id: -99 });
103
- });
104
-
105
- const fileFolder = `${__dirname}/../../server/dist/server/media/`;
106
- const fileName = "sample_file.txt";
107
- await tryRun("Local file upload", async () => {
108
- let str = "This is a string",
109
- data = Buffer.from(str, "utf-8"),
110
- mediaFile = { data, name: fileName }
111
-
112
- const file = await db.files.insert!(mediaFile, { returning: "*" });
113
- const _data = fs.readFileSync(fileFolder + file.name);
114
- assert.equal(str, _data.toString('utf8'));
115
-
116
- await tryRun("Nested insert", async () => {
117
-
118
- const nestedInsert = await db.users_public_info.insert!({ name: "somename.txt", avatar: mediaFile }, { returning: "*" });
119
- const { name, avatar } = nestedInsert;
120
- const { extension, content_type, original_name } = avatar;
121
- assert.deepStrictEqual(
122
- { extension, content_type, original_name },
123
- {
124
- extension: 'txt',
125
- content_type: 'text/plain',
126
- original_name: 'sample_file.txt',
127
- }
128
- );
129
-
130
- assert.equal(name, "somename.txt");
131
- });
132
-
133
- });
134
-
135
-
136
- await tryRun("Local file delete", async () => {
137
- const file = {
138
- data: Buffer.from("str", "utf-8"),
139
- name: "will delete.txt"
140
- }
141
- await db.files.insert!(file);
142
-
143
- const files = await db.files.find!({ original_name: file.name });
144
- assert.equal(files.length, 1);
145
- const exists0 = fs.existsSync(fileFolder+files[0].name);
146
- assert.equal(exists0, true);
147
- await db.files.delete!({ original_name: file.name }, { returning: "*" });
148
- const exists = fs.existsSync(fileFolder+files[0].name);
149
- assert.equal(exists, false);
150
- })
151
-
152
- await tryRun("Local file update", async () => {
153
- const initialStr = "str";
154
- const newStr = "str new";
155
- const file = {
156
- data: Buffer.from(initialStr, "utf-8"),
157
- name: "will update.txt"
158
- }
159
- const newFile = {
160
- data: Buffer.from(newStr, "utf-8"),
161
- name: "will update new.txt"
162
- }
163
- await db.files.insert!(file);
164
- const originals = await db.files.find!({ original_name: file.name });
165
- assert.equal(originals.length, 1);
166
- const [original] = originals;
167
- const initialFileStr = fs.readFileSync(fileFolder + original.name).toString('utf8');
168
- assert.equal(initialStr, initialFileStr);
169
-
170
- await db.files.update!({ id: original.id }, newFile);
171
-
172
- const newFileStr = fs.readFileSync(fileFolder + original.name).toString('utf8');
173
- assert.equal(newStr, newFileStr);
174
-
175
- const newF = await db.files.findOne!({ id: original.id });
176
-
177
- assert.equal(newF.original_name, newFile.name)
178
- });
179
-
180
- await tryRun("getColumns definition", async () => {
181
- const res = await db.tr2.getColumns!("fr");
182
- const expected = [
183
- {
184
- "label": "Id",
185
- "name": "id",
186
- "data_type": "integer",
187
- "udt_name": "int4",
188
- "element_type": null,
189
- is_updatable: true,
190
- "element_udt_name": null,
191
- "is_pkey": true,
192
- "column_default": null,
193
- "comment": null,
194
- "ordinal_position": 1,
195
- "is_nullable": false,
196
- "references": null,
197
- "has_default": true,
198
- "tsDataType": "number",
199
- "insert": true,
200
- "select": true,
201
- "orderBy": true,
202
- "filter": true,
203
- "update": true,
204
- "delete": true
205
- },
206
- {
207
- "label": "Tr1 id",
208
- "name": "tr1_id",
209
- "data_type": "integer",
210
- "udt_name": "int4",
211
- "element_type": null,
212
- is_updatable: true,
213
- "element_udt_name": null,
214
- "is_pkey": false,
215
- "column_default": null,
216
- "comment": null,
217
- "ordinal_position": 2,
218
- "is_nullable": true,
219
- "references": [{
220
- "ftable": "tr1",
221
- "fcols": [
222
- "id"
223
- ],
224
- "cols": [
225
- "tr1_id"
226
- ]
227
- }],
228
- "has_default": false,
229
- "tsDataType": "number",
230
- "insert": true,
231
- "select": true,
232
- "orderBy": true,
233
- "filter": true,
234
- "update": true,
235
- "delete": true
236
- },
237
- {
238
- "label": "fr_t1",
239
- hint: "hint...",
240
- min: "a",
241
- max: "b",
242
- "name": "t1",
243
- "data_type": "text",
244
- "udt_name": "text",
245
- "element_type": null,
246
- is_updatable: true,
247
- "element_udt_name": null,
248
- "is_pkey": false,
249
- "column_default": null,
250
- "comment": null,
251
- "ordinal_position": 3,
252
- "is_nullable": true,
253
- "references": null,
254
- "has_default": false,
255
- "tsDataType": "string",
256
- "insert": true,
257
- "select": true,
258
- "orderBy": true,
259
- "filter": true,
260
- "update": true,
261
- "delete": true
262
- },
263
- {
264
- "label": "en_t2",
265
- "name": "t2",
266
- "data_type": "text",
267
- "udt_name": "text",
268
- "element_type": null,
269
- is_updatable: true,
270
- "element_udt_name": null,
271
- "is_pkey": false,
272
- "column_default": null,
273
- "comment": null,
274
- "ordinal_position": 4,
275
- "is_nullable": true,
276
- "references": null,
277
- "has_default": false,
278
- "tsDataType": "string",
279
- "insert": true,
280
- "select": true,
281
- "orderBy": true,
282
- "filter": true,
283
- "update": true,
284
- "delete": true
285
- }
286
- ];
287
-
288
- assert.deepStrictEqual(
289
- res,
290
- expected
291
- );
292
- const resDynamic = await db.tr2.getColumns!("fr", { rule: "update", filter: {}, data: { t2: "a" } });
293
- assert.deepStrictEqual(
294
- resDynamic,
295
- expected
296
- );
297
- });
298
-
299
- await tryRun("returnType", async () => {
300
-
301
- const whereStatement = await db.tr1.find!({ t1: "a" }, { returnType: "statement-where" });
302
-
303
- assert.equal(whereStatement, `"t1" = 'a'`);
304
- })
305
-
306
- await tryRun("Table config triggers", async () => {
307
- const tr1 = await db.tr1.insert!({ })
308
- const tr2 = await db.tr2.insert!({
309
- tr1_id: 1,
310
- t1: "a",
311
- t2: "b"
312
- });
313
- try {
314
- await db.tr2.delete!();
315
- } catch(e){
316
-
317
- }
318
- const one = await db.tr2.findOne!({
319
- t1: "a",
320
- t2: "b"
321
- });
322
- if(!one) {
323
- throw "Row missing";
324
- }
325
- });
326
-
327
- await tryRun("$unnest_words", async () => {
328
- const res = await db.various.find!({}, { returnType: "values", select: { name: "$unnest_words" } });
329
-
330
- assert.deepStrictEqual( res, [
331
- 'abc9',
332
- 'abc1',
333
- 'abc81',
334
- 'here'
335
- ]);
336
- })
337
-
338
- /**
339
- * Group by/Distinct
340
- */
341
- await tryRun("Group by/Distinct", async () => {
342
- const res = await db.items.find!({}, { select: { name: 1 }, groupBy: true });
343
- const resV = await db.items.find!({}, { select: { name: 1 }, groupBy: true, returnType: "values" });
344
-
345
- assert.deepStrictEqual(
346
- res,
347
- [
348
- { name: 'a' },
349
- { name: 'b' },
350
- ]
351
- );
352
- assert.deepStrictEqual(
353
- resV,
354
- ["a", "b"]
355
- );
356
- })
357
-
358
- /**
359
- * returnType "value"
360
- */
361
- await tryRun("returnType: value", async () => {
362
- const resVl = await db.items.find!({}, { select: { name: { $array_agg: ["name"] } }, returnType: "value" });
363
-
364
- assert.deepStrictEqual(
365
- resVl,
366
- ["a", "a", "b"]
367
- );
368
-
369
- });
370
-
371
- /**
372
- * TODO -> ADD ALL FILTER TYPES
373
- */
374
- await tryRun("FTS filtering", async () => {
375
- const res = await db.various.count!({ "tsv.@@.to_tsquery": ["a"] });
376
- assert.equal(res, 0);
377
-
378
-
379
- const d = await db.various.findOne!(
380
- { "name.@@.to_tsquery": ["abc81"] },
381
- { select: {
382
- h: { "$ts_headline_simple": ["name", { plainto_tsquery: "abc81" }] },
383
- hh: { "$ts_headline": ["name", "abc81"] } ,
384
- added: "$year",
385
- addedY: { "$date": ["added"] }
386
- }});
387
- // console.log(d);
388
- await db.various.findOne!(
389
- { },
390
- { select: {
391
- h: { "$ts_headline_simple": ["name", { plainto_tsquery: "abc81" }] },
392
- hh: { "$ts_headline": ["name", "abc81"] } ,
393
- added: "$year",
394
- addedY: { "$date": ["added"] }
395
- }});
396
-
397
- /*
398
- * Dates become strings after reaching client.
399
- * Serialize col dataTypes and then recast ??
400
- */
401
- assert.deepStrictEqual(JSON.parse(JSON.stringify(d)), {
402
- h: '<b>abc81</b> here',
403
- hh: '<b>abc81</b> here',
404
- added: '1997',
405
- addedY: '1997-12-04',
406
- });
407
- });
408
-
409
- await tryRun("$term_highlight", async () => {
410
- const term = "abc81";
411
- const res = await db.various.find!(
412
- { "hIdx.>": -2 },
413
- { select: {
414
- h: { $term_highlight: [["name"], term, { }] },
415
- hFull: { $term_highlight: ["*", "81", { }] },
416
- hOrdered: { $term_highlight: [["name", "id"], "81", { }] },
417
- hIdx: { $term_highlight: [["name"], term, { returnType: "index" }] },
418
- hBool: { $term_highlight: [["name"], term, { returnType: "boolean" }] },
419
- hObj: { $term_highlight: [["name"], term, { returnType: "object" }] },
420
- hObjAll: { $term_highlight: ["*", term, { returnType: "object" }] },
421
- },
422
- orderBy: { hIdx: -1 }
423
- }
424
- );
425
-
426
- assert.deepStrictEqual(
427
- res[0],
428
- {
429
- h:["name: ",["abc81"]," here"],
430
-
431
- /* Search all allowed fields using "*" */
432
- hFull: [
433
- 'id: 3, h: , name: abc',
434
- [ '81' ],
435
- ' here, tsv: , jsn: {"a":{"b":2}}, added: 1997-12-04 00:12:00'
436
- ],
437
-
438
- /* Search specific fields in specific order */
439
- hOrdered:["name: abc",["81"]," here, id: 3"],
440
- hIdx: 6,
441
- hBool: true,
442
- hObj: {
443
- name: [
444
- '', ['abc81'],' here'
445
- ]
446
- },
447
- hObjAll: {
448
- name: [
449
- '', ['abc81'],' here'
450
- ]
451
- },
452
- }
453
- )
454
- });
455
-
456
- await tryRun("funcFilters: $term_highlight", async () => {
457
- const term = "abc81";
458
- const res = await db.various.count!(
459
- { $term_highlight: [["*"], term, { returnType: "boolean" }] }
460
- );
461
- assert.equal(+res, 1)
462
- });
463
-
464
- await tryRunP("subscribe", async (resolve, reject) => {
465
- await db.various.insert!({ id: 99 });
466
- const sub = await db.various.subscribe!({ id: 99 }, { }, async items => {
467
- const item = items[0];
468
-
469
- if(item && item.name === "zz3zz3"){
470
- await db.various.delete!({ name: "zz3zz3" });
471
- sub.unsubscribe();
472
- resolve(true)
473
- }
474
- });
475
- await db.various.update!({ id: 99 }, { name: "zz3zz1" });
476
- await db.various.update!({ id: 99 }, { name: "zz3zz2" });
477
- await db.various.update!({ id: 99 }, { name: "zz3zz3" });
478
- }, { timeout: 4000 });
479
-
480
- await tryRunP("subscribeOne with throttle", async (resolve, reject) => {
481
- await db.various.insert!({ id: 99 });
482
- const start = Date.now(); // name: "zz3zz"
483
- const sub = await db.various.subscribeOne!({ id: 99 }, { throttle: 1700 }, async item => {
484
- // const item = items[0]
485
- // console.log(item)
486
-
487
- const now = Date.now();
488
- if(item && item.name === "zz3zz2" && now - start > 1600 && now - start < 1800){
489
- await db.various.delete!({ name: "zz3zz2" });
490
- sub.unsubscribe()
491
- resolve(true)
492
- }
493
- });
494
- await db.various.update!({ id: 99 }, { name: "zz3zz1" });
495
- await db.various.update!({ id: 99 }, { name: "zz3zz2" });
496
- }, { timeout: 4000 });
497
-
498
- await tryRun("JSON filtering", async () => {
499
- const res = await db.various.count!({ "jsn->a->>b": '3' });
500
- assert.equal(res, 1)
501
- });
502
-
503
- await tryRun("Complex filtering", async () => {
504
- const res = await db.various.count!({
505
- $and: [
506
- {
507
- $filter: [
508
- { $year: ["added"] },
509
- "=",
510
- '1996'
511
- ]
512
- },
513
- {
514
- $filter: [
515
- { $Mon: ["added"] },
516
- "=",
517
- 'Dec'
518
- ]
519
- }
520
-
521
- ]
522
- });
523
- assert.equal(res, 1)
524
- });
525
-
526
- await tryRun("template_string function", async () => {
527
- const res = await db.various.findOne!({ name: 'abc9' }, { select: { tstr: { $template_string: ["{name} is hehe"] } } });
528
- const res2 = await db.various.findOne!({ name: 'abc9' }, { select: { tstr: { $template_string: ["is hehe"] } } });
529
- assert.equal(res.tstr, "abc9 is hehe")
530
- assert.equal(res2.tstr, "is hehe")
531
- });
532
-
533
- await tryRun("Between filtering", async () => {
534
- const res = await db.various.count!({
535
- added: { $between: [
536
- '06 Dec 1995 00:12:00',
537
- '03 Dec 1997 00:12:00'
538
- ] } });
539
- assert.equal(res, 1)
540
- });
541
- await tryRun("In filtering", async () => {
542
- const res = await db.various.count!({ added: { $in: ['04 Dec 1996 00:12:00'] } });
543
- assert.equal(res, 1)
544
- });
545
-
546
- await tryRun("Order by", async () => {
547
- const res = await db.items.find!({}, { select: { name: 1 }, orderBy: [{ key: "name", asc: false, nulls: "first", nullEmpty: true }] });
548
- assert.deepStrictEqual(res, [{ name: 'b'}, { name: 'a'}, { name: 'a'}]);
549
- });
550
-
551
- await tryRun("Order by aliased func", async () => {
552
- const res = await db.items.find!({ }, { select: { uname: { $upper: ["name"] }, count: { $countAll: [] } }, orderBy: { uname: -1 }});
553
- assert.deepStrictEqual(res, [{ uname: 'B', count: '1'}, { uname: 'A', count: '2'} ])
554
- });
555
-
556
- await tryRun("Order by aggregation", async () => {
557
- const res = await db.items.find!({ }, { select: { name: 1, count: { $countAll: [] } }, orderBy: { count: -1 }});
558
- assert.deepStrictEqual(res, [ { name: 'a', count: '2'} , { name: 'b', count: '1'} ])
559
- });
560
-
561
- await tryRun("Order by colliding alias name", async () => {
562
- const res = await db.items.find!({ }, { select: { name: { $countAll: [] }, n: { $left: ["name", 1]} }, orderBy: { name: -1 }});
563
- assert.deepStrictEqual(res, [ { name: '2', n: 'a' } , { name: '1', n: 'b'} ])
564
- });
565
-
566
- await tryRun("Update batch example", async () => {
567
-
568
- await db.items4.updateBatch!([
569
- [{ name: "abc1" }, { name: "abc" }],
570
- [{ name: "abc2" }, { name: "abc" }]
571
- ]);
572
- assert.equal(await db.items4.count!({ name: "abc" }), 2);
573
- })
574
-
575
- await tryRun("Function example", async () => {
576
-
577
- const f = await db.items4.findOne!({}, { select: { public: 1, p_5: { $left: ["public", 3] } } });
578
- assert.equal(f.p_5.length, 3);
579
- assert.equal(f.p_5, f.public.substr(0, 3));
580
-
581
- // Nested function
582
- const fg = await db.items2.findOne!({}, { select: { id: 1, name: 1, items3: { name: "$upper" } } });// { $upper: ["public"] } } });
583
- assert.deepStrictEqual(fg, { id: 1, name: 'a', items3: [ { name: 'A' } ] });
584
-
585
- // Date utils
586
- const Mon = await db.items4.findOne!({ name: "abc" }, { select: { added: "$Mon" } });
587
- assert.deepStrictEqual(Mon, { added: "Dec" });
588
-
589
- // Date + agg
590
- const MonAgg = await db.items4.find!({ name: "abc" }, { select: { added: "$Mon", public: "$count" } });
591
- assert.deepStrictEqual(MonAgg, [{ added: "Dec", public: '2' }]);
592
-
593
- // Returning
594
- const returningParam = { returning: { id: 1, name: 1, public: 1 , $rowhash: 1, added_day: { "$day": ["added"] } }} as const ; // ctid: 1,
595
- let i = await db.items4_pub.insert!( { name: "abc123", public: "public data", added: '04 Dec 1995 00:12:00' }, returningParam);
596
- assert.deepStrictEqual(i, { id: 1, name: 'abc123', public: 'public data', $rowhash: '347c26babad535aa697a794af89195fe', added_day: 'monday' }); // , ctid: '(0,1)'
597
-
598
- let u = await db.items4_pub.update! ({ name: "abc123" }, { public: "public data2" }, returningParam);
599
- assert.deepStrictEqual(u, [{ id: 1, name: 'abc123', public: 'public data2', $rowhash: '9d18ddfbff9e13411d13f82d414644de', added_day: 'monday' }]);
600
-
601
- let d = await db.items4_pub.delete!( { name: "abc123" }, returningParam);
602
- assert.deepStrictEqual(d, [{ id: 1, name: 'abc123', public: 'public data2', $rowhash: '9d18ddfbff9e13411d13f82d414644de', added_day: 'monday' }]);
603
-
604
- console.log("TODO: socket.io stringifies dates")
605
- });
606
-
607
- await tryRun("JSONB filtering", async () => {
608
- const obj = { propName: 3232 };
609
- const row = await db.obj_table.insert!({ obj }, { returning: "*" });
610
- const sameRow = await db.obj_table.findOne!({obj });
611
- const sameRow1 = await db.obj_table.findOne!({ obj: { "=": obj } });
612
- const sameRow2 = await db.obj_table.findOne!({ "obj.=": obj });
613
- const count = await db.obj_table.count!({ obj });
614
- assert.deepStrictEqual(row, sameRow);
615
- assert.deepStrictEqual(row, sameRow1);
616
- assert.deepStrictEqual(row, sameRow2);
617
- assert.deepStrictEqual(+count, 1);
618
- })
619
-
620
- await tryRun("Postgis examples", async () => {
621
- await db.shapes.delete!();
622
- const p1 = { $ST_GeomFromText: ["POINT(-1 1)", 4326] },
623
- p2 = { $ST_GeomFromText: ["POINT(-2 2)", 4326] };
624
- await db.shapes.insert!([
625
- { geom: p1, geog: p1 },
626
- { geom: p2, geog: p2 },
627
- ])
628
-
629
- /** Basic functions and extent filters */
630
- const f = await db.shapes.findOne!({ $and: [
631
- {"geom.&&.st_makeenvelope": [
632
- -3, 2,
633
- -2, 2
634
- ]},
635
- {"geog.&&.st_makeenvelope": [
636
- -3, 2,
637
- -2, 2
638
- ] }]
639
- }, {
640
- select: {
641
- geomTxt: {"$ST_AsText": ["geom"]},
642
- geomGeo: {"$ST_AsGeoJSON": ["geom"]},
643
- },
644
- orderBy: "geom"
645
- });
646
- assert.deepStrictEqual(f, {
647
- geomGeo: {
648
- coordinates: [-2,2],
649
- type: 'Point'
650
- },
651
- geomTxt: 'POINT(-2 2)'
652
- });
653
-
654
- /**Aggregate functions */
655
- const aggs = await db.shapes.findOne!({ }, {
656
- select: {
657
- xMin: { "$ST_XMin_Agg": ["geom"] },
658
- xMax: { "$ST_XMax_Agg": ["geom"] },
659
- yMin: { "$ST_YMin_Agg": ["geom"] },
660
- yMax: { "$ST_YMax_Agg": ["geom"] },
661
- zMin: { "$ST_ZMin_Agg": ["geom"] },
662
- zMax: { "$ST_ZMax_Agg": ["geom"] },
663
- extent: { "$ST_Extent": ["geom"] },
664
- // extent3D: { "$ST_3DExtent": ["geom"] },
665
- },
666
- });
667
- assert.deepStrictEqual(aggs, {
668
- xMax: -1,
669
- xMin: -2,
670
- yMax: 2,
671
- yMin: 1,
672
- zMax: 0,
673
- zMin: 0,
674
- extent: 'BOX(-2 1,-1 2)',
675
- // extent3D: 'BOX3D(-2 1 0,-1 2 6.952908662134e-310)' <-- looks like a value that will fail tests at some point
676
- });
677
-
678
- });
679
-
680
-
681
- await tryRun("jsonbSchema validation", async () => {
682
-
683
- /**
684
- *
685
- tjson: {
686
- json: { jsonbSchema: {
687
- a: { type: "boolean" },
688
- arr: { enum: ["1", "2", "3"] },
689
- arr1: { enum: [1, 2, 3] },
690
- arr2: { type: "integer[]" },
691
- o: { oneOf: [{ o1: { type: "integer" } }, { o2: { type: "boolean" } }], optional: true },
692
- }
693
- }
694
- },
695
- */
696
-
697
- const fo = await db.tjson.insert!({ colOneOf: "a", json }, { returning: "*"});
698
- // assert.deepStrictEqual(fo.json, json);
699
- await db.tjson.insert!({ colOneOf: "a", json: {...json, o: { o1: 2 } } })
700
- try {
701
- await db.tjson.insert!({ colOneOf: "a", json: { a: true, arr: "22"} });
702
- throw "Should have failed"
703
- } catch(e){
704
- // Expected
705
- }
706
- });
707
-
708
-
709
- await tryRun("find and findOne", async () => {
710
-
711
- const fo = await db.items.findOne!();
712
- const f = await db.items.find!();
713
- assert.deepStrictEqual(fo, { h: null, id: 1, name: 'a' });
714
- assert.deepStrictEqual(f[0], { h: null, id: 1, name: 'a' });
715
- });
716
-
717
- await tryRun("Result size", async () => {
718
- const is75bits = await db.items.size!({
719
- }, { select: { name: 1 } });
720
- assert.equal(is75bits, '75', "Result size query failed")
721
- });
722
-
723
- await tryRun("Basic exists", async () => {
724
- const expect0 = await db.items.count!({
725
- $and: [
726
- { $exists: { items2: { name: "a" } } },
727
- { $exists: { items3: { name: "b" } } },
728
- ]
729
- });
730
- assert.equal(expect0, 0, "$exists query failed")
731
- });
732
-
733
- await tryRun("Basic fts with shorthand notation", async () => {
734
- const res = await db.items.count!({
735
- $and: [
736
- { $exists: { items2: { "name.@@.to_tsquery": ["a"] } } },
737
- { $exists: { items3: { "name.@@.to_tsquery": ["b"] } } },
738
- ]
739
- });
740
- // assert.deepStrictEqual(res, { name: 'a'})
741
- assert.equal(res, 0, "FTS query failed")
742
- });
743
-
744
- await tryRun("Exists with shortest path wildcard filter example", async () => {
745
- const expect2 = await db.items.find!({
746
- $and: [
747
- { $existsJoined: { "**.items3": { name: "a" } } },
748
- { $existsJoined: { items2: { name: "a" } } }
749
- ]
750
- });
751
- assert.equal(expect2.length, 2, "$existsJoined query failed");
752
- const expectNothing = await db.items.find!({
753
- $and: [
754
- { $existsJoined: { "**.items3": { name: "nothing" } } },
755
- { $existsJoined: { items2: { name: "a" } } }
756
- ]
757
- });
758
- assert.equal(expectNothing.length, 0, "$existsJoined query failed");
759
- });
760
-
761
-
762
- await tryRun("Exists with exact path filter example", async () => {
763
- const _expect2 = await db.items.find!({
764
- $and: [
765
- // { "items2": { name: "a" } },
766
- // { "items2.items3": { name: "a" } },
767
- { $existsJoined: { items2: { name: "a" } } }
768
- ]
769
- });
770
- assert.equal(_expect2.length, 2, "$existsJoined query failed");
771
- });
772
-
773
- await tryRun("Not Exists with exact path filter example", async () => {
774
- const _expect1 = await db.items.find!({
775
- $and: [
776
- { $notExistsJoined: { items2: { name: "a" } } }
777
- ]
778
- });
779
- assert.equal(_expect1.length, 1, "$notExistsJoined query failed");
780
- });
781
-
782
- /* Upsert */
783
- await tryRun("Upsert example", async () => {
784
- await db.items.upsert!({ name: "tx" }, { name: "tx" });
785
- await db.items.upsert!({ name: "tx" }, { name: "tx" });
786
- assert.equal(await db.items.count!({ name: "tx" }), 1, "upsert command failed");
787
- });
788
-
789
- /* Joins example */
790
- await tryRun("Joins example", async () => {
791
- const items = await db.items.find!({}, {
792
- select: {
793
- "*": 1,
794
- items3: "*",
795
- items22: db.leftJoin.items2({}, "*")
796
- }
797
- });
798
-
799
- if(!items.length || !items.every(it => Array.isArray(it.items3) && Array.isArray(it.items22))){
800
- console.log(items[0].items3)
801
- throw "Joined select query failed";
802
- }
803
- });
804
-
805
- /* Joins duplicate table example */
806
- await tryRun("Joins repeating table example", async () => {
807
- const items2 = await db.items.find!({}, {
808
- select: {
809
- "*": 1,
810
- items2: "*"
811
- }
812
- });
813
- const items2j = await db.items.find!({}, {
814
- select: {
815
- "*": 1,
816
- items2: "*",
817
- items2j: db.leftJoin.items2({}, "*")
818
- }
819
- });
820
-
821
- items2.forEach((d, i)=> {
822
- assert.deepStrictEqual(d.items2, items2j[i].items2, "Joins duplicate aliased table query failed");
823
- assert.deepStrictEqual(d.items2, items2j[i].items2j, "Joins duplicate aliased table query failed");
824
- });
825
- });
826
-
827
-
828
-
829
- await tryRun("Join aggregate functions example", async () => {
830
- const singleShortHandAgg = await db.items.findOne!(
831
- {},
832
- { select: { id: "$max" }}
833
- );
834
- const singleAgg = await db.items.findOne!(
835
- {},
836
- { select: { id: { "$max": ["id"] } }}
837
- );
838
- assert.deepStrictEqual(singleShortHandAgg, { id: 4 });
839
- assert.deepStrictEqual(singleAgg, { id: 4 });
840
-
841
- const shortHandAggJoined = await db.items.findOne!(
842
- { id: 4 },
843
- { select: { id: 1, items2: { name: "$max" } }}
844
- );
845
- assert.deepStrictEqual(shortHandAggJoined, { id: 4, items2: [] });
846
- });
847
-
848
-
849
-
850
- /* $rowhash -> Custom column that returms md5(ctid + allowed select columns). Used in joins & CRUD to bypass PKey details */
851
- await tryRun("$rowhash example", async () => {
852
- const rowhash = await db.items.findOne!({}, { select: { $rowhash: 1, "*": 1 }});
853
- const f = { $rowhash: rowhash.$rowhash };
854
- const rowhashView = await db.v_items.findOne!({}, { select: { $rowhash: 1 }});
855
- const rh1 = await db.items.findOne!({ $rowhash: rowhash.$rowhash }, { select: { $rowhash: 1 }});
856
- const rhView = await db.v_items.findOne!({ $rowhash: rowhashView.$rowhash }, { select: { $rowhash: 1 }});
857
- // console.log({ rowhash, f });
858
-
859
- await db.items.update!(f, { name: 'a' });
860
-
861
- // console.log(rowhash, rh1)
862
- // console.log(rowhashView, rhView)
863
- if(
864
- typeof rowhash.$rowhash !== "string" ||
865
- typeof rowhashView.$rowhash !== "string" ||
866
- rowhash.$rowhash !== rh1.$rowhash ||
867
- rowhashView.$rowhash !== rhView.$rowhash
868
- ){
869
- throw "$rowhash query failed";
870
- }
871
- });
872
-
873
-
874
- await tryRun("Reference column nested insert", async () => {
875
- const nestedRow = { name: "nested_insert" };
876
- const parentRow = { name: "parent insert" }
877
- const pr = await db.items2.insert!({ items_id: nestedRow, ...parentRow }, { returning: "*" });
878
-
879
- const childRows = await db.items.find!(nestedRow);
880
- assert.equal(childRows.length, 1);
881
- assert.deepStrictEqual(await db.items2.findOne!(parentRow), { hh: null, id: pr.id, ...parentRow, items_id: childRows[0].id });
882
-
883
- });
884
-
885
- await tryRun("Join escaped table names with quotes", async () => {
886
- await db[`"""quoted0"""`].insert!({
887
- [`"text_col0"`]: "0",
888
- [`"quoted1_id"`]: {
889
- [`"text_col1"`]: "1",
890
- [`"quoted2_id"`]: {
891
- [`"text_col2"`]: "2",
892
- }
893
- }
894
- });
895
-
896
- const res = await db[`"""quoted0"""`].find!({
897
- [`"text_col0"`]: "0",
898
- }, {
899
- select: {
900
- "*": 1,
901
- [`"""quoted2"""`]: {
902
- [`"text_col2"`]: 1,
903
- [`"id2"`]: "$min",
904
- [`id2 max`]: { "$max": [`"id2"`] },
905
- }
906
- }
907
- });
908
-
909
- assert.deepStrictEqual(res[0], {
910
- '"""quoted2"""': [
911
- {
912
- '"text_col2"': '2',
913
- 'id2 max': 1,
914
- '"id2"': 1,
915
- }
916
- ],
917
- '"id0"': 1,
918
- '"quoted1_id"': 1,
919
- '"text_col0"': '0'
920
- });
921
-
922
-
923
- const aliasedQuotedJoin = await db[`"""quoted0"""`].find!({
924
- [`"text_col0"`]: "0",
925
- }, {
926
- select: {
927
- "*": 1,
928
- '"ali as"': {
929
- $leftJoin: '"""quoted2"""',
930
- select: {
931
- '"text_col2"': { $left: ['"text_col2"', 2] },
932
- '"id2"': "$min",
933
- 'id2 max': { "$max": [`"id2"`] },
934
- }
935
- }
936
- }
937
- });
938
-
939
- assert.deepStrictEqual(aliasedQuotedJoin, [{
940
- '"id0"': 1,
941
- '"quoted1_id"': 1,
942
- '"text_col0"': '0',
943
- '"ali as"': [{
944
- '"text_col2"': '2',
945
- '"id2"': 1,
946
- 'id2 max': 1,
947
- }]
948
- }]);
949
-
950
- const exists1 = await db[`"""quoted0"""`].find!({
951
- $existsJoined: {
952
- path: ['"""quoted1"""', '"""quoted2"""'],
953
- filter: {
954
- '"id2"': 1,
955
- }
956
- }
957
- }, { select: "*" });
958
- /** Duplicated tables */
959
- const exists2 = await db[`"""quoted0"""`].find!({
960
- $existsJoined: {
961
- path: ['"""quoted1"""', '"""quoted2"""','"""quoted1"""', '"""quoted2"""'],
962
- filter: {
963
- '"id2"': 1,
964
- }
965
- }
966
- }, { select: "*" });
967
- assert.deepStrictEqual(exists1, exists2)
968
- })
969
-
970
- await tryRun("Reverse join with agg", async () => {
971
- const inserted = await db.tr1.insert!({ tr2: { t1: "a", t2: "b" } }, { returning: "*" });
972
-
973
- const idAggSelect = {
974
- ids: {
975
- $array_agg: ["id"],
976
- }
977
- }
978
- const normalJoin = await db.tr1.find!({}, { orderBy: { id: true }, select: { "*": 1, tr2: { $innerJoin: "tr2", filter: { t1: "a" }, select: idAggSelect } } })
979
- const reverseJoin = await db.tr2.find!({ t1: "a" }, { orderBy: { id: true }, select: { "*": 1, tr1: { $innerJoin: "tr1", select: idAggSelect } } });
980
- assert.deepStrictEqual(normalJoin[0], {"id": 1,"t1": null,"tr2": [{ "ids": [1] }]});
981
- assert.deepStrictEqual(normalJoin[1], {"id": 2,"t1": null,"tr2": [{ "ids": [2] }]});
982
- assert.deepStrictEqual(reverseJoin[0], { "id": 1, "tr1_id": 1, "t1": "a", "t2": "b", "tr1": [{ "ids": [1] }] });
983
- assert.deepStrictEqual(reverseJoin[1], { "id": 2, "tr1_id": 2, "t1": "a", "t2": "b", "tr1": [{ "ids": [2] }] });
984
- });
985
-
986
- await tryRun("Related table subscribe", async () => {
987
- const sub = await db.tr1.subscribe!({}, {
988
- select: {
989
- "*": 1,
990
- tr2: "*",
991
- tr3: "*",
992
- }
993
- }, _rows => {
994
-
995
- });
996
-
997
- await sub.unsubscribe();
998
- });
999
-
1000
- await tryRun("Nested sort by computed col", async () => {
1001
- const getSorted = (asc = false) => db.tr1.find!({}, {
1002
- select: {
1003
- "*": 1,
1004
- tr2: {
1005
- maxId: { $max: ["id"] }
1006
- }
1007
- },
1008
- orderBy: {
1009
- "tr2.maxId": asc
1010
- }
1011
- })
1012
- const sortedAsc = await getSorted(true);
1013
- const sortedDesc = await getSorted(false);
1014
- assert.deepStrictEqual(
1015
- sortedAsc.map(d => d.tr2[0].maxId).slice(0).reverse(),
1016
- sortedDesc.map(d => d.tr2[0].maxId)
1017
- );
1018
- });
1019
-
1020
- await tryRun("Nested function on different than source column getNewQuery name bug fix", async () => {
1021
- const res = await db.tr1.find!({}, {
1022
- select: {
1023
- "*": 1,
1024
- tr2: {
1025
- sign: { $sign: ["tr1_id"] }
1026
- }
1027
- },
1028
- orderBy: {
1029
- id: true
1030
- }
1031
- });
1032
- assert.deepStrictEqual(res.map(row => [row.id, row.tr2[0]!.sign]), [[1,1], [2, 1]]);
1033
- })
1034
-
1035
- await tryRun("Reference column deep nested insert", async () => {
1036
-
1037
- const pr = await db.items4a.insert!({
1038
- items_id: { name: "it" },
1039
- items2_id: { name: "it2", items_id: { name: "it" } },
1040
- name: "it4a"
1041
- }, { returning: "*" });
1042
- const itemsCount = await db.items.count!({ name: "it" })
1043
- const items2Count = await db.items2.count!({ name: "it2" })
1044
- const items4aCount = await db.items4a.count!({ name: "it4a" })
1045
-
1046
- assert.equal(+itemsCount, 2);
1047
- assert.equal(+items2Count, 1);
1048
- assert.equal(+items4aCount, 1);
1049
-
1050
- });
1051
-
1052
- await tryRun("Multi reference column nested insert", async () => {
1053
- await db.items_multi.insert!({
1054
- items0_id: { name: "multi" },
1055
- items1_id: { name: "multi" },
1056
- items2_id: { name: "multi" },
1057
- items3_id: { name: "multi" },
1058
- name: "root_multi"
1059
- }, { returning: "*" });
1060
- const itemsCount = await db.items.count!({ name: "multi" })
1061
- assert.equal(+itemsCount, 4);
1062
-
1063
- const multiItem = await db.items_multi.findOne!({ name: "root_multi" }, { select: { "*": 1, items: "*" } });
1064
- assert.equal(multiItem?.name, "root_multi");
1065
- assert.equal(multiItem?.items.filter(d => d.name === "multi").length, 4);
1066
-
1067
- });
1068
-
1069
- await tryRun("Join path", async () => {
1070
-
1071
- await db.items_multi.insert!({
1072
- items0_id: { name: "multi0" },
1073
- items1_id: { name: "multi1" },
1074
- name: "root_multi"
1075
- }, { returning: "*" });
1076
-
1077
- const res = await db.items_multi.find!(
1078
- {},
1079
- {
1080
- select: {
1081
- "*": 1,
1082
- i0: db.innerJoin.items_multi(
1083
- { name: "multi0" },
1084
- "*",
1085
- { path: [{ table: "items", on: [{ items0_id: "id" }] }] }
1086
- )
1087
- },
1088
- orderBy: {
1089
- "i0.name": -1
1090
- }
1091
- }
1092
- );
1093
- assert.equal(res.length, 1);
1094
- assert.equal(res[0].i0[0].name, "multi0");
1095
- assert.equal(res[0].items2_id, null);
1096
- assert.equal(res[0].items2_id, null);
1097
- });
1098
-
1099
- await tryRun("Self join", async () => {
1100
- await db.self_join.delete!();
1101
- const a = await db.self_join.insert!({ name: "a" });
1102
- const a1 = await db.self_join.insert!({ name: "a", my_id: { name: "b" } });
1103
- const a2 = await db.self_join.insert!({ name: "a", my_id1: { name: "b1" } });
1104
-
1105
- const one = await db.self_join.find!({}, {
1106
- select: {
1107
- name: 1,
1108
- my: {
1109
- $innerJoin: [{ table: "self_join", on: [{ my_id: "id" }] }],
1110
- filter: { name: "b" },
1111
- select: "*",
1112
- orderBy: "name"
1113
- }
1114
- }
1115
- });
1116
- assert.equal(one.length, 1);
1117
- assert.equal(one[0].my.length, 1);
1118
- assert.equal(one[0].my[0].name, "b");
1119
- });
1120
-
1121
-
1122
- await tryRun("One to many multi join duplicate row bug fix", async () => {
1123
- await db.symbols.insert!([
1124
- {
1125
- id: "btc",
1126
- trades: [
1127
- { price: 1 },
1128
- { price: 3 },
1129
- { price: 2 },
1130
- ]
1131
- },{
1132
- id: "eth",
1133
- trades: [
1134
- { price: .1 },
1135
- { price: .3 },
1136
- { price: .2 },
1137
- ]
1138
- },{
1139
- id: "abc"
1140
- }
1141
- ]);
1142
-
1143
- const res = await db.symbols.find!({}, {
1144
- select: {
1145
- id: 1,
1146
- trades: "*",
1147
- tradesAlso: {
1148
- $leftJoin: "trades",
1149
- select: "*",
1150
- },
1151
- }
1152
- });
1153
- assert.equal(res.length, 3);
1154
- res.forEach(row => {
1155
- assert(typeof row.id, "number");
1156
- assert(typeof (row as any).price, "number");
1157
- assert(typeof (row as any).symbol, "string");
1158
- assert.deepStrictEqual(row.trades, row.tradesAlso);
1159
- if(row.id !== "abc"){
1160
- assert.equal(row.trades.length, 3)
1161
- }
1162
- });
1163
-
1164
- const resSortedInnerJoin = await db.symbols.find!({}, {
1165
- select: {
1166
- id: 1,
1167
- trades: {
1168
- $innerJoin: "trades",
1169
- select: "*",
1170
- orderBy: { price: -1 },
1171
- },
1172
- tradesAlso: {
1173
- $innerJoin: "trades",
1174
- select: "*",
1175
- orderBy: { price: 1 },
1176
- },
1177
- }
1178
- });
1179
- assert.equal(resSortedInnerJoin.length, 2);
1180
- resSortedInnerJoin.forEach(row => {
1181
- assert.deepStrictEqual(row.trades.slice(0).reverse(), row.tradesAlso);
1182
- assert.notEqual(row.id, "abc");
1183
- });
1184
- });
1185
- }
1186
-
1187
-
1188
- const tout = (t = 3000) => {
1189
- return new Promise(async (resolve, reject) => {
1190
- setTimeout(() => {
1191
- resolve(true)
1192
- },t)
1193
- });
1194
- }