@xuda.io/xuda-dbs-plugin-xuda 1.0.318 → 1.0.320
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 +1 -1
- package/server.mjs +86 -21
- package/studio.mjs +73 -50
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xuda.io/xuda-dbs-plugin-xuda",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.320",
|
|
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.mjs
CHANGED
|
@@ -515,27 +515,68 @@ const query_db = async function (e, db, app_id_reference, table_obj) {
|
|
|
515
515
|
return raw_data();
|
|
516
516
|
};
|
|
517
517
|
|
|
518
|
+
const get_mango_index_fields = function (selector) {
|
|
519
|
+
const fields = new Set();
|
|
520
|
+
const add_selector_fields = function (obj) {
|
|
521
|
+
if (!obj || typeof obj !== 'object' || Array.isArray(obj)) {
|
|
522
|
+
return;
|
|
523
|
+
}
|
|
524
|
+
for (const [key, val] of Object.entries(obj)) {
|
|
525
|
+
if (['$and', '$or', '$nor'].includes(key)) {
|
|
526
|
+
if (Array.isArray(val)) {
|
|
527
|
+
val.forEach(add_selector_fields);
|
|
528
|
+
}
|
|
529
|
+
continue;
|
|
530
|
+
}
|
|
531
|
+
if (key.startsWith('$')) {
|
|
532
|
+
continue;
|
|
533
|
+
}
|
|
534
|
+
fields.add(key);
|
|
535
|
+
}
|
|
536
|
+
};
|
|
537
|
+
add_selector_fields(selector);
|
|
538
|
+
return [...fields];
|
|
539
|
+
};
|
|
540
|
+
|
|
541
|
+
const create_mango_index_for_selector = async function () {
|
|
542
|
+
const index = get_mango_index_fields(opt.selector);
|
|
543
|
+
if (!index.length) {
|
|
544
|
+
return;
|
|
545
|
+
}
|
|
546
|
+
const index_suffix = index.join('_').replace(/[^a-zA-Z0-9_]+/g, '_').slice(0, 80) || 'selector';
|
|
547
|
+
const mango_index_obj = {
|
|
548
|
+
index: {
|
|
549
|
+
fields: index,
|
|
550
|
+
},
|
|
551
|
+
name: `index_${e.table_id}_${index_suffix}`,
|
|
552
|
+
ddoc: `mango_index_table_${e.table_id}_${index_suffix}`,
|
|
553
|
+
};
|
|
554
|
+
try {
|
|
555
|
+
await db.createIndex(mango_index_obj);
|
|
556
|
+
} catch (err) {
|
|
557
|
+
// The read already succeeded; index creation is only an optimization.
|
|
558
|
+
}
|
|
559
|
+
};
|
|
560
|
+
|
|
561
|
+
// A COUNT must span the WHOLE result set, not the current page. `opt` carries
|
|
562
|
+
// the page limit/skip, so counting body.docs.length under them could never
|
|
563
|
+
// exceed one page. Ask for ids only (unless count_data's index-grouping
|
|
564
|
+
// branch needs the field data) and cap the scan so a pathological table
|
|
565
|
+
// cannot stall the request.
|
|
566
|
+
const COUNT_SCAN_CAP = 100000;
|
|
567
|
+
const find_opt = e.count
|
|
568
|
+
? Object.assign({}, opt, {
|
|
569
|
+
limit: COUNT_SCAN_CAP,
|
|
570
|
+
skip: 0,
|
|
571
|
+
fields: e.indexId ? opt.fields : ['_id'],
|
|
572
|
+
})
|
|
573
|
+
: opt;
|
|
574
|
+
|
|
518
575
|
try {
|
|
519
576
|
try {
|
|
520
|
-
const doc = await db.find(
|
|
521
|
-
var mango_index_obj;
|
|
577
|
+
const doc = await db.find(find_opt);
|
|
522
578
|
if (doc?.warning?.includes('No matching index found')) {
|
|
523
|
-
|
|
524
|
-
var index = [];
|
|
525
|
-
|
|
526
|
-
for (const [key, val] of Object.entries(opt.selector)) {
|
|
527
|
-
index.push(key);
|
|
528
|
-
}
|
|
529
|
-
mango_index_obj = {
|
|
530
|
-
index: {
|
|
531
|
-
fields: index,
|
|
532
|
-
},
|
|
533
|
-
name: index_name,
|
|
534
|
-
ddoc: `mango_index_table_${e.table_id}`,
|
|
535
|
-
};
|
|
536
|
-
db.createIndex(mango_index_obj).then((result) => {
|
|
537
|
-
console.log(result);
|
|
538
|
-
});
|
|
579
|
+
await create_mango_index_for_selector();
|
|
539
580
|
}
|
|
540
581
|
return await done(doc);
|
|
541
582
|
} catch (err) {
|
|
@@ -544,7 +585,7 @@ const query_db = async function (e, db, app_id_reference, table_obj) {
|
|
|
544
585
|
const _selector = _.cloneDeep(opt.selector);
|
|
545
586
|
delete _selector['$and'];
|
|
546
587
|
const _sort = _.cloneDeep(opt.sort);
|
|
547
|
-
mango_index_obj = {
|
|
588
|
+
const mango_index_obj = {
|
|
548
589
|
index: {
|
|
549
590
|
fields: Object.keys(
|
|
550
591
|
_.reduce(
|
|
@@ -564,7 +605,7 @@ const query_db = async function (e, db, app_id_reference, table_obj) {
|
|
|
564
605
|
try {
|
|
565
606
|
const result = await db.createIndex(mango_index_obj);
|
|
566
607
|
|
|
567
|
-
const doc = await db.find(
|
|
608
|
+
const doc = await db.find(find_opt);
|
|
568
609
|
return await done(doc);
|
|
569
610
|
} catch (err) {
|
|
570
611
|
return { code: -1, data: err.message };
|
|
@@ -692,7 +733,31 @@ const query_db = async function (e, db, app_id_reference, table_obj) {
|
|
|
692
733
|
return await runtime_get_query_data();
|
|
693
734
|
}
|
|
694
735
|
|
|
695
|
-
|
|
736
|
+
// A COUNT must answer "how many rows match THIS query", not "how many rows are
|
|
737
|
+
// in the table". count_tables() reads the db_table_counts reduce view keyed by
|
|
738
|
+
// table_id alone, so it ignores the selector completely — and `filter_from` is
|
|
739
|
+
// only ever set for dataSourceFilterModelType==='index', so every MONGO-filtered
|
|
740
|
+
// count landed here and came back with the whole-table total. Live symptom: a
|
|
741
|
+
// filtered question list returned its correct rows while rows_found stayed 837,
|
|
742
|
+
// so the pager kept offering 168 pages over an empty/short result set.
|
|
743
|
+
// Keep the cheap view for a genuinely UNFILTERED count; anything carrying a
|
|
744
|
+
// selector is counted through Mango against that same selector.
|
|
745
|
+
const _has_filter_value = function (v) {
|
|
746
|
+
if (v === undefined || v === null || v === '') return false;
|
|
747
|
+
try {
|
|
748
|
+
const o = typeof v === 'string' ? JSON.parse(v) : v;
|
|
749
|
+
return !_.isEmpty(o);
|
|
750
|
+
} catch (err) {
|
|
751
|
+
return true; // present but unparseable — assume it filters; never over-count
|
|
752
|
+
}
|
|
753
|
+
};
|
|
754
|
+
const _count_is_filtered =
|
|
755
|
+
!!e.filter_from ||
|
|
756
|
+
_has_filter_value(e.filterModelMongo) ||
|
|
757
|
+
_has_filter_value(e.filterModelUserMongo) ||
|
|
758
|
+
_has_filter_value(e.filterModelNative);
|
|
759
|
+
|
|
760
|
+
if (e.count && !_count_is_filtered) {
|
|
696
761
|
return await count_tables();
|
|
697
762
|
}
|
|
698
763
|
|
package/studio.mjs
CHANGED
|
@@ -180,6 +180,20 @@ const get_cast_val = async function (source, attributeP, typeP, valP) {
|
|
|
180
180
|
return xu_cast(typeP, valP, success, fail);
|
|
181
181
|
};
|
|
182
182
|
|
|
183
|
+
const get_json_value = function (value, fallback) {
|
|
184
|
+
if (typeof value === 'undefined' || value === null || value === '') {
|
|
185
|
+
return fallback;
|
|
186
|
+
}
|
|
187
|
+
if (typeof value === 'string') {
|
|
188
|
+
try {
|
|
189
|
+
return JSON.parse(value);
|
|
190
|
+
} catch (err) {
|
|
191
|
+
return fallback;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
return value;
|
|
195
|
+
};
|
|
196
|
+
|
|
183
197
|
const get_opt = function (e, table_obj) {
|
|
184
198
|
var limit = 99999;
|
|
185
199
|
var skip = 0;
|
|
@@ -244,9 +258,9 @@ const get_opt = function (e, table_obj) {
|
|
|
244
258
|
opt.sort = sort;
|
|
245
259
|
}
|
|
246
260
|
|
|
247
|
-
let _sortModel = e?.sortModel
|
|
248
|
-
if (
|
|
249
|
-
_sortModel =
|
|
261
|
+
let _sortModel = get_json_value(e?.sortModel, []);
|
|
262
|
+
if (!Array.isArray(_sortModel)) {
|
|
263
|
+
_sortModel = [];
|
|
250
264
|
}
|
|
251
265
|
|
|
252
266
|
if (_sortModel.length) {
|
|
@@ -288,7 +302,7 @@ const get_opt = function (e, table_obj) {
|
|
|
288
302
|
opt.fields[key] = 'udfData.data.' + val;
|
|
289
303
|
}
|
|
290
304
|
|
|
291
|
-
if (!e?.sortModel || !
|
|
305
|
+
if (!e?.sortModel || !_sortModel.length) {
|
|
292
306
|
// added 2021 09 10
|
|
293
307
|
if (opt.sort) {
|
|
294
308
|
for (const [key, val] of Object.entries(opt.sort)) {
|
|
@@ -372,14 +386,16 @@ const get_opt = function (e, table_obj) {
|
|
|
372
386
|
return recursiveReplace(query);
|
|
373
387
|
}
|
|
374
388
|
|
|
375
|
-
|
|
376
|
-
|
|
389
|
+
const filterModelMongo = get_json_value(e.filterModelMongo, {});
|
|
390
|
+
if (e.dataSourceFilterModelType === 'query' && !_.isEmpty(filterModelMongo)) {
|
|
391
|
+
selector_new['$and'] = [replaceRegexOptions(replaceKeysInQuery(filterModelMongo))];
|
|
377
392
|
}
|
|
378
|
-
|
|
393
|
+
const filterModelUserMongo = get_json_value(e.filterModelUserMongo, {});
|
|
394
|
+
if (!_.isEmpty(filterModelUserMongo)) {
|
|
379
395
|
if (!selector_new['$and']) {
|
|
380
396
|
selector_new['$and'] = [];
|
|
381
397
|
}
|
|
382
|
-
selector_new['$and'].push(replaceRegexOptions(replaceKeysInQuery(
|
|
398
|
+
selector_new['$and'].push(replaceRegexOptions(replaceKeysInQuery(filterModelUserMongo)));
|
|
383
399
|
}
|
|
384
400
|
|
|
385
401
|
if (e.dataSourceFilterModelType === 'index') {
|
|
@@ -587,44 +603,66 @@ const query_db = async function (e, db, table_obj) {
|
|
|
587
603
|
};
|
|
588
604
|
|
|
589
605
|
// xuda
|
|
606
|
+
const get_mango_index_fields = function (selector) {
|
|
607
|
+
const fields = new Set();
|
|
608
|
+
const add_selector_fields = function (obj) {
|
|
609
|
+
if (!obj || typeof obj !== 'object' || Array.isArray(obj)) {
|
|
610
|
+
return;
|
|
611
|
+
}
|
|
612
|
+
for (const [key, val] of Object.entries(obj)) {
|
|
613
|
+
if (['$and', '$or', '$nor'].includes(key)) {
|
|
614
|
+
if (Array.isArray(val)) {
|
|
615
|
+
val.forEach(add_selector_fields);
|
|
616
|
+
}
|
|
617
|
+
continue;
|
|
618
|
+
}
|
|
619
|
+
if (key.startsWith('$')) {
|
|
620
|
+
continue;
|
|
621
|
+
}
|
|
622
|
+
fields.add(key);
|
|
623
|
+
}
|
|
624
|
+
};
|
|
625
|
+
add_selector_fields(selector);
|
|
626
|
+
return [...fields];
|
|
627
|
+
};
|
|
628
|
+
|
|
629
|
+
const create_mango_index_for_selector = async function () {
|
|
630
|
+
const index = get_mango_index_fields(opt.selector);
|
|
631
|
+
if (!index.length) {
|
|
632
|
+
return;
|
|
633
|
+
}
|
|
634
|
+
const index_suffix = index.join('_').replace(/[^a-zA-Z0-9_]+/g, '_').slice(0, 80) || 'selector';
|
|
635
|
+
const mango_index_obj = {
|
|
636
|
+
index: {
|
|
637
|
+
fields: index,
|
|
638
|
+
},
|
|
639
|
+
name: `index_${e.table_id}_${index_suffix}`,
|
|
640
|
+
ddoc: `mango_index_table_${e.table_id}_${index_suffix}`,
|
|
641
|
+
};
|
|
642
|
+
try {
|
|
643
|
+
await db.createIndex(mango_index_obj);
|
|
644
|
+
} catch (err) {
|
|
645
|
+
// The read already succeeded; index creation is only an optimization.
|
|
646
|
+
}
|
|
647
|
+
};
|
|
590
648
|
|
|
591
649
|
try {
|
|
592
650
|
try {
|
|
593
651
|
// console.log("opt", opt);
|
|
594
652
|
|
|
595
653
|
const doc = await db.find(opt);
|
|
596
|
-
var mango_index_obj;
|
|
597
654
|
if (doc?.warning?.includes('No matching index found')) {
|
|
598
|
-
|
|
599
|
-
const index_name = `index_${e.table_id}_${new Date().valueOf().toString()}`;
|
|
600
|
-
var index = [];
|
|
601
|
-
|
|
602
|
-
for (const [key, val] of Object.entries(opt.selector)) {
|
|
603
|
-
index.push(key);
|
|
604
|
-
}
|
|
605
|
-
mango_index_obj = {
|
|
606
|
-
index: {
|
|
607
|
-
fields: index,
|
|
608
|
-
},
|
|
609
|
-
name: index_name,
|
|
610
|
-
ddoc: `mango_index_table_${e.table_id}`,
|
|
611
|
-
};
|
|
612
|
-
const result = await db.createIndex(mango_index_obj);
|
|
613
|
-
// console.log("createIndex", result);
|
|
614
|
-
throw new Error('creating index in progress');
|
|
615
|
-
} catch (err) {
|
|
616
|
-
return { code: -1, data: err.message };
|
|
617
|
-
}
|
|
655
|
+
await create_mango_index_for_selector();
|
|
618
656
|
}
|
|
619
657
|
return await done(doc);
|
|
620
658
|
} catch (err) {
|
|
621
|
-
if (err
|
|
659
|
+
if (err?.message?.includes('Cannot sort on field')) {
|
|
622
660
|
const index_name = `index_${e.table_id}_${new Date().valueOf().toString()}`;
|
|
623
661
|
|
|
624
662
|
// const _selector = _.cloneDeep(opt.selector);
|
|
625
663
|
let _sort = _.cloneDeep(opt.sort);
|
|
626
664
|
|
|
627
|
-
mango_index_obj = {
|
|
665
|
+
const mango_index_obj = {
|
|
628
666
|
index: {
|
|
629
667
|
fields: [],
|
|
630
668
|
},
|
|
@@ -655,32 +693,17 @@ const query_db = async function (e, db, table_obj) {
|
|
|
655
693
|
|
|
656
694
|
try {
|
|
657
695
|
const result = await db.createIndex(mango_index_obj);
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
// db.on("indexing", function (event) {
|
|
661
|
-
// // called when indexes are updated
|
|
662
|
-
// console.log("indexes event", event);
|
|
663
|
-
// // resolve();
|
|
664
|
-
// });
|
|
665
|
-
// setTimeout(() => {
|
|
666
|
-
// resolve();
|
|
667
|
-
// }, 5000);
|
|
668
|
-
// });
|
|
669
|
-
// };
|
|
670
|
-
// await monitor_indexing();
|
|
671
|
-
// throw new Error({ code: -88, data: "creating index" });
|
|
672
|
-
throw new Error('creating index in progress');
|
|
673
|
-
// const doc = await db.find(opt);
|
|
674
|
-
// return await done(doc);
|
|
696
|
+
const doc = await db.find(opt);
|
|
697
|
+
return await done(doc);
|
|
675
698
|
} catch (err) {
|
|
676
699
|
return { code: -1, data: err.message };
|
|
677
700
|
}
|
|
678
701
|
} else {
|
|
679
|
-
return { code: -1, data: err
|
|
702
|
+
return { code: -1, data: err?.message || err };
|
|
680
703
|
}
|
|
681
704
|
}
|
|
682
705
|
} catch (err) {
|
|
683
|
-
return { code: -1, data: err
|
|
706
|
+
return { code: -1, data: err?.message || err };
|
|
684
707
|
}
|
|
685
708
|
};
|
|
686
709
|
|