total5 0.0.1 → 0.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/nosql.js ADDED
@@ -0,0 +1,782 @@
1
+ // Total.js NoSQL embedded database
2
+ // The MIT License
3
+ // Copyright 2014-2023 (c) Peter Širka <petersirka@gmail.com>
4
+
5
+ 'use strict';
6
+
7
+ const NoSQLStream = require('./nosql-stream');
8
+ const NoSQLReader = require('./nosql-reader');
9
+ const NoSQLQueryBuilder = require('./nosql-builder').NoSQLQueryBuilder;
10
+
11
+ require('./nosql-querybuilder');
12
+
13
+ const MAXREADERS = 3;
14
+ const JSONBUFFER = 40;
15
+
16
+ const JSONBOOL = '":true ';
17
+ const NEWLINE = '\n';
18
+ const REG_BOOL = /":true/g; // for updates of boolean types
19
+ const REG_DATE = /"\d{4}-\d{2}-\d{2}T[0-9.:]+Z"/g;
20
+
21
+ function NoSQL(filename) {
22
+ var t = this;
23
+ t.filename = filename;
24
+ t.duration = [];
25
+ t.pending_count = 0;
26
+ t.pending_update = [];
27
+ t.pending_append = [];
28
+ t.pending_reader = [];
29
+ t.pending_remove = [];
30
+ t.pending_reader2 = [];
31
+ t.pending_streamer = [];
32
+ t.pending_clean = [];
33
+ t.pending_clear = [];
34
+ t.step = 0;
35
+ t.pending_drops = false;
36
+ t.$timeoutmeta;
37
+ t.$writing = false;
38
+ t.$reading = 0;
39
+ t.total = 0;
40
+ t.inmemory = false;
41
+ t.next2 = () => t.next(0);
42
+ }
43
+
44
+ NoSQL.prototype.memory = function(count, size) {
45
+ var self = this;
46
+ count && (self.buffercount = count + 1); // def: 15 - count of stored documents in memory while reading/writing
47
+ size && (self.buffersize = size * 1024); // def: 32 - size of buffer in kB
48
+ return self;
49
+ };
50
+
51
+ function next_operation(self, type) {
52
+ self.next(type);
53
+ }
54
+
55
+ NoSQL.prototype.drop = function(callback) {
56
+ var self = this;
57
+ self.pending_drops = true;
58
+ setImmediate(next_operation, self, 7);
59
+ callback && callback(F.TUtils.success);
60
+ return self;
61
+ };
62
+
63
+ NoSQL.prototype.clear = function(callback) {
64
+ var self = this;
65
+ self.pending_clear.push(callback || NOOP);
66
+ setImmediate(next_operation, self, 12);
67
+ return self;
68
+ };
69
+
70
+ NoSQL.prototype.clean = function(callback) {
71
+ var self = this;
72
+ self.pending_clean.push(callback || NOOP);
73
+ setImmediate(next_operation, self, 13);
74
+ return self;
75
+ };
76
+
77
+ NoSQL.prototype.remove = function() {
78
+ var self = this;
79
+ var builder = new NoSQLQueryBuilder(self);
80
+ self.pending_remove.push(builder);
81
+ setImmediate(next_operation, self, 3);
82
+ return builder;
83
+ };
84
+
85
+ NoSQL.prototype.find = function(builder) {
86
+ var self = this;
87
+ if (builder instanceof NoSQLQueryBuilder)
88
+ builder.db = self;
89
+ else
90
+ builder = new NoSQLQueryBuilder(self);
91
+ self.pending_reader.push(builder);
92
+ setImmediate(next_operation, self, 4);
93
+ return builder;
94
+ };
95
+
96
+ NoSQL.prototype.update = function(builder) {
97
+ var self = this;
98
+ if (builder instanceof NoSQLQueryBuilder)
99
+ builder.db = self;
100
+ else
101
+ builder = new NoSQLQueryBuilder(self);
102
+ self.pending_update.push(builder);
103
+ setImmediate(next_operation, self, 2);
104
+ return builder;
105
+ };
106
+
107
+ NoSQL.prototype.find2 = function(builder) {
108
+ var self = this;
109
+ if (builder instanceof NoSQLQueryBuilder)
110
+ builder.db = self;
111
+ else
112
+ builder = new NoSQLQueryBuilder(self);
113
+ self.pending_reader2.push(builder);
114
+ setImmediate(next_operation, self, 11);
115
+ return builder;
116
+ };
117
+
118
+ NoSQL.prototype.stream = function(fn, arg, callback) {
119
+ var self = this;
120
+
121
+ if (typeof(arg) === 'function') {
122
+ callback = arg;
123
+ arg = null;
124
+ }
125
+
126
+ self.pending_streamer.push({ fn: fn, callback: callback, arg: arg || {} });
127
+ setImmediate(next_operation, self, 10);
128
+ return self;
129
+ };
130
+
131
+ NoSQL.prototype.recount = function() {
132
+ var self = this;
133
+ self.pending_count++;
134
+ setImmediate(next_operation, self, 5);
135
+ };
136
+
137
+ // 1 append
138
+ // 2 update
139
+ // 3 remove
140
+ // 4 reader
141
+ // 5 counting
142
+ // 7 drop
143
+ // 8 backup
144
+ // 9 restore
145
+ // 10 streamer
146
+ // 11 reader reverse
147
+ // 12 clear
148
+ // 13 clean
149
+
150
+ const NEXTWAIT = { 7: true, 8: true, 9: true, 12: true, 13: true };
151
+
152
+ NoSQL.prototype.next = function(type) {
153
+
154
+ if (type && NEXTWAIT[this.step])
155
+ return;
156
+
157
+ if (!this.$writing && !this.$reading) {
158
+
159
+ if (this.step !== 12 && this.pending_clear.length) {
160
+ this.$clear();
161
+ return;
162
+ }
163
+
164
+ if (this.step !== 13 && this.pending_clean.length) {
165
+ this.$clean();
166
+ return;
167
+ }
168
+
169
+ if (this.step !== 7 && this.pending_drops) {
170
+ this.$drop();
171
+ return;
172
+ }
173
+
174
+ if (this.step !== 5 && this.pending_count) {
175
+ this.$count();
176
+ return;
177
+ }
178
+ }
179
+
180
+ if (!this.$writing) {
181
+
182
+ if (this.step !== 1 && this.pending_append.length) {
183
+ this.$append();
184
+ return;
185
+ }
186
+
187
+ if (this.step !== 2 && !this.$writing && this.pending_update.length) {
188
+ this.$update();
189
+ return;
190
+ }
191
+
192
+ if (this.step !== 3 && !this.$writing && this.pending_remove.length) {
193
+ this.$remove();
194
+ return;
195
+ }
196
+
197
+ }
198
+
199
+ if (this.$reading < MAXREADERS) {
200
+
201
+ // if (this.step !== 4 && this.pending_reader.length) {
202
+ if (this.pending_reader.length) {
203
+ this.$reader();
204
+ return;
205
+ }
206
+
207
+ // if (this.step !== 11 && this.pending_reader2.length) {
208
+ if (this.pending_reader2.length) {
209
+ this.$reader2();
210
+ return;
211
+ }
212
+
213
+ // if (this.step !== 10 && this.pending_streamer.length) {
214
+ if (this.pending_streamer.length) {
215
+ this.$streamer();
216
+ return;
217
+ }
218
+ }
219
+
220
+ if (this.step !== type) {
221
+ this.step = 0;
222
+ setImmediate(next_operation, this, 0);
223
+ }
224
+ };
225
+
226
+ // ======================================================================
227
+ // FILE OPERATIONS
228
+ // ======================================================================
229
+
230
+ NoSQL.prototype.$append = function() {
231
+ var self = this;
232
+ self.step = 1;
233
+
234
+ if (!self.pending_append.length) {
235
+ self.next(0);
236
+ return;
237
+ }
238
+
239
+ self.$writing = true;
240
+
241
+ self.pending_append.splice(0).limit(JSONBUFFER, function(items, next) {
242
+
243
+ var json = '';
244
+ var now = Date.now();
245
+
246
+ for (var i = 0; i < items.length; i++) {
247
+ var builder = items[i];
248
+ json += JSON.stringify(builder.payload) + NEWLINE;
249
+ self.oninsert && self.oninsert(builder.payload);
250
+ }
251
+
252
+ F.Fs.appendFile(self.filename, json, function(err) {
253
+
254
+ err && F.error(err, 'NoSQL insert: ' + self.filename);
255
+
256
+ var diff = Date.now() - now;
257
+
258
+ if (self.duration.push({ type: 'insert', duration: diff }) > 20)
259
+ self.duration.shift();
260
+
261
+ for (var i = 0; i < items.length; i++) {
262
+ var builder = items[i];
263
+ builder.duration = diff;
264
+ builder.response = builder.counter = builder.count = 1;
265
+ builder.logrule && builder.logrule();
266
+ builder.done();
267
+ }
268
+
269
+ self.total += items.length;
270
+ next();
271
+ });
272
+
273
+ }, () => setImmediate(next_append, self));
274
+ };
275
+
276
+ function next_append(self) {
277
+ self.$writing = false;
278
+ self.next(0);
279
+ }
280
+
281
+ NoSQL.prototype.$update = function() {
282
+
283
+ var self = this;
284
+ self.step = 2;
285
+
286
+ if (!self.pending_update.length) {
287
+ self.next(0);
288
+ return self;
289
+ }
290
+
291
+ self.$writing = true;
292
+
293
+ var filter = self.pending_update.splice(0);
294
+ var filters = NoSQLReader.make();
295
+ var fs = new NoSQLStream(self.filename);
296
+ var change = false;
297
+
298
+ filters.type = 'update';
299
+ filters.db = self;
300
+
301
+ for (var i = 0; i < filter.length; i++)
302
+ filters.add(filter[i], true);
303
+
304
+ if (self.buffersize)
305
+ fs.buffersize = self.buffersize;
306
+
307
+ if (self.buffercount)
308
+ fs.buffercount = self.buffercount;
309
+
310
+ var update = function(docs, doc, dindex, f) {
311
+ try {
312
+ f.modifyrule(docs[dindex], f.modifyarg);
313
+ f.backuprule && f.backuprule(fs.docsbuffer[dindex].doc);
314
+ } catch (e) {
315
+ f.canceled = true;
316
+ f.error = e + '';
317
+ }
318
+ };
319
+
320
+ var updateflush = function(docs, doc, dindex) {
321
+
322
+ doc = docs[dindex];
323
+
324
+ var rec = fs.docsbuffer[dindex];
325
+ var upd = JSON.stringify(doc).replace(REG_BOOL, JSONBOOL);
326
+
327
+ if (upd === rec.doc)
328
+ return;
329
+
330
+ !change && (change = true);
331
+ var was = true;
332
+
333
+ if (rec.doc.length === upd.length) {
334
+ var b = Buffer.byteLength(upd);
335
+ if (rec.length === b) {
336
+ fs.write(upd + NEWLINE, rec.position);
337
+ was = false;
338
+ }
339
+ }
340
+
341
+ if (was) {
342
+ var tmp = fs.remchar + rec.doc.substring(1) + NEWLINE;
343
+ fs.write(tmp, rec.position);
344
+ fs.write2(upd + NEWLINE);
345
+ }
346
+
347
+ self.onupdate && self.onupdate(doc);
348
+ };
349
+
350
+ fs.ondocuments = function() {
351
+ try {
352
+ var docs = (new Function('return [' + fs.docs.replace(REG_DATE, 'new Date($&)') + ']'))();
353
+ filters.compare2(docs, update, updateflush);
354
+ } catch (e) {
355
+ F.error('TextDB("' + self.filename + '").update()', e);
356
+ return false;
357
+ }
358
+ };
359
+
360
+ fs.$callback = function() {
361
+
362
+ fs = null;
363
+ self.$writing = false;
364
+ self.next(0);
365
+
366
+ for (var i = 0; i < filters.builders.length; i++) {
367
+ var builder = filters.builders[i];
368
+ builder.logrule && builder.logrule();
369
+ builder.done();
370
+ }
371
+
372
+ if (filters.total > 0)
373
+ filters.db.total = filters.total;
374
+
375
+ };
376
+
377
+ fs.openupdate();
378
+ return self;
379
+ };
380
+
381
+ NoSQL.prototype.$reader = function(items, reader) {
382
+
383
+ var self = this;
384
+ self.step = 4;
385
+
386
+ if (!self.pending_reader.length) {
387
+ self.next(0);
388
+ return self;
389
+ }
390
+
391
+ var filters = NoSQLReader.make(self.pending_reader.splice(0));
392
+
393
+ filters.type = 'read';
394
+ filters.db = self;
395
+ filters.inmemory = false;
396
+
397
+ var fs = new NoSQLStream(self.filename);
398
+
399
+ self.$reading++;
400
+
401
+ if (self.buffersize)
402
+ fs.buffersize = self.buffersize;
403
+
404
+ if (self.buffercount)
405
+ fs.buffercount = self.buffercount;
406
+
407
+ var memory = !filters.cancelable && self.inmemory ? [] : null;
408
+
409
+ fs.ondocuments = function() {
410
+
411
+ try {
412
+
413
+ var docs = (new Function('return [' + fs.docs.replace(REG_DATE, 'new Date($&)') + ']'))();
414
+
415
+ if (memory) {
416
+ for (var i = 0; i < docs.length; i++)
417
+ memory.push(docs[i]);
418
+ }
419
+
420
+ return filters.compare(docs);
421
+
422
+ } catch (e) {
423
+ F.error('TextDB("' + self.filename + '").read()', e);
424
+ return false;
425
+ }
426
+ };
427
+
428
+ fs.$callback = function() {
429
+ self.filesize = fs.stats.size;
430
+ self.$reading--;
431
+ filters.done();
432
+ fs = null;
433
+ self.next(0);
434
+ };
435
+
436
+ if (reader)
437
+ fs.openstream(reader);
438
+ else
439
+ fs.openread();
440
+
441
+ return self;
442
+ };
443
+
444
+ NoSQL.prototype.$reader2 = function() {
445
+
446
+ var self = this;
447
+ self.step = 11;
448
+
449
+ if (!self.pending_reader2.length) {
450
+ self.next(0);
451
+ return self;
452
+ }
453
+
454
+ var filters = NoSQLReader.make(self.pending_reader2.splice(0));
455
+ filters.type = 'readreverse';
456
+ filters.db = self;
457
+ filters.inmemory = false;
458
+
459
+ self.$reading++;
460
+
461
+ var fs = new NoSQLStream(self.filename);
462
+
463
+ if (self.buffersize)
464
+ fs.buffersize = self.buffersize;
465
+
466
+ if (self.buffercount)
467
+ fs.buffercount = self.buffercount;
468
+
469
+ fs.ondocuments = function() {
470
+ try {
471
+ var data = (new Function('return [' + fs.docs.replace(REG_DATE, 'new Date($&)') + ']'))();
472
+ data.reverse();
473
+ return filters.compare(data);
474
+ } catch (e) {
475
+ F.error('TextDB("' + self.filename + '").read()', e);
476
+ return false;
477
+ }
478
+ };
479
+
480
+ fs.$callback = function() {
481
+ self.filesize = fs.stats.size;
482
+ filters.done();
483
+ self.$reading--;
484
+ fs = null;
485
+ self.next(0);
486
+ };
487
+
488
+ fs.openreadreverse();
489
+ return self;
490
+ };
491
+
492
+ NoSQL.prototype.$streamer = function() {
493
+
494
+ var self = this;
495
+ self.step = 10;
496
+
497
+ if (!self.pending_streamer.length) {
498
+ self.next(0);
499
+ return self;
500
+ }
501
+
502
+ self.$reading++;
503
+
504
+ var filter = self.pending_streamer.splice(0);
505
+ var length = filter.length;
506
+ var count = 0;
507
+ var fs = new NoSQLStream(self.filename);
508
+
509
+ if (self.buffersize)
510
+ fs.buffersize = self.buffersize;
511
+
512
+ if (self.buffercount)
513
+ fs.buffercount = self.buffercount;
514
+
515
+ fs.ondocuments = function() {
516
+ try {
517
+ var docs = (new Function('return [' + fs.docs.replace(REG_DATE, 'new Date($&)') + ']'))();
518
+ for (var j = 0; j < docs.length; j++) {
519
+ var json = docs[j];
520
+ count++;
521
+ for (var i = 0; i < length; i++)
522
+ filter[i].fn(json, filter[i].arg, count);
523
+ }
524
+ } catch (e) {
525
+ F.error('TextDB("' + self.filename + '").stream()', e);
526
+ return false;
527
+ }
528
+ };
529
+
530
+ fs.$callback = function() {
531
+ for (var i = 0; i < length; i++)
532
+ filter[i].callback && filter[i].callback(null, filter[i].arg, count);
533
+ self.$reading--;
534
+ self.next(0);
535
+ fs = null;
536
+ };
537
+
538
+ fs.openread();
539
+ return self;
540
+ };
541
+
542
+ NoSQL.prototype.$remove = function() {
543
+
544
+ var self = this;
545
+ self.step = 3;
546
+
547
+ if (!self.pending_remove.length) {
548
+ self.next(0);
549
+ return;
550
+ }
551
+
552
+ self.$writing = true;
553
+
554
+ var fs = new NoSQLStream(self.filename);
555
+ var filter = self.pending_remove.splice(0);
556
+ var filters = NoSQLReader.make(filter);
557
+ var change = false;
558
+
559
+ filters.type = 'remove';
560
+ filters.db = self;
561
+
562
+ if (self.buffersize)
563
+ fs.buffersize = self.buffersize;
564
+
565
+ if (self.buffercount)
566
+ fs.buffercount = self.buffercount;
567
+
568
+ var remove = function(docs, d, dindex, f) {
569
+ filters.total--;
570
+ f.backuprule && f.backuprule(fs.docsbuffer[dindex].doc);
571
+ self.onremove && self.onremove(fs.docsbuffer[dindex].doc);
572
+ return 1;
573
+ };
574
+
575
+ var removeflush = function(docs, d, dindex) {
576
+ var rec = fs.docsbuffer[dindex];
577
+ !change && (change = true);
578
+ fs.write(fs.remchar + rec.doc.substring(1) + NEWLINE, rec.position);
579
+ };
580
+
581
+ fs.ondocuments = function() {
582
+ try {
583
+ var docs = (new Function('return [' + fs.docs.replace(REG_DATE, 'new Date($&)') + ']'))();
584
+ filters.compare2(docs, remove, removeflush);
585
+ } catch (e) {
586
+ F.error('TextDB("' + self.filename + '").read()', e);
587
+ return false;
588
+ }
589
+ };
590
+
591
+ fs.$callback = function() {
592
+ filters.done();
593
+ fs = null;
594
+ self.$writing = false;
595
+ self.next(0);
596
+ };
597
+
598
+ fs.openupdate();
599
+ };
600
+
601
+ NoSQL.prototype.$clear = function() {
602
+
603
+ var self = this;
604
+ self.step = 12;
605
+
606
+ if (!self.pending_clear.length) {
607
+ self.next(0);
608
+ return;
609
+ }
610
+
611
+ var filter = self.pending_clear.splice(0);
612
+ F.Fs.unlink(self.filename, function() {
613
+
614
+ self.total = 0;
615
+
616
+ for (var i = 0; i < filter.length; i++)
617
+ filter[i]();
618
+
619
+ self.total = 0;
620
+ self.filesize = 0;
621
+ self.next(0);
622
+ });
623
+ };
624
+
625
+ NoSQL.prototype.$clean = function() {
626
+
627
+ var self = this;
628
+ self.step = 13;
629
+
630
+ if (!self.pending_clean.length) {
631
+ self.next(0);
632
+ return;
633
+ }
634
+
635
+ var filter = self.pending_clean.splice(0);
636
+ var length = filter.length;
637
+
638
+ var fs = new NoSQLStream(self.filename);
639
+ var writer = F.Fs.createWriteStream(self.filename + '-tmp');
640
+
641
+ if (self.buffersize)
642
+ fs.buffersize = self.buffersize;
643
+
644
+ if (self.buffercount)
645
+ fs.buffercount = self.buffercount;
646
+
647
+ fs.divider = NEWLINE;
648
+
649
+ fs.ondocuments = function() {
650
+ fs.docs && writer.write(fs.docs + NEWLINE);
651
+ };
652
+
653
+ fs.$callback = function() {
654
+ writer.end();
655
+ };
656
+
657
+ writer.on('finish', function() {
658
+ F.Fs.rename(self.filename + '-tmp', self.filename, function() {
659
+ for (var i = 0; i < length; i++)
660
+ filter[i]();
661
+ self.next(0);
662
+ fs = null;
663
+ });
664
+ });
665
+
666
+ fs.openread();
667
+ };
668
+
669
+ NoSQL.prototype.$drop = function() {
670
+ var self = this;
671
+ self.step = 7;
672
+
673
+ if (!self.pending_drops) {
674
+ self.next(0);
675
+ return;
676
+ }
677
+
678
+ self.pending_drops = false;
679
+
680
+ var remove = [self.filename];
681
+ remove.wait((filename, next) => F.Fs.unlink(filename, next), function() {
682
+ self.total = 0;
683
+ self.filesize = 0;
684
+ self.next(0);
685
+ }, 5);
686
+ };
687
+
688
+ NoSQL.prototype.insert = function() {
689
+ var self = this;
690
+ var builder = new NoSQLQueryBuilder(self);
691
+ self.pending_append.push(builder);
692
+ setImmediate(next_operation, self, 1);
693
+ return builder;
694
+ };
695
+
696
+ NoSQL.prototype.$count = function() {
697
+
698
+ var self = this;
699
+ self.step = 5;
700
+
701
+ if (!self.pending_count) {
702
+ self.next(0);
703
+ return self;
704
+ }
705
+
706
+ self.pending_count = 0;
707
+ self.$reading++;
708
+
709
+ var fs = new NoSQLStream(self.filename);
710
+
711
+ // Table
712
+ if (self.$header) {
713
+ fs.start = self.$header;
714
+ fs.linesize = self.$size;
715
+ }
716
+
717
+ fs.divider = '\n';
718
+
719
+ if (self.buffersize)
720
+ fs.buffersize = self.buffersize;
721
+
722
+ if (self.buffercount)
723
+ fs.buffercount = self.buffercount;
724
+
725
+ fs.ondocuments = NOOP;
726
+
727
+ fs.$callback = function() {
728
+
729
+ self.filesize = fs.stats.size;
730
+ self.total = fs.indexer;
731
+
732
+ fs = null;
733
+ self.$reading--;
734
+ self.next(0);
735
+ };
736
+
737
+ fs.openread();
738
+ return self;
739
+ };
740
+
741
+ exports.create = filename => new NoSQL(filename);
742
+
743
+
744
+ function NoSQLWrapper(filename) {
745
+ this.filename = filename;
746
+ }
747
+
748
+ NoSQLWrapper.prototype.insert = function(data) {
749
+ return DATA.insert('nosql/' + this.filename, data);
750
+ };
751
+
752
+ NoSQLWrapper.prototype.update = NoSQLWrapper.prototype.modify = function(data) {
753
+ return DATA.update('nosql/' + this.filename, data);
754
+ };
755
+
756
+ NoSQLWrapper.prototype.remove = function() {
757
+ return DATA.remove('nosql/' + this.filename);
758
+ };
759
+
760
+ NoSQLWrapper.prototype.find = function() {
761
+ return DATA.find('nosql/' + this.filename);
762
+ };
763
+
764
+ NoSQLWrapper.prototype.clean = function() {
765
+ return DATA.command('nosql/' + this.filename, 'clean');
766
+ };
767
+
768
+ NoSQLWrapper.prototype.drop = function() {
769
+ return DATA.drop('nosql/' + this.filename);
770
+ };
771
+
772
+ NoSQLWrapper.prototype.read = function() {
773
+ return DATA.read('nosql/' + this.filename);
774
+ };
775
+
776
+ NoSQLWrapper.prototype.clear = function() {
777
+ return DATA.clear('nosql/' + this.filename);
778
+ };
779
+
780
+ exports.nosql = function(filename) {
781
+ return new NoSQLWrapper(filename);
782
+ };