spice-couchbase 2.0.44 → 2.0.46

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/build/index.js CHANGED
@@ -36,8 +36,10 @@ if (!Promise.allSettled) {
36
36
  status: "rejected",
37
37
  reason
38
38
  }))));
39
- }
39
+ } // Couchbase lookupIn has a limit of 16 operations per call
40
40
 
41
+
42
+ var LOOKUP_IN_MAX_SPECS = 16;
41
43
  module.exports = class Database {
42
44
  constructor(connection) {
43
45
  var {
@@ -305,24 +307,40 @@ module.exports = class Database {
305
307
  return _asyncToGenerator(function* () {
306
308
  try {
307
309
  var useLookupIn = columns.length > 0;
308
- var response = yield useLookupIn ? (_this8.use_bucket_name ? _this8.connection : _this8.collection).lookupIn(doc_id, columns.map(col => couchbase.LookupInSpec.get(col))) : (_this8.use_bucket_name ? _this8.connection : _this8.collection).get(doc_id);
310
+ var collection = _this8.use_bucket_name ? _this8.connection : _this8.collection;
309
311
 
310
- if (useLookupIn) {
311
- // lookupIn returns content array with each spec result
312
- var value = {
313
- id: doc_id
314
- };
315
- columns.forEach((col, colIndex) => {
316
- if (response.content && response.content[colIndex]) {
317
- value[col] = response.content[colIndex].value;
318
- }
319
- });
320
- return value;
321
- } else {
312
+ if (!useLookupIn) {
313
+ var response = yield collection.get(doc_id);
322
314
  return _objectSpread({
323
315
  id: doc_id
324
316
  }, response.content);
325
- }
317
+ } // Batch columns into chunks of LOOKUP_IN_MAX_SPECS (16)
318
+
319
+
320
+ var value = {
321
+ id: doc_id
322
+ };
323
+
324
+ var columnBatches = _.chunk(columns, LOOKUP_IN_MAX_SPECS); // Execute all batch requests in parallel
325
+
326
+
327
+ var batchPromises = columnBatches.map(batch => {
328
+ var specs = batch.map(col => couchbase.LookupInSpec.get(col));
329
+ return collection.lookupIn(doc_id, specs);
330
+ });
331
+ var batchResults = yield Promise.allSettled(batchPromises); // Merge results from all batches
332
+
333
+ batchResults.forEach((result, batchIndex) => {
334
+ if (result.status === "fulfilled" && result.value) {
335
+ var batch = columnBatches[batchIndex];
336
+ batch.forEach((col, colIndex) => {
337
+ if (result.value.content && result.value.content[colIndex]) {
338
+ value[col] = result.value.content[colIndex].value;
339
+ }
340
+ });
341
+ }
342
+ });
343
+ return value;
326
344
  } catch (e) {
327
345
  console.error(e);
328
346
  }
@@ -400,42 +418,89 @@ module.exports = class Database {
400
418
 
401
419
  if (_.isArray(keys) && keys.length > 0) {
402
420
  var useLookupIn = columns.length > 0;
421
+ var collection = _this14.use_bucket_name ? _this14.connection : _this14.collection;
403
422
 
404
- var functions = _.map(keys, key => useLookupIn ? (_this14.use_bucket_name ? _this14.connection : _this14.collection).lookupIn(key, columns.map(col => couchbase.LookupInSpec.get(col))) : (_this14.use_bucket_name ? _this14.connection : _this14.collection).get(key));
423
+ if (!useLookupIn) {
424
+ // Regular get - no batching needed
425
+ var functions = _.map(keys, key => collection.get(key));
405
426
 
406
- var returned_all = yield Promise.allSettled(functions);
427
+ var returned_all = yield Promise.allSettled(functions);
407
428
 
408
- var responses = _.flatten(_.map(returned_all, returned_obj => {
409
- return returned_obj === null || returned_obj === void 0 ? void 0 : returned_obj.value;
410
- }));
429
+ var responses = _.flatten(_.map(returned_all, returned_obj => returned_obj === null || returned_obj === void 0 ? void 0 : returned_obj.value));
411
430
 
412
- var final_responses = _.compact(_.map(responses, (res, index) => {
413
- if (res) {
414
- var value;
431
+ var _final_responses = _.compact(_.map(responses, (res, index) => {
432
+ if (res) {
433
+ var value = res.value;
415
434
 
416
- if (useLookupIn) {
417
- // lookupIn returns content array with each spec result
418
- value = {};
419
- columns.forEach((col, colIndex) => {
420
- if (res.content && res.content[colIndex]) {
421
- value[col] = res.content[colIndex].value;
435
+ if (!keep_type && value) {
436
+ delete value.type;
437
+ }
438
+
439
+ if (value) {
440
+ value.id = keys[index];
441
+ }
442
+
443
+ return value;
444
+ }
445
+ }));
446
+
447
+ return _final_responses;
448
+ } // lookupIn with batching for columns > 16
449
+
450
+
451
+ var columnBatches = _.chunk(columns, LOOKUP_IN_MAX_SPECS); // For each key, we need to fetch all column batches and merge the results
452
+
453
+
454
+ var keyPromises = _.map(keys,
455
+ /*#__PURE__*/
456
+ function () {
457
+ var _ref = _asyncToGenerator(function* (key) {
458
+ try {
459
+ var value = {
460
+ id: key
461
+ }; // Execute all batch requests for this key in parallel
462
+
463
+ var batchPromises = columnBatches.map(batch => {
464
+ var specs = batch.map(col => couchbase.LookupInSpec.get(col));
465
+ return collection.lookupIn(key, specs);
466
+ });
467
+ var batchResults = yield Promise.allSettled(batchPromises); // Merge results from all batches
468
+
469
+ batchResults.forEach((result, batchIndex) => {
470
+ if (result.status === "fulfilled" && result.value) {
471
+ var batch = columnBatches[batchIndex];
472
+ batch.forEach((col, colIndex) => {
473
+ if (result.value.content && result.value.content[colIndex]) {
474
+ value[col] = result.value.content[colIndex].value;
475
+ }
476
+ });
422
477
  }
423
478
  });
424
- } else {
425
- // regular get returns the full document in value
426
- value = res.value;
427
- }
428
479
 
429
- if (!keep_type && value) {
430
- delete value.type;
431
- }
480
+ if (!keep_type && value) {
481
+ delete value.type;
482
+ }
432
483
 
433
- if (value) {
434
- value.id = keys[index];
484
+ return value;
485
+ } catch (e) {
486
+ return null;
435
487
  }
488
+ });
489
+
490
+ return function (_x) {
491
+ return _ref.apply(this, arguments);
492
+ };
493
+ }());
494
+
495
+ var results = yield Promise.allSettled(keyPromises);
436
496
 
437
- return value;
497
+ var final_responses = _.compact(_.map(results, result => {
498
+ if (result.status === "fulfilled" && result.value) {
499
+ // Only return if we got at least some data beyond just the id
500
+ return Object.keys(result.value).length > 1 ? result.value : null;
438
501
  }
502
+
503
+ return null;
439
504
  }));
440
505
 
441
506
  return final_responses;
@@ -450,7 +515,7 @@ module.exports = class Database {
450
515
  return new Promise(
451
516
  /*#__PURE__*/
452
517
  function () {
453
- var _ref = _asyncToGenerator(function* (resolve, reject) {
518
+ var _ref2 = _asyncToGenerator(function* (resolve, reject) {
454
519
  var ViewQuery = couchbase.ViewQuery;
455
520
  var query = ViewQuery.from(design_doc, view);
456
521
 
@@ -477,7 +542,7 @@ module.exports = class Database {
477
542
  that.connection.query(query,
478
543
  /*#__PURE__*/
479
544
  function () {
480
- var _ref2 = _asyncToGenerator(function* (err, results, matrix) {
545
+ var _ref3 = _asyncToGenerator(function* (err, results, matrix) {
481
546
  var return_value = [];
482
547
 
483
548
  if (err) {
@@ -500,14 +565,14 @@ module.exports = class Database {
500
565
  }
501
566
  });
502
567
 
503
- return function (_x3, _x4, _x5) {
504
- return _ref2.apply(this, arguments);
568
+ return function (_x4, _x5, _x6) {
569
+ return _ref3.apply(this, arguments);
505
570
  };
506
571
  }());
507
572
  });
508
573
 
509
- return function (_x, _x2) {
510
- return _ref.apply(this, arguments);
574
+ return function (_x2, _x3) {
575
+ return _ref2.apply(this, arguments);
511
576
  };
512
577
  }());
513
578
  }
@@ -611,7 +676,7 @@ module.exports = class Database {
611
676
  return new Promise(
612
677
  /*#__PURE__*/
613
678
  function () {
614
- var _ref3 = _asyncToGenerator(function* (resolve, reject) {
679
+ var _ref4 = _asyncToGenerator(function* (resolve, reject) {
615
680
  try {
616
681
  var full_query = search_query + " AND type:" + type;
617
682
  var query = that.SearchQuery.new(that.full_text_index || full_text_index, that.SearchQuery.queryString(full_query));
@@ -632,7 +697,7 @@ module.exports = class Database {
632
697
  that.cluster.query(query,
633
698
  /*#__PURE__*/
634
699
  function () {
635
- var _ref4 = _asyncToGenerator(function* (err, results, matrix) {
700
+ var _ref5 = _asyncToGenerator(function* (err, results, matrix) {
636
701
  var return_value = [];
637
702
 
638
703
  if (err) {
@@ -658,8 +723,8 @@ module.exports = class Database {
658
723
  }
659
724
  });
660
725
 
661
- return function (_x8, _x9, _x10) {
662
- return _ref4.apply(this, arguments);
726
+ return function (_x9, _x10, _x11) {
727
+ return _ref5.apply(this, arguments);
663
728
  };
664
729
  }());
665
730
  } catch (e) {
@@ -667,8 +732,8 @@ module.exports = class Database {
667
732
  }
668
733
  });
669
734
 
670
- return function (_x6, _x7) {
671
- return _ref3.apply(this, arguments);
735
+ return function (_x7, _x8) {
736
+ return _ref4.apply(this, arguments);
672
737
  };
673
738
  }());
674
739
  }
@@ -692,40 +757,96 @@ module.exports = class Database {
692
757
  return response.rows;
693
758
  })();
694
759
  }
760
+ /**
761
+ * Recursively checks if the query plan contains a Fetch operator
762
+ * @param {Object} plan - The EXPLAIN query plan
763
+ * @returns {boolean} - True if Fetch operator is found
764
+ */
765
+
766
+
767
+ hasFetchOperator(plan) {
768
+ if (!plan || typeof plan !== "object") {
769
+ return false;
770
+ } // Check if this node is a Fetch operator
771
+
772
+
773
+ if (plan["#operator"] === "Fetch") {
774
+ return true;
775
+ } // Recursively check all child properties
776
+
777
+
778
+ for (var key of Object.keys(plan)) {
779
+ var value = plan[key];
780
+
781
+ if (Array.isArray(value)) {
782
+ for (var item of value) {
783
+ if (this.hasFetchOperator(item)) {
784
+ return true;
785
+ }
786
+ }
787
+ } else if (typeof value === "object" && value !== null) {
788
+ if (this.hasFetchOperator(value)) {
789
+ return true;
790
+ }
791
+ }
792
+ }
793
+
794
+ return false;
795
+ }
695
796
 
696
797
  optimize(query, exec_scope) {
798
+ var _this18 = this;
799
+
697
800
  return _asyncToGenerator(function* () {
698
801
  try {
699
- var _$last, _$first, _$first$advice, _$first$advice$advise, _$first$advice$advise2;
802
+ var _explainResponse$rows;
700
803
 
701
- var options = {
702
- adhoc: true
703
- };
704
- var advise_query = "ADVISE ".concat(query);
705
- var response = yield exec_scope.query(advise_query, options);
706
- var best_index = (_$last = _.last(((_$first = _.first((response === null || response === void 0 ? void 0 : response.rows) || [])) === null || _$first === void 0 ? void 0 : (_$first$advice = _$first.advice) === null || _$first$advice === void 0 ? void 0 : (_$first$advice$advise = _$first$advice.adviseinfo) === null || _$first$advice$advise === void 0 ? void 0 : (_$first$advice$advise2 = _$first$advice$advise.recommended_indexes) === null || _$first$advice$advise2 === void 0 ? void 0 : _$first$advice$advise2.indexes) || [])) === null || _$last === void 0 ? void 0 : _$last.index_statement;
804
+ var {
805
+ buildCoveringIndex
806
+ } = require("./utility"); // 1. Run EXPLAIN to get the query plan
807
+
808
+
809
+ var explainQuery = "EXPLAIN ".concat(query);
810
+ var explainResponse = yield exec_scope.query(explainQuery); // 2. Check if query plan contains Fetch operator (not covered)
707
811
 
708
- if (best_index) {
709
- yield exec_scope.query(best_index);
812
+ var plan = explainResponse === null || explainResponse === void 0 ? void 0 : (_explainResponse$rows = explainResponse.rows) === null || _explainResponse$rows === void 0 ? void 0 : _explainResponse$rows[0];
813
+
814
+ if (!_this18.hasFetchOperator(plan)) {
815
+ // Query is already covered, no need to create index
816
+ return;
817
+ } // 3. Build covering index from the query
818
+
819
+
820
+ var indexStatement = buildCoveringIndex(query, {
821
+ bucket: _this18.bucket,
822
+ scope: _this18.scope_name,
823
+ collection: _this18.collection_name
824
+ }); // 4. Execute the index creation if we got a valid statement
825
+
826
+ if (indexStatement) {
827
+ console.log("Executing index creation:", indexStatement);
828
+ var response = yield exec_scope.query(indexStatement);
829
+ console.log("Index creation response:", response);
710
830
  }
711
- } catch (e) {}
831
+ } catch (e) {// Silently handle errors (index may already exist, etc.)
832
+ }
712
833
  })();
713
834
  }
714
835
 
715
836
  query(query) {
716
- var _this18 = this;
837
+ var _this19 = this;
717
838
 
718
839
  var query_scope = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : "cluster";
719
840
  return _asyncToGenerator(function* () {
720
- var scope = query_scope || _this18.query_scope;
841
+ var scope = query_scope || _this19.query_scope;
721
842
  var sanitizedQuery = query; //sanitization step to be implemented
722
843
 
723
844
  var exec_scope = null;
724
845
 
725
846
  if (scope === "cluster") {
726
- exec_scope = _this18.cluster;
847
+ exec_scope = _this19.cluster;
727
848
  } else {
728
- exec_scope = _this18.scope;
849
+ exec_scope = _this19.scope;
729
850
  } // Start time
730
851
 
731
852
 
@@ -738,10 +859,10 @@ module.exports = class Database {
738
859
  var endTime = process.hrtime.bigint(); // Calculate duration in microseconds
739
860
 
740
861
  var durationInMilliseconds = (endTime - startTime) / BigInt(1000000); // optimize if response time is greater than 1 second
862
+ //if (durationInMilliseconds > 2000) {
863
+
864
+ _this19.optimize(query, exec_scope); //}
741
865
 
742
- if (durationInMilliseconds > 2000) {
743
- _this18.optimize(query, exec_scope);
744
- }
745
866
 
746
867
  return response;
747
868
  })();