@xuda.io/xuda-dbs-plugin-xuda 1.0.46 → 1.0.47

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/server.js +699 -5
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xuda.io/xuda-dbs-plugin-xuda",
3
- "version": "1.0.46",
3
+ "version": "1.0.47",
4
4
  "description": "Xuda Database Socket for Xuda's proprietary structure powered by CouchDB",
5
5
  "scripts": {
6
6
  "pub": "npm version patch --force && npm publish --access public"
package/server.js CHANGED
@@ -1,6 +1,6 @@
1
1
  const _ = require("lodash");
2
2
 
3
- const check_unique = async function (e, docP, table_obj) {
3
+ const check_unique = async function (e, docP, table_obj, db, app_id_reference) {
4
4
  var len = docP.udfIndex.length;
5
5
  if (!len) {
6
6
  return { code: -11, data: "table must have primary index" };
@@ -72,7 +72,8 @@ const check_unique = async function (e, docP, table_obj) {
72
72
  try {
73
73
  // const json = await _this.runtime_xuda_driver("runtime_get_index", e);
74
74
  e.view = "db_index";
75
- const json = await exports.read(params, setup_doc, resolve, reject);
75
+ const ret = await query_db(e, db, app_id_reference, table_obj);
76
+ const json = ret.data;
76
77
 
77
78
  // var data;
78
79
 
@@ -186,6 +187,690 @@ const get_cast_val = async function (source, attributeP, typeP, valP) {
186
187
  }
187
188
  );
188
189
  };
190
+ const query_db = async function (e, db, app_id_reference, table_obj) {
191
+ var key;
192
+
193
+ const runtime_get_mango_data = async function () {
194
+ const done = async function (body) {
195
+ const raw_data = async function () {
196
+ var rows = [];
197
+
198
+ for await (var val of body.docs) {
199
+ var data = {};
200
+ if (e.fields) {
201
+ for await (const [key2, val2] of Object.entries(
202
+ typeof e.fields === "string" ? e.fields.split(",") : e.fields
203
+ )) {
204
+ if (val.udfData) {
205
+ data[val2] = val["udfData"]["data"][val2];
206
+ } else {
207
+ data[val2] = null;
208
+ }
209
+ }
210
+ } else {
211
+ for await (const [key2, val2] of Object.entries(
212
+ table_obj.tableFields
213
+ )) {
214
+ if (val.udfData) {
215
+ data[val2.data.field_id] =
216
+ val["udfData"]["data"][val2.data.field_id];
217
+ } else {
218
+ data[val2.data.field_id] = null;
219
+ }
220
+ }
221
+ }
222
+
223
+ rows.push({
224
+ id: val._id,
225
+ value: {
226
+ udfData: { data: data },
227
+ _id: val._id,
228
+ _rev: val._rev,
229
+ },
230
+ });
231
+ }
232
+
233
+ return { code: 1, data: { rows: rows, total_rows: rows.length } };
234
+ };
235
+ const count_data = async function () {
236
+ var rows = [];
237
+ var keys_obj = {};
238
+ const table_index = find_item_by_key_root(
239
+ table_obj.tableIndexes,
240
+ "id",
241
+ e.indexId
242
+ );
243
+ if (e.indexId && table_index) {
244
+ var index_keys = table_index.data.keys;
245
+ var index_id = e.indexId;
246
+
247
+ for await (var val of body.docs) {
248
+ var data = {};
249
+ const _tableFieldsObj = find_item_by_key(
250
+ table_obj.tableFields,
251
+ "field_id",
252
+ e.field_id
253
+ );
254
+
255
+ for await (const [key2, val2] of Object.entries(
256
+ table_obj.tableFields
257
+ )) {
258
+ data[val2.data.field_id] =
259
+ val["udfData"]["data"][val2.data.field_id];
260
+ }
261
+
262
+ var key_arr = [];
263
+
264
+ for await (const [key_idx, key_name] of Object.entries(
265
+ index_keys
266
+ )) {
267
+ key_arr.push(data[key_name]);
268
+ }
269
+ keys_obj[key_arr]++;
270
+ }
271
+
272
+ for await (const [key_arr, key_count] of Object.entries(keys_obj)) {
273
+ rows.push({
274
+ key: [e.key, index_id, key_arr],
275
+ value: body.docs.length,
276
+ });
277
+ }
278
+ } else {
279
+ // no index
280
+ rows.push({ key: "", value: body.docs.length });
281
+ }
282
+ return { code: 1, data: { rows: rows, total_rows: rows.length } };
283
+ };
284
+
285
+ const totals = async function () {
286
+ const median = (arr) => {
287
+ const mid = Math.floor(arr.length / 2),
288
+ nums = [...arr].sort((a, b) => a - b);
289
+ return arr.length % 2 !== 0
290
+ ? nums[mid]
291
+ : (nums[mid - 1] + nums[mid]) / 2;
292
+ };
293
+
294
+ var total_fields_info = JSON.parse(e.total_fields_info);
295
+ var totals_prop = {};
296
+ var totals_obj = {};
297
+ var totals_counts = {};
298
+ var totals_arr = {};
299
+ var totals_sums = {};
300
+ var totals_group_obj = {};
301
+
302
+ for await (const [key, val] of Object.entries(total_fields_info)) {
303
+ totals_counts[val.field_id] = 0;
304
+ totals_prop[val.field_id] = val;
305
+ totals_arr[val.field_id] = [];
306
+ totals_sums[val.field_id] = 0;
307
+ totals_group_obj[val.field_id] = {};
308
+ }
309
+
310
+ for await (var row_data of body.docs) {
311
+ for await (const [key, val] of Object.entries(
312
+ row_data.udfData.data
313
+ )) {
314
+ var field_id = key;
315
+ var value = val;
316
+ totals_counts[field_id]++;
317
+
318
+ switch (totals_prop[field_id].sum_type) {
319
+ case "sum":
320
+ if (typeof totals_obj[field_id] === "undefined") {
321
+ totals_obj[field_id] = value;
322
+ } else {
323
+ totals_obj[field_id] += value;
324
+ }
325
+ break;
326
+
327
+ case "average":
328
+ totals_sums[field_id] += value;
329
+ totals_obj[field_id] =
330
+ totals_sums[field_id] / totals_counts[field_id];
331
+ break;
332
+
333
+ case "median":
334
+ if (!totals_arr[field_id].includes(value)) {
335
+ totals_arr[field_id].push(value);
336
+ }
337
+ totals_obj[field_id] = median(totals_arr[field_id]);
338
+ break;
339
+ case "min":
340
+ if (
341
+ typeof totals_obj[field_id] === "undefined" ||
342
+ value < totals_obj[field_id]
343
+ ) {
344
+ totals_obj[field_id] = value;
345
+ }
346
+ break;
347
+ case "max":
348
+ if (
349
+ typeof totals_obj[field_id] === "undefined" ||
350
+ value > totals_obj[field_id]
351
+ ) {
352
+ totals_obj[field_id] = value;
353
+ }
354
+ break;
355
+ case "count":
356
+ if (typeof totals_obj[field_id] === "undefined") {
357
+ totals_obj[field_id] = 1;
358
+ } else {
359
+ totals_obj[field_id]++;
360
+ }
361
+ break;
362
+ case "distinct":
363
+ if (!totals_arr[field_id].includes(value)) {
364
+ totals_arr[field_id].push(value);
365
+ }
366
+ totals_obj[field_id] = totals_arr[field_id].length;
367
+ break;
368
+
369
+ case "group":
370
+ if (!totals_obj[field_id]) {
371
+ totals_obj[field_id] = {};
372
+ }
373
+
374
+ if (typeof totals_obj[field_id][value] === "undefined") {
375
+ totals_obj[field_id][value] = 1; //value;
376
+ } else {
377
+ totals_obj[field_id][value]++; //+= value;
378
+ }
379
+
380
+ break;
381
+ default:
382
+ totals_obj[field_id] = null;
383
+ }
384
+ }
385
+ }
386
+ return { code: 1, data: totals_obj };
387
+ };
388
+ if (e.count === "true" || e.count === true) {
389
+ return count_data();
390
+ }
391
+ if (e.total_fields_info) {
392
+ return totals();
393
+ }
394
+ return raw_data();
395
+ };
396
+
397
+ var limit = 99999;
398
+ var skip = 0;
399
+
400
+ if (e.limit) {
401
+ limit = parseInt(e.limit);
402
+ }
403
+
404
+ if (e.skip) {
405
+ skip = JSON.parse(e.skip);
406
+ }
407
+
408
+ var fields = [];
409
+ if (e.fields) {
410
+ for await (const [key, val] of Object.entries(
411
+ typeof e.fields === "string" ? e.fields.split(",") : e.fields
412
+ )) {
413
+ fields.push(val);
414
+ }
415
+ } else {
416
+ for await (const [key, val] of Object.entries(table_obj.tableFields)) {
417
+ fields.push(val.data.field_id);
418
+ }
419
+ }
420
+
421
+ var data = {};
422
+ var from = e.filter_from ? JSON.parse(e.filter_from) : {};
423
+ var to = e.filter_to ? JSON.parse(e.filter_to) : {};
424
+ var sort = [];
425
+ for await (const [key, val] of Object.entries(from)) {
426
+ var field_name = key;
427
+
428
+ sort.push(field_name);
429
+
430
+ if (val === to[key]) {
431
+ data[field_name] = val;
432
+ } else {
433
+ data[field_name] = { $gte: val };
434
+ }
435
+ }
436
+
437
+ for await (const [key, val] of Object.entries(to)) {
438
+ var field_name = key;
439
+
440
+ if (val !== from[key]) {
441
+ data[field_name]["$lte"] = val;
442
+ }
443
+ }
444
+
445
+ var selector = {};
446
+
447
+ if (from && !_.isEmpty(from)) {
448
+ selector = data;
449
+ }
450
+
451
+ var opt = {
452
+ selector: selector,
453
+ limit: limit,
454
+ skip: skip,
455
+ fields: fields,
456
+ };
457
+
458
+ if (sort.length) {
459
+ opt.sort = sort;
460
+ }
461
+
462
+ if (e.sort_fields && JSON.parse(e.sort_fields).length) {
463
+ opt.sort = JSON.parse(e.sort_fields);
464
+ if (opt.sort) {
465
+ for await (const [key, val] of Object.entries(opt.sort)) {
466
+ var field_name = val.colId;
467
+ opt.sort[key] = {
468
+ ["udfData.data." + field_name]: val.sort,
469
+ };
470
+ }
471
+ }
472
+ }
473
+
474
+ if (e.grid_filter_info) {
475
+ for await (const [key, val] of Object.entries(
476
+ JSON.parse(e.grid_filter_info)
477
+ )) {
478
+ var field_name = key;
479
+ var condition = "$and";
480
+ const make_selector = function (val) {
481
+ var value = "";
482
+ var operator = "";
483
+ var value_to = "";
484
+ var operator_to = "";
485
+
486
+ if (val.filterType === "date") {
487
+ var date = val.dateFrom.substr(0, 10);
488
+ switch (val.type) {
489
+ case "equals":
490
+ operator = "$regex";
491
+ value = `^${date}`;
492
+ break;
493
+
494
+ case "greaterThan":
495
+ operator = "$gt";
496
+ value = `${date}`;
497
+ break;
498
+
499
+ case "lessThan":
500
+ operator = "$lt";
501
+ value = `${date}`;
502
+ break;
503
+
504
+ case "blank":
505
+ operator = "$eq";
506
+ value = "";
507
+ break;
508
+
509
+ case "inRange":
510
+ operator = "$gte";
511
+ value = date;
512
+ operator_to = "$lte";
513
+ value_to = val.dateTo.substr(0, 10);
514
+ break;
515
+
516
+ case "notEqual":
517
+ operator = "$regex";
518
+ value = `^((?!${date}).)*$`;
519
+ break;
520
+
521
+ case "notBlank":
522
+ operator = "$ne";
523
+ value = "";
524
+ break;
525
+
526
+ default:
527
+ operator = "$regex";
528
+ value = `^${date}`;
529
+ }
530
+ }
531
+
532
+ if (val.filterType === "text") {
533
+ switch (val.type) {
534
+ case "contains":
535
+ operator = "$regex";
536
+ value = `${val.filter}`;
537
+ break;
538
+
539
+ case "notContains":
540
+ operator = "$regex";
541
+ value = `^((?!${val.filter}).)*$`;
542
+ break;
543
+
544
+ case "equals":
545
+ operator = "$eq";
546
+ value = `${val.filter}`;
547
+ break;
548
+
549
+ case "notEqual":
550
+ operator = "$ne";
551
+ value = `${val.filter}`;
552
+ break;
553
+
554
+ case "startsWith":
555
+ operator = "$regex";
556
+ value = `^${val.filter}`;
557
+ break;
558
+
559
+ case "endsWith":
560
+ operator = "$regex";
561
+ value = `${val.filter}$`;
562
+ break;
563
+
564
+ case "blank":
565
+ operator = "$eq";
566
+ value = "";
567
+ break;
568
+
569
+ case "notBlank":
570
+ operator = "$ne";
571
+ value = "";
572
+ break;
573
+
574
+ default:
575
+ value = "^" + val.filter;
576
+ }
577
+ }
578
+
579
+ if (val.filterType === "number") {
580
+ switch (val.type) {
581
+ case "equals":
582
+ operator = "$eq";
583
+ value = val.filter;
584
+ break;
585
+
586
+ case "notEqual":
587
+ operator = "$ne";
588
+ value = val.filter;
589
+ break;
590
+
591
+ case "lessThanOrEqual":
592
+ operator = "$lte";
593
+ value = val.filter;
594
+ break;
595
+
596
+ case "greaterThanOrEqual":
597
+ operator = "$gte";
598
+ value = val.filter;
599
+ break;
600
+
601
+ case "lessThan":
602
+ operator = "$lt";
603
+ value = val.filter;
604
+ break;
605
+
606
+ case "greaterThan":
607
+ operator = "$gt";
608
+ value = val.filter;
609
+ break;
610
+
611
+ case "blank":
612
+ operator = "$eq";
613
+ value = 0;
614
+ break;
615
+
616
+ case "notBlank":
617
+ operator = "$ne";
618
+ value = 0;
619
+ break;
620
+
621
+ case "inRange":
622
+ operator = "$gte";
623
+ value = val.filter;
624
+ operator_to = "$lte";
625
+ value_to = val.filterTo;
626
+ break;
627
+
628
+ default:
629
+ operator = "$eq";
630
+ value = val.filter;
631
+ }
632
+ }
633
+ if (!opt.selector[field_name]) {
634
+ opt.selector[field_name] = {};
635
+ }
636
+ if (!opt.selector[field_name][condition]) {
637
+ opt.selector[field_name][condition] = [];
638
+ }
639
+ opt.selector[field_name][condition].push({
640
+ [operator]: value,
641
+ });
642
+
643
+ if (operator_to) {
644
+ opt.selector[field_name][condition].push({
645
+ [operator_to]: value_to,
646
+ });
647
+ }
648
+ };
649
+
650
+ if (!val.condition1) {
651
+ make_selector(val);
652
+ } else {
653
+ condition = "$" + val.operator.toLowerCase();
654
+ make_selector(val.condition1);
655
+ make_selector(val.condition2);
656
+ }
657
+ }
658
+ }
659
+
660
+ if (e.total_fields_info) {
661
+ fields = [];
662
+ for await (const [key, val] of Object.entries(
663
+ JSON.parse(e.total_fields_info)
664
+ )) {
665
+ var field_name = val.field_id;
666
+
667
+ fields.push(field_name);
668
+ }
669
+ opt.fields = fields;
670
+ }
671
+ // fix names
672
+
673
+ for await (const [key, val] of Object.entries(opt.fields)) {
674
+ opt.fields[key] = "udfData.data." + val;
675
+ }
676
+ if (!e.sort_fields || !JSON.parse(e.sort_fields).length) {
677
+ // added 2021 09 10
678
+ if (opt.sort) {
679
+ for await (const [key, val] of Object.entries(opt.sort)) {
680
+ opt.sort[key] = {
681
+ ["udfData.data." + val]: e.sortOrder === "des" ? "desc" : "asc",
682
+ };
683
+ }
684
+ }
685
+ }
686
+
687
+ var selector_new = {};
688
+
689
+ for await (const [key, val] of Object.entries(opt.selector)) {
690
+ selector_new["udfData.data." + key] = val;
691
+ }
692
+
693
+ if (e.viewDbQuery) {
694
+ for await (const [key, val] of Object.entries(table_obj.tableFields)) {
695
+ if (e.viewDbQuery.includes(val.id)) {
696
+ const replacer = new RegExp(val.id, "g");
697
+ e.viewDbQuery = e.viewDbQuery.replace(
698
+ replacer,
699
+ "udfData.data." + val.data.field_id
700
+ );
701
+ }
702
+ }
703
+
704
+ let viewDbQuery = JSON.parse(e.viewDbQuery.replace(/\\/g, ""));
705
+
706
+ for await (const [key, val] of Object.entries(viewDbQuery)) {
707
+ selector_new[key] = val;
708
+ }
709
+ }
710
+
711
+ opt.selector = selector_new;
712
+ opt.selector["udfData.udffileid"] = e.table_id;
713
+ opt.selector.stat = e.archived ? 4 : 3;
714
+
715
+ opt.fields.push("_id");
716
+ opt.fields.push("_rev");
717
+
718
+ // xuda
719
+ var cache_string = JSON.stringify(opt);
720
+
721
+ if (__.CACHE_QUERY_REQ?.[app_id_reference]?.[e.table_id]?.[cache_string]) {
722
+ return await done(
723
+ __.CACHE_QUERY_REQ[app_id_reference][e.table_id][cache_string].doc
724
+ );
725
+ }
726
+
727
+ try {
728
+ try {
729
+ const doc = await db.find(opt);
730
+ if (!__.CACHE_QUERY_REQ[app_id_reference]) {
731
+ __.CACHE_QUERY_REQ[app_id_reference] = {};
732
+ }
733
+
734
+ if (!__.CACHE_QUERY_REQ[app_id_reference][e.table_id]) {
735
+ __.CACHE_QUERY_REQ[app_id_reference][e.table_id] = {};
736
+ }
737
+
738
+ __.CACHE_QUERY_REQ[app_id_reference][e.table_id][cache_string] = {
739
+ doc: doc,
740
+ ts: Date.now(),
741
+ };
742
+
743
+ if (doc?.warning?.includes("No matching index found")) {
744
+ const index_name = `index_${e.table_id}_${new Date()
745
+ .valueOf()
746
+ .toString()}`;
747
+ var index = [];
748
+
749
+ for await (const [key, val] of Object.entries(opt.selector)) {
750
+ index.push(key);
751
+ }
752
+ mango_index_obj = {
753
+ index: {
754
+ fields: index,
755
+ },
756
+ name: index_name,
757
+ ddoc: `mango_index_table_${e.table_id}`,
758
+ };
759
+ db.createIndex(mango_index_obj).then((result) => {
760
+ console.log(result);
761
+ });
762
+ }
763
+ return await done(doc);
764
+ } catch (err) {
765
+ if (err.error === "no_usable_index") {
766
+ const index_name = `index_${e.table_id}_${new Date()
767
+ .valueOf()
768
+ .toString()}`;
769
+
770
+ mango_index_obj = {
771
+ index: {
772
+ fields: opt.sort,
773
+ },
774
+ name: index_name,
775
+ ddoc: `mango_index_table_${e.table_id}`,
776
+ };
777
+
778
+ try {
779
+ const result = await db.createIndex(mango_index_obj);
780
+
781
+ const doc = await db.find(opt);
782
+ return await done(doc);
783
+ } catch (error) {
784
+ return { code: -1, data: error };
785
+ }
786
+ } else {
787
+ return { code: -1, data: err.message };
788
+ }
789
+ }
790
+ } catch (err) {
791
+ return { code: -1, data: err.error };
792
+ }
793
+ };
794
+
795
+ const runtime_get_query_data = async function () {
796
+ var limit = 99999;
797
+ var skip = 0;
798
+
799
+ if (e.limit) {
800
+ limit = e.limitP;
801
+ }
802
+
803
+ if (e.skipP) {
804
+ skip = e.skipP;
805
+ }
806
+
807
+ var desc = e.desc === "true" ? true : false;
808
+
809
+ var opt;
810
+ if (key) {
811
+ opt = {
812
+ key: key,
813
+ include_docs: !e.ids && view !== "db_index_reduce",
814
+ limit: limit,
815
+ skip: skip,
816
+ };
817
+ } else {
818
+ if (e.reduce == "true") {
819
+ opt = {
820
+ startkey: e.startkey ? JSON.parse(e.startkey) : "",
821
+ endkey: e.endkey ? JSON.parse(e.endkey) : "",
822
+ include_docs: false,
823
+ reduce: true,
824
+ group_level: 3,
825
+
826
+ descending: desc,
827
+ };
828
+ } else {
829
+ opt = {
830
+ startkey: e.startkey ? JSON.parse(e.startkey) : "",
831
+ endkey: e.endkey ? JSON.parse(e.endkey) : "",
832
+ include_docs: !e.ids,
833
+ limit: limit,
834
+ skip: skip,
835
+ descending: desc,
836
+ };
837
+ }
838
+ }
839
+
840
+ try {
841
+ const body = db.view(`xuda`, view, opt);
842
+ if (e.reduce) {
843
+ return { code: 1, data: body };
844
+ }
845
+ var rows = [];
846
+
847
+ for await (var val of body.rows) {
848
+ if (e.ids) {
849
+ rows.push({ id: val.id, value: val.value._id });
850
+ return true;
851
+ }
852
+ rows.push({
853
+ id: val.id,
854
+ value: { udfData: val.doc.udfData, _id: val.id },
855
+ });
856
+ }
857
+ return { code: 1, data: { rows: rows, total_rows: rows.length } };
858
+ } catch (error) {
859
+ return { code: -1, data: error };
860
+ }
861
+ };
862
+
863
+ if (e.reduce && JSON.parse(e.reduce)) {
864
+ view = "db_index_reduce";
865
+ return await runtime_get_query_data();
866
+ }
867
+ if (e.view === "db_index") {
868
+ key = e.key;
869
+ view = e.view;
870
+ return await runtime_get_query_data();
871
+ }
872
+ return await runtime_get_mango_data();
873
+ };
189
874
 
190
875
  exports.create = async (params, setup_doc, resolve, reject) => {
191
876
  const e = params.e;
@@ -215,7 +900,7 @@ exports.create = async (params, setup_doc, resolve, reject) => {
215
900
  if (index_ret < 0) throw index_ret.data;
216
901
  doc.udfIndex = index_ret.data;
217
902
 
218
- const ret = await check_unique(e, doc, table_obj);
903
+ const ret = await check_unique(e, doc, table_obj, db, app_id_reference);
219
904
  if (ret.code < 0) {
220
905
  throw ret;
221
906
  }
@@ -251,7 +936,7 @@ exports.create = async (params, setup_doc, resolve, reject) => {
251
936
  return index_ret;
252
937
  }
253
938
  doc.udfIndex = index_ret.data;
254
- const ret = await check_unique(e, doc, table_obj);
939
+ const ret = await check_unique(e, doc, table_obj, db, app_id_reference);
255
940
  if (ret.code < 0) {
256
941
  return ret;
257
942
  }
@@ -273,6 +958,13 @@ exports.read = async (params, setup_doc, resolve, reject) => {
273
958
  const app_id_reference = params.app_id_reference;
274
959
  const table_obj = params.table_obj;
275
960
 
961
+ const ret = await query_db(e, db, app_id_reference, table_obj);
962
+ if (ret.code < 0) {
963
+ return reject(ret.data);
964
+ }
965
+
966
+ return resolve(ret.data);
967
+
276
968
  var key;
277
969
 
278
970
  const runtime_get_mango_data = async function () {
@@ -991,7 +1683,7 @@ exports.update = async (params, setup_doc, resolve, reject) => {
991
1683
  return index_ret;
992
1684
  }
993
1685
  doc.udfIndex = index_ret.data;
994
- const ret = await check_unique(e, doc, table_obj);
1686
+ const ret = await check_unique(e, doc, table_obj, db, app_id_reference);
995
1687
  if (ret.code < 0) {
996
1688
  return ret;
997
1689
  }
@@ -1011,6 +1703,8 @@ exports.delete = async (params, setup_doc, resolve, reject) => {
1011
1703
  const app_id_reference = params.app_id_reference;
1012
1704
  const table_obj = params.table_obj;
1013
1705
  const data = await db.fetch({ keys: e.ids });
1706
+ var docs_to_delete = [];
1707
+
1014
1708
  for await (var val of data.rows) {
1015
1709
  var doc = val.doc;
1016
1710