@xuda.io/xuda-dbs-plugin-xuda 1.0.319 → 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.
Files changed (3) hide show
  1. package/package.json +1 -1
  2. package/server.mjs +41 -3
  3. package/studio.mjs +24 -8
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xuda.io/xuda-dbs-plugin-xuda",
3
- "version": "1.0.319",
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
@@ -558,9 +558,23 @@ const query_db = async function (e, db, app_id_reference, table_obj) {
558
558
  }
559
559
  };
560
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
+
561
575
  try {
562
576
  try {
563
- const doc = await db.find(opt);
577
+ const doc = await db.find(find_opt);
564
578
  if (doc?.warning?.includes('No matching index found')) {
565
579
  await create_mango_index_for_selector();
566
580
  }
@@ -591,7 +605,7 @@ const query_db = async function (e, db, app_id_reference, table_obj) {
591
605
  try {
592
606
  const result = await db.createIndex(mango_index_obj);
593
607
 
594
- const doc = await db.find(opt);
608
+ const doc = await db.find(find_opt);
595
609
  return await done(doc);
596
610
  } catch (err) {
597
611
  return { code: -1, data: err.message };
@@ -719,7 +733,31 @@ const query_db = async function (e, db, app_id_reference, table_obj) {
719
733
  return await runtime_get_query_data();
720
734
  }
721
735
 
722
- if (e.count && !e.filter_from) {
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) {
723
761
  return await count_tables();
724
762
  }
725
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 (typeof _sortModel === 'string') {
249
- _sortModel = JSON.parse(e.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 || !JSON.parse(e.sortModel).length) {
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
- if (e.dataSourceFilterModelType === 'query' && e.filterModelMongo) {
376
- selector_new['$and'] = [replaceRegexOptions(replaceKeysInQuery(JSON.parse(e.filterModelMongo)))];
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
- if (e.filterModelUserMongo) {
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(JSON.parse(e.filterModelUserMongo))));
398
+ selector_new['$and'].push(replaceRegexOptions(replaceKeysInQuery(filterModelUserMongo)));
383
399
  }
384
400
 
385
401
  if (e.dataSourceFilterModelType === 'index') {