spice-couchbase 2.0.64 → 2.0.65
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.auto-optimize.test.js +122 -204
- package/build/index.count-query.test.js +27 -63
- package/build/index.index-management.test.js +283 -0
- package/build/index.js +486 -418
- package/build/index.multi_get.test.js +9 -56
- package/build/index.query-consistency.test.js +28 -72
- package/build/utility.js +182 -152
- package/build/utility.test.js +79 -59
- package/package.json +1 -1
- package/src/index.index-management.test.js +232 -0
- package/src/index.js +270 -8
- package/src/utility.js +78 -0
package/build/index.js
CHANGED
|
@@ -1,39 +1,30 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
3
|
var _process$env$SPICE_CO;
|
|
4
|
-
|
|
5
|
-
function
|
|
6
|
-
|
|
7
|
-
function
|
|
8
|
-
|
|
9
|
-
function
|
|
10
|
-
|
|
11
|
-
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } }
|
|
12
|
-
|
|
13
|
-
function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }
|
|
14
|
-
|
|
4
|
+
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
5
|
+
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
6
|
+
function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
|
|
7
|
+
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
|
|
8
|
+
function _toPrimitive(t, e) { if ("object" != typeof t || !t) return t; var r; if ("undefined" != typeof Symbol && void 0 !== (r = t[Symbol.toPrimitive])) { var i = r.call(t, e || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === e ? String : Number)(t); }
|
|
9
|
+
function asyncGeneratorStep(n, t, e, r, o, a, c) { try { var i = n[a](c), u = i.value; } catch (n) { return void e(n); } i.done ? t(u) : Promise.resolve(u).then(r, o); }
|
|
10
|
+
function _asyncToGenerator(n) { return function () { var t = this, e = arguments; return new Promise(function (r, o) { var a = n.apply(t, e); function _next(n) { asyncGeneratorStep(a, r, o, _next, _throw, "next", n); } function _throw(n) { asyncGeneratorStep(a, r, o, _next, _throw, "throw", n); } _next(void 0); }); }; }
|
|
15
11
|
var _ = require("lodash");
|
|
16
|
-
|
|
17
12
|
var couchbase = require("couchbase");
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
createHealthMonitor,
|
|
34
|
-
openHandles
|
|
35
|
-
} = require("./connectionHealth");
|
|
36
|
-
|
|
13
|
+
var _require = require("couchbase"),
|
|
14
|
+
ConflictResolutionType = _require.ConflictResolutionType;
|
|
15
|
+
var _require2 = require("couchbase"),
|
|
16
|
+
QueryScanConsistency = _require2.QueryScanConsistency;
|
|
17
|
+
var _require3 = require("./utility"),
|
|
18
|
+
parseResourceList = _require3.parseResourceList,
|
|
19
|
+
extractStatementResources = _require3.extractStatementResources,
|
|
20
|
+
statementShape = _require3.statementShape,
|
|
21
|
+
usesManagedIndex = _require3.usesManagedIndex,
|
|
22
|
+
renderIndexKey = _require3.renderIndexKey,
|
|
23
|
+
BASE_INDEX = _require3.BASE_INDEX,
|
|
24
|
+
MANAGED_INDEX_NAME = _require3.MANAGED_INDEX_NAME;
|
|
25
|
+
var _require4 = require("./connectionHealth"),
|
|
26
|
+
createHealthMonitor = _require4.createHealthMonitor,
|
|
27
|
+
openHandles = _require4.openHandles;
|
|
37
28
|
if (!Promise.allSettled) {
|
|
38
29
|
Promise.allSettled = promises => Promise.all(promises.map((promise, i) => promise.then(value => ({
|
|
39
30
|
status: "fulfilled",
|
|
@@ -42,27 +33,29 @@ if (!Promise.allSettled) {
|
|
|
42
33
|
status: "rejected",
|
|
43
34
|
reason
|
|
44
35
|
}))));
|
|
45
|
-
}
|
|
36
|
+
}
|
|
46
37
|
|
|
38
|
+
// Couchbase lookupIn has a limit of 16 operations per call
|
|
39
|
+
var LOOKUP_IN_MAX_SPECS = 16;
|
|
47
40
|
|
|
48
|
-
|
|
41
|
+
// Auto-optimisation guard rails. `query()` fires `optimize()` (an EXPLAIN,
|
|
49
42
|
// index inventory check, and possible CREATE INDEX) above SLOW_QUERY_MS. When
|
|
50
43
|
// the cluster is degraded every statement is slow, so without limits each
|
|
51
44
|
// query spawned two more heavy ones and deepened the outage. These caps make it
|
|
52
45
|
// a bounded, once-per-statement background task that backs off on failure.
|
|
53
|
-
|
|
54
46
|
var SLOW_QUERY_MS = Number(process.env.SPICE_COUCHBASE_SLOW_QUERY_MS || 3000);
|
|
55
47
|
var AUTO_OPTIMIZE_ENABLED = !["0", "false", "no", "off"].includes(String((_process$env$SPICE_CO = process.env.SPICE_COUCHBASE_AUTO_OPTIMIZE) !== null && _process$env$SPICE_CO !== void 0 ? _process$env$SPICE_CO : "true").trim().toLowerCase());
|
|
56
48
|
var OPTIMIZE_MAX_INFLIGHT = Number(process.env.SPICE_COUCHBASE_OPTIMIZE_MAX_INFLIGHT || 1);
|
|
57
|
-
var OPTIMIZE_FAILURE_COOLDOWN_MS = Number(process.env.SPICE_COUCHBASE_OPTIMIZE_COOLDOWN_MS || 5 * 60 * 1000);
|
|
49
|
+
var OPTIMIZE_FAILURE_COOLDOWN_MS = Number(process.env.SPICE_COUCHBASE_OPTIMIZE_COOLDOWN_MS || 5 * 60 * 1000);
|
|
50
|
+
// Statements already optimised in this process. Bounded so it cannot grow
|
|
58
51
|
// without limit on a workload with many distinct literals.
|
|
59
|
-
|
|
60
52
|
var OPTIMIZE_SEEN_LIMIT = 1000;
|
|
61
53
|
var optimizeState = {
|
|
62
54
|
inFlight: 0,
|
|
63
55
|
seen: new Set(),
|
|
64
56
|
cooldownUntil: 0
|
|
65
|
-
};
|
|
57
|
+
};
|
|
58
|
+
// Auto cover indexing is opt-in per resource. A resource is the collection a
|
|
66
59
|
// Database instance serves (scoped deployments) or the `type = '...'` literal
|
|
67
60
|
// legacy bucket mode adds to every statement; raw statements also count their
|
|
68
61
|
// FROM / JOIN keyspaces. Names are case-insensitive. A slow statement is
|
|
@@ -77,118 +70,118 @@ var optimizeState = {
|
|
|
77
70
|
// SPICE_COUCHBASE_OPTIMIZE_RESOURCES (`*` = every resource, which is the
|
|
78
71
|
// pre-2.0.62 behaviour), or connection config / constructor option
|
|
79
72
|
// `auto_optimize_resources`.
|
|
80
|
-
|
|
81
73
|
var OPTIMIZE_ENABLED_RESOURCES = parseResourceList(process.env.SPICE_COUCHBASE_OPTIMIZE_RESOURCES);
|
|
82
|
-
var autoOptimizePolicy = null;
|
|
74
|
+
var autoOptimizePolicy = null;
|
|
75
|
+
// When the application installs a handler, slow statements are handed to it
|
|
76
|
+
// instead of creating indexes here: it owns every index decision, with its
|
|
77
|
+
// own key rules, caps and build windows. Without one, the legacy covering
|
|
78
|
+
// index path below still runs, so other applications keep their behaviour.
|
|
79
|
+
var slowStatementHandler = null;
|
|
80
|
+
|
|
81
|
+
// One health monitor per process, shared by every copy of this module that
|
|
83
82
|
// ends up loaded. See connectionHealth.js.
|
|
84
|
-
|
|
85
83
|
var HEALTH_MONITOR_KEY = Symbol.for("spice-couchbase.health-monitor");
|
|
86
|
-
var health = globalThis[HEALTH_MONITOR_KEY] || (globalThis[HEALTH_MONITOR_KEY] = createHealthMonitor());
|
|
87
|
-
// replaces. Instances read them at call time instead of copying them.
|
|
84
|
+
var health = globalThis[HEALTH_MONITOR_KEY] || (globalThis[HEALTH_MONITOR_KEY] = createHealthMonitor());
|
|
88
85
|
|
|
86
|
+
// Cluster handles live on the `spice.database` globals, which a reconnect
|
|
87
|
+
// replaces. Instances read them at call time instead of copying them.
|
|
89
88
|
var HANDLE_GLOBALS = {
|
|
90
89
|
cluster: "clusters",
|
|
91
90
|
connection: "connections",
|
|
92
91
|
scope: "scopes",
|
|
93
92
|
bucket_handle: "buckets"
|
|
94
93
|
};
|
|
95
|
-
|
|
96
94
|
function globalHandle(kind, connection_name) {
|
|
97
|
-
var _spice
|
|
98
|
-
|
|
95
|
+
var _spice;
|
|
99
96
|
if (typeof spice === "undefined") return undefined;
|
|
100
|
-
return (_spice = spice) === null || _spice === void 0
|
|
97
|
+
return (_spice = spice) === null || _spice === void 0 || (_spice = _spice.database) === null || _spice === void 0 || (_spice = _spice[HANDLE_GLOBALS[kind]]) === null || _spice === void 0 ? void 0 : _spice[connection_name];
|
|
101
98
|
}
|
|
102
|
-
|
|
103
99
|
function requestedAutoOptimize(query_options) {
|
|
104
100
|
var _query_options$auto_o;
|
|
105
|
-
|
|
106
101
|
if (!query_options || typeof query_options !== "object" || Array.isArray(query_options)) {
|
|
107
102
|
return undefined;
|
|
108
103
|
}
|
|
109
|
-
|
|
110
104
|
var value = (_query_options$auto_o = query_options.auto_optimize) !== null && _query_options$auto_o !== void 0 ? _query_options$auto_o : query_options.optimize;
|
|
111
105
|
return typeof value === "boolean" ? value : undefined;
|
|
112
106
|
}
|
|
113
|
-
|
|
114
107
|
function shouldOptimize(query) {
|
|
115
108
|
if (!AUTO_OPTIMIZE_ENABLED) return false;
|
|
116
109
|
if (optimizeState.inFlight >= OPTIMIZE_MAX_INFLIGHT) return false;
|
|
117
110
|
if (Date.now() < optimizeState.cooldownUntil) return false;
|
|
118
111
|
if (optimizeState.seen.has(query)) return false;
|
|
119
|
-
|
|
120
112
|
if (optimizeState.seen.size >= OPTIMIZE_SEEN_LIMIT) {
|
|
121
113
|
optimizeState.seen.delete(optimizeState.seen.values().next().value);
|
|
122
114
|
}
|
|
123
|
-
|
|
124
115
|
optimizeState.seen.add(query);
|
|
125
116
|
return true;
|
|
126
117
|
}
|
|
127
|
-
|
|
128
118
|
function normalizeScanConsistency(value) {
|
|
129
119
|
if (value === true || value === QueryScanConsistency.RequestPlus || typeof value === "string" && ["true", "request_plus"].includes(value.toLowerCase())) {
|
|
130
120
|
return QueryScanConsistency.RequestPlus;
|
|
131
121
|
}
|
|
132
|
-
|
|
133
122
|
if (value === undefined || value === null || value === "" || value === false || value === QueryScanConsistency.NotBounded || typeof value === "string" && ["false", "not_bounded"].includes(value.toLowerCase())) {
|
|
134
123
|
return QueryScanConsistency.NotBounded;
|
|
135
124
|
}
|
|
136
|
-
|
|
137
125
|
throw new Error("Unsupported query scan consistency: ".concat(value));
|
|
138
126
|
}
|
|
139
|
-
|
|
140
127
|
function buildQueryOptions() {
|
|
141
|
-
var _queryOptions$consist, _queryOptions$scanWai;
|
|
142
|
-
|
|
128
|
+
var _queryOptions$consist, _queryOptions$scanWai, _queryOptions$clientC;
|
|
143
129
|
var queryOptions = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
144
|
-
|
|
145
130
|
if (typeof queryOptions !== "object" || Array.isArray(queryOptions)) {
|
|
146
131
|
queryOptions = {
|
|
147
132
|
scan_consistency: queryOptions
|
|
148
133
|
};
|
|
149
134
|
}
|
|
150
|
-
|
|
151
135
|
var options = {};
|
|
152
136
|
var consistentWith = (_queryOptions$consist = queryOptions.consistentWith) !== null && _queryOptions$consist !== void 0 ? _queryOptions$consist : queryOptions.consistent_with;
|
|
153
|
-
|
|
154
137
|
if (consistentWith !== undefined && consistentWith !== null) {
|
|
155
138
|
options.consistentWith = consistentWith;
|
|
156
139
|
} else {
|
|
157
140
|
var _queryOptions$scanCon;
|
|
158
|
-
|
|
159
141
|
options.scanConsistency = normalizeScanConsistency((_queryOptions$scanCon = queryOptions.scanConsistency) !== null && _queryOptions$scanCon !== void 0 ? _queryOptions$scanCon : queryOptions.scan_consistency);
|
|
160
142
|
}
|
|
161
|
-
|
|
162
143
|
var scanWait = (_queryOptions$scanWai = queryOptions.scanWait) !== null && _queryOptions$scanWai !== void 0 ? _queryOptions$scanWai : queryOptions.scan_wait;
|
|
163
|
-
|
|
164
144
|
if (scanWait !== undefined && scanWait !== null && scanWait !== "") {
|
|
165
145
|
var numericScanWait = Number(scanWait);
|
|
166
|
-
|
|
167
146
|
if (!Number.isFinite(numericScanWait) || numericScanWait < 0) {
|
|
168
147
|
throw new Error("Invalid query scan wait: ".concat(scanWait));
|
|
169
148
|
}
|
|
170
|
-
|
|
171
149
|
options.scanWait = numericScanWait;
|
|
172
150
|
}
|
|
173
151
|
|
|
152
|
+
// Reported as clientContextID in system:completed_requests, which is how a
|
|
153
|
+
// slow statement is traced back to the route or job that ran it.
|
|
154
|
+
var clientContextId = (_queryOptions$clientC = queryOptions.clientContextId) !== null && _queryOptions$clientC !== void 0 ? _queryOptions$clientC : queryOptions.client_context_id;
|
|
155
|
+
if (clientContextId !== undefined && clientContextId !== null && clientContextId !== "") {
|
|
156
|
+
options.clientContextId = String(clientContextId).slice(0, 64);
|
|
157
|
+
}
|
|
174
158
|
return options;
|
|
175
159
|
}
|
|
160
|
+
function wantsScanWaitFallback(query_options) {
|
|
161
|
+
return query_options && typeof query_options === "object" && (query_options.scan_wait_fallback === true || query_options.scanWaitFallback === true);
|
|
162
|
+
}
|
|
176
163
|
|
|
164
|
+
// The query service reports an expired scan_wait as an index scan timeout
|
|
165
|
+
// (12015), which is a query error, not a connectivity timeout.
|
|
166
|
+
function isScanWaitExpired(error) {
|
|
167
|
+
var _error$cause$first_er, _error$cause, _error$context, _error$cause2, _error$context2;
|
|
168
|
+
var code = (_error$cause$first_er = error === null || error === void 0 || (_error$cause = error.cause) === null || _error$cause === void 0 ? void 0 : _error$cause.first_error_code) !== null && _error$cause$first_er !== void 0 ? _error$cause$first_er : error === null || error === void 0 || (_error$context = error.context) === null || _error$context === void 0 ? void 0 : _error$context.first_error_code;
|
|
169
|
+
var message = (error === null || error === void 0 || (_error$cause2 = error.cause) === null || _error$cause2 === void 0 ? void 0 : _error$cause2.first_error_message) || (error === null || error === void 0 || (_error$context2 = error.context) === null || _error$context2 === void 0 ? void 0 : _error$context2.first_error_message) || (error === null || error === void 0 ? void 0 : error.message) || "";
|
|
170
|
+
return code === 12015 || /index scan timed out|scan.{0,20}timed? ?out/i.test(message);
|
|
171
|
+
}
|
|
177
172
|
module.exports = class Database {
|
|
178
173
|
constructor(connection) {
|
|
179
|
-
var {
|
|
180
|
-
full_text_index,
|
|
181
|
-
collection,
|
|
182
|
-
scope,
|
|
183
|
-
query_scope =
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
174
|
+
var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
|
|
175
|
+
full_text_index = _ref.full_text_index,
|
|
176
|
+
collection = _ref.collection,
|
|
177
|
+
scope = _ref.scope,
|
|
178
|
+
_ref$query_scope = _ref.query_scope,
|
|
179
|
+
query_scope = _ref$query_scope === void 0 ? "cluster" : _ref$query_scope,
|
|
180
|
+
auto_create_collection = _ref.auto_create_collection,
|
|
181
|
+
auto_optimize = _ref.auto_optimize,
|
|
182
|
+
auto_optimize_resources = _ref.auto_optimize_resources;
|
|
189
183
|
if (connection) {
|
|
190
|
-
var _spice2
|
|
191
|
-
|
|
184
|
+
var _spice2;
|
|
192
185
|
this.connection_name = connection;
|
|
193
186
|
this.query_scope = query_scope;
|
|
194
187
|
this.bucket = this.connection._scope._bucket._name;
|
|
@@ -201,32 +194,29 @@ module.exports = class Database {
|
|
|
201
194
|
spice.config.database.connections[connection].collection,
|
|
202
195
|
this.collection_name
|
|
203
196
|
);*/
|
|
204
|
-
|
|
205
197
|
this.scope_name = scope || spice.config.database.connections[connection].scope || "_default";
|
|
206
198
|
this.full_text_index = spice.config.database.connections[connection].full_text_index || full_text_index;
|
|
207
199
|
this.N1qlQuery = couchbase.N1qlQuery;
|
|
208
200
|
this.SearchQuery = couchbase.SearchQuery;
|
|
209
|
-
|
|
210
|
-
if (!((_spice2 = spice) === null || _spice2 === void 0 ? void 0 : (_spice2$db = _spice2.db) === null || _spice2$db === void 0 ? void 0 : _spice2$db.collections)) {
|
|
201
|
+
if (!((_spice2 = spice) !== null && _spice2 !== void 0 && (_spice2 = _spice2.db) !== null && _spice2 !== void 0 && _spice2.collections)) {
|
|
211
202
|
spice.db = spice.db || {};
|
|
212
203
|
spice.db.collections = new Set();
|
|
213
204
|
}
|
|
214
|
-
|
|
215
205
|
if (this.auto_create_collection) {
|
|
216
206
|
this.upsertCollection();
|
|
217
207
|
}
|
|
208
|
+
this.use_bucket_name = spice.config.database.connections[this.connection_name].scope == undefined;
|
|
218
209
|
|
|
219
|
-
|
|
220
|
-
|
|
210
|
+
// Auto cover indexing. See OPTIMIZE_ENABLED_RESOURCES for precedence.
|
|
221
211
|
var configured_auto_optimize = auto_optimize !== null && auto_optimize !== void 0 ? auto_optimize : this.db_config.auto_optimize;
|
|
222
212
|
this.auto_optimize = typeof configured_auto_optimize === "boolean" ? configured_auto_optimize : undefined;
|
|
223
213
|
this.enabled_resources = parseResourceList([...parseResourceList(this.db_config.auto_optimize_resources), ...parseResourceList(auto_optimize_resources)]);
|
|
224
214
|
}
|
|
225
|
-
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Handles resolve from the globals on every access, so an instance created
|
|
226
218
|
// before a reconnect uses the new cluster afterwards. Assigning one pins it
|
|
227
219
|
// on that instance instead (tests, custom wiring).
|
|
228
|
-
|
|
229
|
-
|
|
230
220
|
pinHandle(kind, value) {
|
|
231
221
|
if (!Object.prototype.hasOwnProperty.call(this, "_pinned_handles")) {
|
|
232
222
|
Object.defineProperty(this, "_pinned_handles", {
|
|
@@ -235,55 +225,44 @@ module.exports = class Database {
|
|
|
235
225
|
writable: true
|
|
236
226
|
});
|
|
237
227
|
}
|
|
238
|
-
|
|
239
228
|
this._pinned_handles[kind] = value;
|
|
240
229
|
}
|
|
241
|
-
|
|
242
230
|
pinnedHandle(kind) {
|
|
243
231
|
var pinned = this._pinned_handles;
|
|
244
232
|
return pinned && kind in pinned ? {
|
|
245
233
|
value: pinned[kind]
|
|
246
234
|
} : null;
|
|
247
235
|
}
|
|
248
|
-
|
|
249
236
|
handle(kind) {
|
|
250
237
|
var pinned = this.pinnedHandle(kind);
|
|
251
238
|
return pinned ? pinned.value : globalHandle(kind, this.connection_name);
|
|
252
239
|
}
|
|
253
|
-
|
|
254
240
|
get cluster() {
|
|
255
241
|
return this.handle("cluster");
|
|
256
242
|
}
|
|
257
|
-
|
|
258
243
|
set cluster(value) {
|
|
259
244
|
this.pinHandle("cluster", value);
|
|
260
245
|
}
|
|
261
|
-
|
|
262
246
|
get connection() {
|
|
263
247
|
return this.handle("connection");
|
|
264
248
|
}
|
|
265
|
-
|
|
266
249
|
set connection(value) {
|
|
267
250
|
this.pinHandle("connection", value);
|
|
268
251
|
}
|
|
269
|
-
|
|
270
252
|
get scope() {
|
|
271
253
|
return this.handle("scope");
|
|
272
254
|
}
|
|
273
|
-
|
|
274
255
|
set scope(value) {
|
|
275
256
|
this.pinHandle("scope", value);
|
|
276
257
|
}
|
|
277
|
-
|
|
278
258
|
get collection() {
|
|
279
259
|
var _this$_collection_cac;
|
|
280
|
-
|
|
281
260
|
var pinned = this.pinnedHandle("collection");
|
|
282
261
|
if (pinned) return pinned.value;
|
|
283
262
|
var scope = this.scope;
|
|
284
|
-
if (!scope) return undefined;
|
|
263
|
+
if (!scope) return undefined;
|
|
264
|
+
// Collection handles are cheap but not free; cache one per scope object,
|
|
285
265
|
// which changes exactly when the connection is replaced.
|
|
286
|
-
|
|
287
266
|
if (((_this$_collection_cac = this._collection_cache) === null || _this$_collection_cac === void 0 ? void 0 : _this$_collection_cac.scope) !== scope) {
|
|
288
267
|
Object.defineProperty(this, "_collection_cache", {
|
|
289
268
|
value: {
|
|
@@ -295,96 +274,80 @@ module.exports = class Database {
|
|
|
295
274
|
configurable: true
|
|
296
275
|
});
|
|
297
276
|
}
|
|
298
|
-
|
|
299
277
|
return this._collection_cache.collection;
|
|
300
278
|
}
|
|
301
|
-
|
|
302
279
|
set collection(value) {
|
|
303
280
|
this.pinHandle("collection", value);
|
|
304
281
|
}
|
|
305
|
-
|
|
306
282
|
get collection_manager() {
|
|
307
283
|
var _this$handle;
|
|
308
|
-
|
|
309
284
|
var pinned = this.pinnedHandle("collection_manager");
|
|
310
285
|
if (pinned) return pinned.value;
|
|
311
286
|
return (_this$handle = this.handle("bucket_handle")) === null || _this$handle === void 0 ? void 0 : _this$handle.collections();
|
|
312
287
|
}
|
|
313
|
-
|
|
314
288
|
set collection_manager(value) {
|
|
315
289
|
this.pinHandle("collection_manager", value);
|
|
316
290
|
}
|
|
317
|
-
|
|
318
291
|
get userManager() {
|
|
319
292
|
var _this$cluster;
|
|
320
|
-
|
|
321
293
|
var pinned = this.pinnedHandle("userManager");
|
|
322
294
|
if (pinned) return pinned.value;
|
|
323
295
|
return (_this$cluster = this.cluster) === null || _this$cluster === void 0 ? void 0 : _this$cluster.users();
|
|
324
296
|
}
|
|
325
|
-
|
|
326
297
|
set userManager(value) {
|
|
327
298
|
this.pinHandle("userManager", value);
|
|
328
299
|
}
|
|
300
|
+
|
|
329
301
|
/**
|
|
330
302
|
* Reports a finished operation to the health monitor. Only connectivity
|
|
331
303
|
* errors (timeouts, cancelled requests, service not available) count.
|
|
332
304
|
*/
|
|
333
|
-
|
|
334
|
-
|
|
335
305
|
recordOutcome(error) {
|
|
336
306
|
health.recordOutcome(this.connection_name, error);
|
|
337
307
|
}
|
|
338
|
-
|
|
339
308
|
recordSettled(results) {
|
|
340
309
|
for (var result of results) {
|
|
341
310
|
if ((result === null || result === void 0 ? void 0 : result.status) === "rejected") this.recordOutcome(result.reason);
|
|
342
311
|
}
|
|
343
312
|
}
|
|
344
|
-
|
|
345
313
|
observe(operation) {
|
|
346
314
|
var _this = this;
|
|
347
|
-
|
|
348
315
|
return _asyncToGenerator(function* () {
|
|
349
316
|
try {
|
|
350
317
|
return yield operation;
|
|
351
318
|
} catch (error) {
|
|
352
319
|
_this.recordOutcome(error);
|
|
353
|
-
|
|
354
320
|
throw error;
|
|
355
321
|
}
|
|
356
322
|
})();
|
|
357
323
|
}
|
|
358
|
-
/** Per-connection health: healthy, reconnecting, generation, last error. */
|
|
359
|
-
|
|
360
324
|
|
|
325
|
+
/** Per-connection health: healthy, reconnecting, generation, last error. */
|
|
361
326
|
static healthStatus() {
|
|
362
327
|
return health.status();
|
|
363
328
|
}
|
|
364
|
-
/** Probes a connection now and reconnects it if the probe fails. */
|
|
365
|
-
|
|
366
329
|
|
|
330
|
+
/** Probes a connection now and reconnects it if the probe fails. */
|
|
367
331
|
static checkHealth() {
|
|
368
332
|
var connection_name = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : "default";
|
|
369
333
|
return health.check(connection_name);
|
|
370
334
|
}
|
|
371
|
-
/** Opens a fresh cluster for a connection and swaps it in. */
|
|
372
|
-
|
|
373
335
|
|
|
336
|
+
/** Opens a fresh cluster for a connection and swaps it in. */
|
|
374
337
|
static forceReconnect() {
|
|
375
338
|
var connection_name = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : "default";
|
|
376
339
|
return health.reconnect(connection_name);
|
|
377
340
|
}
|
|
341
|
+
|
|
378
342
|
/**
|
|
379
343
|
* Stops probing and reconnecting and closes replaced clusters still in
|
|
380
344
|
* their grace period. Call before closing `spice.database.clusters` on
|
|
381
345
|
* shutdown so a reconnect cannot race it.
|
|
382
346
|
*/
|
|
383
|
-
|
|
384
|
-
|
|
385
347
|
static stopHealthMonitor() {
|
|
386
348
|
return health.stop();
|
|
387
349
|
}
|
|
350
|
+
|
|
388
351
|
/**
|
|
389
352
|
* Lets the application decide per resource whether slow statements may
|
|
390
353
|
* create covering indexes, e.g. from a per-resource setting an operator
|
|
@@ -392,53 +355,68 @@ module.exports = class Database {
|
|
|
392
355
|
* returns true or false, or undefined to defer to the static lists. It is
|
|
393
356
|
* synchronous because it runs on the query path. Pass null to clear.
|
|
394
357
|
*/
|
|
395
|
-
|
|
396
|
-
|
|
397
358
|
static setAutoOptimizePolicy(policy) {
|
|
398
359
|
if (policy !== null && policy !== undefined && typeof policy !== "function") {
|
|
399
360
|
throw new TypeError("Auto-optimize policy must be a function or null");
|
|
400
361
|
}
|
|
401
|
-
|
|
402
362
|
autoOptimizePolicy = policy || null;
|
|
403
363
|
}
|
|
404
|
-
|
|
405
364
|
static getAutoOptimizePolicy() {
|
|
406
365
|
return autoOptimizePolicy;
|
|
407
366
|
}
|
|
367
|
+
|
|
408
368
|
/**
|
|
409
|
-
*
|
|
410
|
-
*
|
|
369
|
+
* Hands slow statements to the application instead of creating covering
|
|
370
|
+
* indexes here. `handler({ statement, shape, plan, resources, connection,
|
|
371
|
+
* bucket, scope, collection, duration_ms })` runs in the background, once
|
|
372
|
+
* per statement shape, after the resource opt-in policy has allowed it and
|
|
373
|
+
* only when the plan is not already served by a managed index. Pass null to
|
|
374
|
+
* restore the legacy behaviour.
|
|
411
375
|
*/
|
|
376
|
+
static setSlowStatementHandler(handler) {
|
|
377
|
+
if (handler !== null && handler !== undefined && typeof handler !== "function") {
|
|
378
|
+
throw new TypeError("Slow statement handler must be a function or null");
|
|
379
|
+
}
|
|
380
|
+
slowStatementHandler = handler || null;
|
|
381
|
+
}
|
|
382
|
+
static getSlowStatementHandler() {
|
|
383
|
+
return slowStatementHandler;
|
|
384
|
+
}
|
|
412
385
|
|
|
386
|
+
/** The fallback index every collection gets in place of a primary index. */
|
|
387
|
+
static get BASE_INDEX() {
|
|
388
|
+
return BASE_INDEX;
|
|
389
|
+
}
|
|
390
|
+
static get MANAGED_INDEX_NAME() {
|
|
391
|
+
return MANAGED_INDEX_NAME;
|
|
392
|
+
}
|
|
413
393
|
|
|
394
|
+
/**
|
|
395
|
+
* Resources a statement touches, plus this instance's collection: that is
|
|
396
|
+
* the keyspace `optimize()` would create the index on.
|
|
397
|
+
*/
|
|
414
398
|
statementResources(query) {
|
|
415
399
|
var resources = extractStatementResources(query);
|
|
416
|
-
|
|
417
400
|
if (this.collection_name && this.collection_name !== "_default") {
|
|
418
401
|
resources.add(String(this.collection_name).toLowerCase());
|
|
419
402
|
}
|
|
420
|
-
|
|
421
403
|
return resources;
|
|
422
404
|
}
|
|
405
|
+
|
|
423
406
|
/**
|
|
424
407
|
* True when a slow statement may create a covering index. See
|
|
425
408
|
* OPTIMIZE_ENABLED_RESOURCES for the order of precedence.
|
|
426
409
|
*/
|
|
427
|
-
|
|
428
|
-
|
|
429
410
|
shouldAutoOptimize(query) {
|
|
430
411
|
var query_options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
431
412
|
var requested = requestedAutoOptimize(query_options);
|
|
432
413
|
if (requested !== undefined) return requested;
|
|
433
414
|
if (typeof this.auto_optimize === "boolean") return this.auto_optimize;
|
|
434
415
|
var resources = this.statementResources(query);
|
|
435
|
-
|
|
436
416
|
if (typeof autoOptimizePolicy === "function" && resources.size > 0) {
|
|
437
417
|
var deferred = false;
|
|
438
|
-
|
|
439
418
|
for (var resource of resources) {
|
|
440
419
|
var verdict = void 0;
|
|
441
|
-
|
|
442
420
|
try {
|
|
443
421
|
verdict = autoOptimizePolicy(resource, {
|
|
444
422
|
query,
|
|
@@ -449,54 +427,42 @@ module.exports = class Database {
|
|
|
449
427
|
// A broken policy must not create indexes nobody asked for.
|
|
450
428
|
verdict = false;
|
|
451
429
|
}
|
|
452
|
-
|
|
453
430
|
if (verdict === true) return true;
|
|
454
431
|
if (verdict === undefined || verdict === null) deferred = true;
|
|
455
432
|
}
|
|
456
|
-
|
|
457
433
|
if (!deferred) return false;
|
|
458
434
|
}
|
|
459
|
-
|
|
460
435
|
var enabled = new Set([...OPTIMIZE_ENABLED_RESOURCES, ...(this.enabled_resources || [])]);
|
|
461
436
|
if (enabled.has("*")) return true;
|
|
462
|
-
|
|
463
437
|
for (var _resource of resources) {
|
|
464
438
|
if (enabled.has(_resource)) return true;
|
|
465
439
|
}
|
|
466
|
-
|
|
467
440
|
return false;
|
|
468
441
|
}
|
|
469
|
-
|
|
470
442
|
setGlobalConnectionObject(collection) {
|
|
471
443
|
if (spice.checked_collections == undefined) {
|
|
472
444
|
spice.checked_collections = {};
|
|
473
445
|
}
|
|
474
|
-
|
|
475
446
|
_.set(spice.checked_collections, collection, true);
|
|
476
447
|
}
|
|
477
|
-
|
|
478
448
|
checkGlobelConnectionObject(collection) {
|
|
479
449
|
if (spice.checked_collections == undefined) {
|
|
480
450
|
spice.checked_collections = {};
|
|
481
451
|
}
|
|
482
|
-
|
|
483
452
|
if (_.get(spice.checked_collections, collection)) {
|
|
484
453
|
return true;
|
|
485
454
|
}
|
|
486
|
-
|
|
487
455
|
this.setGlobalConnectionObject(collection);
|
|
488
456
|
return false;
|
|
489
457
|
}
|
|
490
|
-
|
|
491
458
|
connect(connection_name, config) {
|
|
492
459
|
return _asyncToGenerator(function* () {
|
|
493
|
-
var
|
|
494
|
-
cluster,
|
|
495
|
-
bucket,
|
|
496
|
-
scope,
|
|
497
|
-
collection
|
|
498
|
-
|
|
499
|
-
|
|
460
|
+
var _yield$openHandles = yield openHandles(config),
|
|
461
|
+
cluster = _yield$openHandles.cluster,
|
|
462
|
+
bucket = _yield$openHandles.bucket,
|
|
463
|
+
scope = _yield$openHandles.scope,
|
|
464
|
+
collection = _yield$openHandles.collection;
|
|
465
|
+
// Watch the connection from here on and replace it if it wedges.
|
|
500
466
|
health.register(connection_name, config);
|
|
501
467
|
return {
|
|
502
468
|
connection: connection_name,
|
|
@@ -507,14 +473,11 @@ module.exports = class Database {
|
|
|
507
473
|
};
|
|
508
474
|
})();
|
|
509
475
|
}
|
|
510
|
-
|
|
511
476
|
getUserManager(connection) {
|
|
512
477
|
return globalHandle("cluster", connection).users();
|
|
513
478
|
}
|
|
514
|
-
|
|
515
479
|
assignUserPermission() {
|
|
516
480
|
var _this2 = this;
|
|
517
|
-
|
|
518
481
|
return _asyncToGenerator(function* () {
|
|
519
482
|
try {
|
|
520
483
|
var allUsers = yield _this2.userManager.getAllUsers({
|
|
@@ -533,42 +496,47 @@ module.exports = class Database {
|
|
|
533
496
|
console.log(e);
|
|
534
497
|
}
|
|
535
498
|
})();
|
|
536
|
-
}
|
|
537
|
-
|
|
499
|
+
}
|
|
538
500
|
|
|
501
|
+
//create wait function
|
|
539
502
|
wait(ms) {
|
|
540
503
|
return _asyncToGenerator(function* () {
|
|
541
504
|
return new Promise(resolve => setTimeout(resolve, ms));
|
|
542
505
|
})();
|
|
543
506
|
}
|
|
544
|
-
|
|
545
507
|
quoteIdentifier(identifier) {
|
|
546
508
|
return "`".concat(String(identifier).replace(/`/g, "``"), "`");
|
|
547
509
|
}
|
|
548
|
-
|
|
549
510
|
collectionPath(scope_name, collection_name) {
|
|
550
511
|
return "default:".concat(this.quoteIdentifier(this.db_config.bucket), ".").concat(this.quoteIdentifier(scope_name), ".").concat(this.quoteIdentifier(collection_name));
|
|
551
512
|
}
|
|
552
|
-
|
|
553
513
|
isCollectionAlreadyExistsError(error) {
|
|
554
|
-
var _error$
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
return error instanceof couchbase.CollectionExistsError || (error === null || error === void 0 ? void 0 : (_error$cause2 = error.cause) === null || _error$cause2 === void 0 ? void 0 : _error$cause2.first_error_code) === 12003 || (error === null || error === void 0 ? void 0 : (_error$cause3 = error.cause) === null || _error$cause3 === void 0 ? void 0 : _error$cause3.first_error_code) === 12027 || /collection .*already exists|already exists/i.test(message);
|
|
514
|
+
var _error$cause3, _error$context3, _error$context4, _error$cause4, _error$cause5;
|
|
515
|
+
var message = (error === null || error === void 0 ? void 0 : error.message) || (error === null || error === void 0 || (_error$cause3 = error.cause) === null || _error$cause3 === void 0 ? void 0 : _error$cause3.first_error_message) || (error === null || error === void 0 || (_error$context3 = error.context) === null || _error$context3 === void 0 ? void 0 : _error$context3.first_error_message) || (error === null || error === void 0 || (_error$context4 = error.context) === null || _error$context4 === void 0 ? void 0 : _error$context4.http_response_body) || "";
|
|
516
|
+
return error instanceof couchbase.CollectionExistsError || (error === null || error === void 0 || (_error$cause4 = error.cause) === null || _error$cause4 === void 0 ? void 0 : _error$cause4.first_error_code) === 12003 || (error === null || error === void 0 || (_error$cause5 = error.cause) === null || _error$cause5 === void 0 ? void 0 : _error$cause5.first_error_code) === 12027 || /collection .*already exists|already exists/i.test(message);
|
|
558
517
|
}
|
|
559
|
-
|
|
560
518
|
upsertCollection() {
|
|
561
519
|
var _this3 = this;
|
|
562
|
-
|
|
563
520
|
return _asyncToGenerator(function* () {
|
|
564
521
|
try {
|
|
565
522
|
if (!_this3.checkGlobelConnectionObject(_this3.collection_name)) {
|
|
566
523
|
var collection_exists = yield _this3.collectionExists(_this3.collection_name, _this3.scope_name);
|
|
567
|
-
|
|
568
524
|
if (!collection_exists) {
|
|
525
|
+
var _this3$db_config;
|
|
569
526
|
yield _this3.createCollection(_this3.collection_name, _this3.scope_name);
|
|
570
527
|
yield _this3.wait(2000);
|
|
571
|
-
|
|
528
|
+
// `primary_index: false` on the connection replaces the primary
|
|
529
|
+
// index with the base secondary index. A new collection is empty,
|
|
530
|
+
// so building it straight away costs nothing.
|
|
531
|
+
if (((_this3$db_config = _this3.db_config) === null || _this3$db_config === void 0 ? void 0 : _this3$db_config.primary_index) === false) {
|
|
532
|
+
yield _this3.createIndex(_objectSpread(_objectSpread({}, BASE_INDEX), {}, {
|
|
533
|
+
defer_build: false,
|
|
534
|
+
collection: _this3.collection_name,
|
|
535
|
+
scope: _this3.scope_name
|
|
536
|
+
}));
|
|
537
|
+
} else {
|
|
538
|
+
yield _this3.createPrimaryIndex(_this3.scope_name, _this3.collection_name);
|
|
539
|
+
}
|
|
572
540
|
}
|
|
573
541
|
}
|
|
574
542
|
} catch (e) {
|
|
@@ -576,27 +544,22 @@ module.exports = class Database {
|
|
|
576
544
|
}
|
|
577
545
|
})();
|
|
578
546
|
}
|
|
579
|
-
|
|
580
547
|
collectionExists(collection_name, scope_name) {
|
|
581
548
|
var _this4 = this;
|
|
582
|
-
|
|
583
549
|
return _asyncToGenerator(function* () {
|
|
584
550
|
try {
|
|
585
|
-
var _spice3
|
|
586
|
-
|
|
587
|
-
if ((_spice3 = spice) === null || _spice3 === void 0 ? void 0 : (_spice3$db = _spice3.db) === null || _spice3$db === void 0 ? void 0 : (_spice3$db$collection = _spice3$db.collections) === null || _spice3$db$collection === void 0 ? void 0 : _spice3$db$collection.has(collection_name)) {
|
|
551
|
+
var _spice3;
|
|
552
|
+
if ((_spice3 = spice) !== null && _spice3 !== void 0 && (_spice3 = _spice3.db) !== null && _spice3 !== void 0 && (_spice3 = _spice3.collections) !== null && _spice3 !== void 0 && _spice3.has(collection_name)) {
|
|
588
553
|
return true;
|
|
589
554
|
} else {
|
|
590
555
|
var response = yield _this4.cluster.query("SELECT RAW keyspace_id\n FROM system:keyspaces\n WHERE bucket_id = ?\n AND scope_id = ?\n AND keyspace_id = ?\n LIMIT 1", {
|
|
591
556
|
parameters: [_this4.db_config.bucket, scope_name, collection_name],
|
|
592
557
|
scanConsistency: QueryScanConsistency.RequestPlus
|
|
593
558
|
});
|
|
594
|
-
|
|
595
559
|
if (Array.isArray(response === null || response === void 0 ? void 0 : response.rows) && response.rows.length > 0) {
|
|
596
560
|
spice.db.collections.add(collection_name);
|
|
597
561
|
return true;
|
|
598
562
|
}
|
|
599
|
-
|
|
600
563
|
return false;
|
|
601
564
|
}
|
|
602
565
|
} catch (e) {
|
|
@@ -605,10 +568,8 @@ module.exports = class Database {
|
|
|
605
568
|
}
|
|
606
569
|
})();
|
|
607
570
|
}
|
|
608
|
-
|
|
609
571
|
createCollection(connection_name, scope_name) {
|
|
610
572
|
var _this5 = this;
|
|
611
|
-
|
|
612
573
|
return _asyncToGenerator(function* () {
|
|
613
574
|
try {
|
|
614
575
|
var response = yield _this5.cluster.query("CREATE COLLECTION ".concat(_this5.collectionPath(scope_name, connection_name)), {
|
|
@@ -621,15 +582,12 @@ module.exports = class Database {
|
|
|
621
582
|
spice.db.collections.add(connection_name);
|
|
622
583
|
return;
|
|
623
584
|
}
|
|
624
|
-
|
|
625
585
|
console.log("CREATE Collection :::", e);
|
|
626
586
|
}
|
|
627
587
|
})();
|
|
628
588
|
}
|
|
629
|
-
|
|
630
589
|
primaryIndexExists(scope_name, col_name) {
|
|
631
590
|
var _this6 = this;
|
|
632
|
-
|
|
633
591
|
return _asyncToGenerator(function* () {
|
|
634
592
|
try {
|
|
635
593
|
var response = yield _this6.cluster.query("SELECT RAW name\n FROM system:indexes\n WHERE bucket_id = ?\n AND scope_id = ?\n AND keyspace_id = ?\n AND is_primary = true\n LIMIT 1", {
|
|
@@ -643,33 +601,26 @@ module.exports = class Database {
|
|
|
643
601
|
}
|
|
644
602
|
})();
|
|
645
603
|
}
|
|
646
|
-
|
|
647
604
|
createPrimaryIndex(scope_name, col_name) {
|
|
648
605
|
var _this7 = this;
|
|
649
|
-
|
|
650
606
|
return _asyncToGenerator(function* () {
|
|
651
607
|
try {
|
|
652
608
|
if (yield _this7.primaryIndexExists(scope_name, col_name)) {
|
|
653
609
|
return;
|
|
654
610
|
}
|
|
655
|
-
|
|
656
611
|
var response = yield _this7.query("CREATE PRIMARY INDEX ON ".concat(_this7.collectionPath(scope_name, col_name)));
|
|
657
612
|
return response;
|
|
658
613
|
} catch (e) {
|
|
659
614
|
var _e$cause, _e$cause2;
|
|
660
|
-
|
|
661
|
-
if (e instanceof couchbase.IndexExistsError || (e === null || e === void 0 ? void 0 : (_e$cause = e.cause) === null || _e$cause === void 0 ? void 0 : _e$cause.first_error_code) === 4300 || /index .*already exists|index_exists/i.test((e === null || e === void 0 ? void 0 : e.message) || (e === null || e === void 0 ? void 0 : (_e$cause2 = e.cause) === null || _e$cause2 === void 0 ? void 0 : _e$cause2.first_error_message) || "")) {
|
|
615
|
+
if (e instanceof couchbase.IndexExistsError || (e === null || e === void 0 || (_e$cause = e.cause) === null || _e$cause === void 0 ? void 0 : _e$cause.first_error_code) === 4300 || /index .*already exists|index_exists/i.test((e === null || e === void 0 ? void 0 : e.message) || (e === null || e === void 0 || (_e$cause2 = e.cause) === null || _e$cause2 === void 0 ? void 0 : _e$cause2.first_error_message) || "")) {
|
|
662
616
|
return;
|
|
663
617
|
}
|
|
664
|
-
|
|
665
618
|
console.log("CREATE Index :::", e);
|
|
666
619
|
}
|
|
667
620
|
})();
|
|
668
621
|
}
|
|
669
|
-
|
|
670
622
|
createScope(scope_name) {
|
|
671
623
|
var _this8 = this;
|
|
672
|
-
|
|
673
624
|
return _asyncToGenerator(function* () {
|
|
674
625
|
try {
|
|
675
626
|
yield _this8.collection_manager.createScope(scope_name);
|
|
@@ -683,41 +634,187 @@ module.exports = class Database {
|
|
|
683
634
|
})();
|
|
684
635
|
}
|
|
685
636
|
|
|
686
|
-
|
|
687
|
-
|
|
637
|
+
// Index management. Every statement here is DDL or a catalog read against a
|
|
638
|
+
// fully qualified keyspace, run at cluster level and outside `query()` so a
|
|
639
|
+
// slow build never feeds back into the slow-statement path.
|
|
640
|
+
|
|
641
|
+
runDdl(statement) {
|
|
642
|
+
var _arguments = arguments,
|
|
643
|
+
_this9 = this;
|
|
644
|
+
return _asyncToGenerator(function* () {
|
|
645
|
+
var options = _arguments.length > 1 && _arguments[1] !== undefined ? _arguments[1] : {};
|
|
646
|
+
return _this9.observe(_this9.cluster.query(statement, options));
|
|
647
|
+
})();
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
/**
|
|
651
|
+
* Every index on a collection, from system:indexes, including the legacy
|
|
652
|
+
* catalog form of the default collection. Rows carry name, index_key,
|
|
653
|
+
* condition, state, is_primary, using.
|
|
654
|
+
*/
|
|
655
|
+
listIndexes() {
|
|
656
|
+
var _arguments2 = arguments,
|
|
657
|
+
_this0 = this;
|
|
658
|
+
return _asyncToGenerator(function* () {
|
|
659
|
+
var _response$meta;
|
|
660
|
+
var _ref2 = _arguments2.length > 0 && _arguments2[0] !== undefined ? _arguments2[0] : {},
|
|
661
|
+
_ref2$collection = _ref2.collection,
|
|
662
|
+
collection = _ref2$collection === void 0 ? _this0.collection_name : _ref2$collection,
|
|
663
|
+
_ref2$scope = _ref2.scope,
|
|
664
|
+
scope = _ref2$scope === void 0 ? _this0.scope_name : _ref2$scope;
|
|
665
|
+
var response = yield _this0.runDdl("SELECT RAW i FROM system:indexes AS i\n WHERE i.namespace_id = 'default'\n AND ((i.bucket_id = $bucket AND i.scope_id = $scope AND i.keyspace_id = $collection)\n OR ($scope = '_default' AND $collection = '_default'\n AND i.bucket_id IS MISSING AND i.scope_id IS MISSING AND i.keyspace_id = $bucket))", {
|
|
666
|
+
parameters: {
|
|
667
|
+
bucket: _this0.db_config.bucket,
|
|
668
|
+
scope,
|
|
669
|
+
collection
|
|
670
|
+
}
|
|
671
|
+
});
|
|
672
|
+
// A failed or partial inventory is not evidence that no index exists.
|
|
673
|
+
if (!Array.isArray(response === null || response === void 0 ? void 0 : response.rows) || response !== null && response !== void 0 && (_response$meta = response.meta) !== null && _response$meta !== void 0 && (_response$meta = _response$meta.warnings) !== null && _response$meta !== void 0 && _response$meta.length) {
|
|
674
|
+
throw new Error("Cannot read the index inventory for ".concat(scope, ".").concat(collection));
|
|
675
|
+
}
|
|
676
|
+
return response.rows;
|
|
677
|
+
})();
|
|
678
|
+
}
|
|
688
679
|
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
680
|
+
/**
|
|
681
|
+
* `CREATE INDEX IF NOT EXISTS`. Deferred by default so several indexes on
|
|
682
|
+
* one collection can be built together with `buildIndexes`, in one pass
|
|
683
|
+
* over the data.
|
|
684
|
+
*/
|
|
685
|
+
createIndex() {
|
|
686
|
+
var _arguments3 = arguments,
|
|
687
|
+
_this1 = this;
|
|
688
|
+
return _asyncToGenerator(function* () {
|
|
689
|
+
var _ref3 = _arguments3.length > 0 && _arguments3[0] !== undefined ? _arguments3[0] : {},
|
|
690
|
+
name = _ref3.name,
|
|
691
|
+
keys = _ref3.keys,
|
|
692
|
+
where = _ref3.where,
|
|
693
|
+
_ref3$defer_build = _ref3.defer_build,
|
|
694
|
+
defer_build = _ref3$defer_build === void 0 ? true : _ref3$defer_build,
|
|
695
|
+
num_replica = _ref3.num_replica,
|
|
696
|
+
_ref3$collection = _ref3.collection,
|
|
697
|
+
collection = _ref3$collection === void 0 ? _this1.collection_name : _ref3$collection,
|
|
698
|
+
_ref3$scope = _ref3.scope,
|
|
699
|
+
scope = _ref3$scope === void 0 ? _this1.scope_name : _ref3$scope;
|
|
700
|
+
if (!name || !Array.isArray(keys) || keys.length === 0) {
|
|
701
|
+
throw new TypeError("createIndex needs a name and at least one key");
|
|
702
|
+
}
|
|
703
|
+
var withClause = {
|
|
704
|
+
defer_build: Boolean(defer_build)
|
|
705
|
+
};
|
|
706
|
+
if (Number.isInteger(num_replica) && num_replica >= 0) {
|
|
707
|
+
withClause.num_replica = num_replica;
|
|
708
|
+
}
|
|
709
|
+
var statement = "CREATE INDEX IF NOT EXISTS ".concat(_this1.quoteIdentifier(name), " ") + "ON ".concat(_this1.collectionPath(scope, collection), "(").concat(keys.map(renderIndexKey).join(", "), ")") + (where ? " WHERE ".concat(where) : "") + " WITH ".concat(JSON.stringify(withClause));
|
|
710
|
+
yield _this1.runDdl(statement);
|
|
711
|
+
return statement;
|
|
712
|
+
})();
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
/** Builds deferred indexes on one collection together. */
|
|
716
|
+
buildIndexes(names) {
|
|
717
|
+
var _arguments4 = arguments,
|
|
718
|
+
_this10 = this;
|
|
719
|
+
return _asyncToGenerator(function* () {
|
|
720
|
+
var _ref4 = _arguments4.length > 1 && _arguments4[1] !== undefined ? _arguments4[1] : {},
|
|
721
|
+
_ref4$collection = _ref4.collection,
|
|
722
|
+
collection = _ref4$collection === void 0 ? _this10.collection_name : _ref4$collection,
|
|
723
|
+
_ref4$scope = _ref4.scope,
|
|
724
|
+
scope = _ref4$scope === void 0 ? _this10.scope_name : _ref4$scope;
|
|
725
|
+
var list = [...new Set(names || [])];
|
|
726
|
+
if (list.length === 0) return null;
|
|
727
|
+
var statement = "BUILD INDEX ON ".concat(_this10.collectionPath(scope, collection)) + "(".concat(list.map(name => _this10.quoteIdentifier(name)).join(", "), ")");
|
|
728
|
+
yield _this10.runDdl(statement);
|
|
729
|
+
return statement;
|
|
730
|
+
})();
|
|
731
|
+
}
|
|
732
|
+
dropIndex(name) {
|
|
733
|
+
var _arguments5 = arguments,
|
|
734
|
+
_this11 = this;
|
|
735
|
+
return _asyncToGenerator(function* () {
|
|
736
|
+
var _ref5 = _arguments5.length > 1 && _arguments5[1] !== undefined ? _arguments5[1] : {},
|
|
737
|
+
_ref5$collection = _ref5.collection,
|
|
738
|
+
collection = _ref5$collection === void 0 ? _this11.collection_name : _ref5$collection,
|
|
739
|
+
_ref5$scope = _ref5.scope,
|
|
740
|
+
scope = _ref5$scope === void 0 ? _this11.scope_name : _ref5$scope;
|
|
741
|
+
var statement = "DROP INDEX IF EXISTS ".concat(_this11.quoteIdentifier(name), " ON ").concat(_this11.collectionPath(scope, collection));
|
|
742
|
+
yield _this11.runDdl(statement);
|
|
743
|
+
return statement;
|
|
744
|
+
})();
|
|
745
|
+
}
|
|
746
|
+
dropPrimaryIndex() {
|
|
747
|
+
var _arguments6 = arguments,
|
|
748
|
+
_this12 = this;
|
|
749
|
+
return _asyncToGenerator(function* () {
|
|
750
|
+
var _ref6 = _arguments6.length > 0 && _arguments6[0] !== undefined ? _arguments6[0] : {},
|
|
751
|
+
_ref6$collection = _ref6.collection,
|
|
752
|
+
collection = _ref6$collection === void 0 ? _this12.collection_name : _ref6$collection,
|
|
753
|
+
_ref6$scope = _ref6.scope,
|
|
754
|
+
scope = _ref6$scope === void 0 ? _this12.scope_name : _ref6$scope;
|
|
755
|
+
var statement = "DROP PRIMARY INDEX IF EXISTS ON ".concat(_this12.collectionPath(scope, collection));
|
|
756
|
+
yield _this12.runDdl(statement);
|
|
757
|
+
return statement;
|
|
758
|
+
})();
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
/** The plan for a statement, resolved in this instance's scope by default. */
|
|
762
|
+
explain(statement) {
|
|
763
|
+
var _arguments7 = arguments,
|
|
764
|
+
_this13 = this;
|
|
765
|
+
return _asyncToGenerator(function* () {
|
|
766
|
+
var _response$rows;
|
|
767
|
+
var query_scope = _arguments7.length > 1 && _arguments7[1] !== undefined ? _arguments7[1] : _this13.use_bucket_name ? "cluster" : "scope";
|
|
768
|
+
var exec_scope = query_scope === "cluster" ? _this13.cluster : _this13.scope;
|
|
769
|
+
var response = yield _this13.observe(exec_scope.query("EXPLAIN ".concat(statement)));
|
|
770
|
+
return response === null || response === void 0 || (_response$rows = response.rows) === null || _response$rows === void 0 ? void 0 : _response$rows[0];
|
|
771
|
+
})();
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
/** Couchbase's own index advice for a statement (the ADVISE statement). */
|
|
775
|
+
advise(statement) {
|
|
776
|
+
var _arguments8 = arguments,
|
|
777
|
+
_this14 = this;
|
|
692
778
|
return _asyncToGenerator(function* () {
|
|
779
|
+
var _response$rows2;
|
|
780
|
+
var query_scope = _arguments8.length > 1 && _arguments8[1] !== undefined ? _arguments8[1] : _this14.use_bucket_name ? "cluster" : "scope";
|
|
781
|
+
var exec_scope = query_scope === "cluster" ? _this14.cluster : _this14.scope;
|
|
782
|
+
var response = yield _this14.observe(exec_scope.query("ADVISE ".concat(statement)));
|
|
783
|
+
return response === null || response === void 0 || (_response$rows2 = response.rows) === null || _response$rows2 === void 0 ? void 0 : _response$rows2[0];
|
|
784
|
+
})();
|
|
785
|
+
}
|
|
786
|
+
get(doc_id) {
|
|
787
|
+
var _arguments9 = arguments,
|
|
788
|
+
_this15 = this;
|
|
789
|
+
return _asyncToGenerator(function* () {
|
|
790
|
+
var _ref7 = _arguments9.length > 1 && _arguments9[1] !== undefined ? _arguments9[1] : {},
|
|
791
|
+
_ref7$columns = _ref7.columns,
|
|
792
|
+
columns = _ref7$columns === void 0 ? [] : _ref7$columns;
|
|
693
793
|
try {
|
|
694
794
|
var useLookupIn = columns.length > 0;
|
|
695
|
-
var collection =
|
|
696
|
-
|
|
795
|
+
var collection = _this15.use_bucket_name ? _this15.connection : _this15.collection;
|
|
697
796
|
if (!useLookupIn) {
|
|
698
|
-
var response = yield
|
|
797
|
+
var response = yield _this15.observe(collection.get(doc_id));
|
|
699
798
|
return _objectSpread({
|
|
700
799
|
id: doc_id
|
|
701
800
|
}, response.content);
|
|
702
|
-
}
|
|
703
|
-
|
|
801
|
+
}
|
|
704
802
|
|
|
803
|
+
// Batch columns into chunks of LOOKUP_IN_MAX_SPECS (16)
|
|
705
804
|
var value = {
|
|
706
805
|
id: doc_id
|
|
707
806
|
};
|
|
807
|
+
var columnBatches = _.chunk(columns, LOOKUP_IN_MAX_SPECS);
|
|
708
808
|
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
809
|
+
// Execute all batch requests in parallel
|
|
712
810
|
var batchPromises = columnBatches.map(batch => {
|
|
713
811
|
var specs = batch.map(col => couchbase.LookupInSpec.get(col));
|
|
714
812
|
return collection.lookupIn(doc_id, specs);
|
|
715
813
|
});
|
|
716
814
|
var batchResults = yield Promise.allSettled(batchPromises);
|
|
815
|
+
_this15.recordSettled(batchResults);
|
|
717
816
|
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
817
|
+
// Merge results from all batches
|
|
721
818
|
batchResults.forEach((result, batchIndex) => {
|
|
722
819
|
if (result.status === "fulfilled" && result.value) {
|
|
723
820
|
var batch = columnBatches[batchIndex];
|
|
@@ -734,131 +831,109 @@ module.exports = class Database {
|
|
|
734
831
|
}
|
|
735
832
|
})();
|
|
736
833
|
}
|
|
737
|
-
|
|
738
834
|
insert(doc_id, document, expiry) {
|
|
739
|
-
var
|
|
740
|
-
|
|
835
|
+
var _this16 = this;
|
|
741
836
|
return _asyncToGenerator(function* () {
|
|
742
837
|
try {
|
|
743
|
-
yield
|
|
838
|
+
yield _this16.observe((_this16.use_bucket_name ? _this16.connection : _this16.collection).insert(doc_id, document, {
|
|
744
839
|
expiry: expiry
|
|
745
840
|
}));
|
|
746
841
|
} catch (e) {
|
|
747
842
|
console.log(e.message, e, doc_id);
|
|
748
843
|
}
|
|
749
|
-
|
|
750
|
-
return yield _this10.get(doc_id);
|
|
844
|
+
return yield _this16.get(doc_id);
|
|
751
845
|
})();
|
|
752
846
|
}
|
|
753
|
-
|
|
754
847
|
touch(doc_id) {
|
|
755
|
-
var
|
|
756
|
-
|
|
757
|
-
var expiry = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
|
|
848
|
+
var _arguments0 = arguments,
|
|
849
|
+
_this17 = this;
|
|
758
850
|
return _asyncToGenerator(function* () {
|
|
759
|
-
|
|
760
|
-
|
|
851
|
+
var expiry = _arguments0.length > 1 && _arguments0[1] !== undefined ? _arguments0[1] : 0;
|
|
852
|
+
yield _this17.observe((_this17.use_bucket_name ? _this17.connection : _this17.collection).touch(doc_id, expiry));
|
|
853
|
+
return yield _this17.get(doc_id);
|
|
761
854
|
})();
|
|
762
855
|
}
|
|
763
|
-
|
|
764
856
|
count(doc_id, adjustment, initial_value) {
|
|
765
|
-
var
|
|
766
|
-
|
|
857
|
+
var _this18 = this;
|
|
767
858
|
return _asyncToGenerator(function* () {
|
|
768
|
-
var res = yield
|
|
859
|
+
var res = yield _this18.observe((_this18.use_bucket_name ? _this18.connection : _this18.collection).counter(doc_id, adjustment, {
|
|
769
860
|
initial: initial_value
|
|
770
861
|
}));
|
|
771
862
|
return res.value;
|
|
772
863
|
})();
|
|
773
864
|
}
|
|
774
|
-
|
|
775
865
|
update(doc_id, document, expiry) {
|
|
776
|
-
var
|
|
777
|
-
|
|
866
|
+
var _this19 = this;
|
|
778
867
|
return _asyncToGenerator(function* () {
|
|
779
|
-
var res = yield
|
|
868
|
+
var res = yield _this19.observe((_this19.use_bucket_name ? _this19.connection : _this19.collection).replace(doc_id, document, {
|
|
780
869
|
expiry: expiry
|
|
781
870
|
}));
|
|
782
871
|
return res.value;
|
|
783
872
|
})();
|
|
784
873
|
}
|
|
785
|
-
|
|
786
874
|
delete(doc_id) {
|
|
787
|
-
var
|
|
788
|
-
|
|
875
|
+
var _this20 = this;
|
|
789
876
|
return _asyncToGenerator(function* () {
|
|
790
|
-
var res = yield
|
|
877
|
+
var res = yield _this20.observe((_this20.use_bucket_name ? _this20.connection : _this20.collection).remove(doc_id));
|
|
791
878
|
return res.value;
|
|
792
879
|
})();
|
|
793
880
|
}
|
|
794
|
-
|
|
795
881
|
multi_get(keys) {
|
|
796
|
-
var
|
|
797
|
-
|
|
798
|
-
var {
|
|
799
|
-
keep_type = true,
|
|
800
|
-
columns = []
|
|
801
|
-
} = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
882
|
+
var _arguments1 = arguments,
|
|
883
|
+
_this21 = this;
|
|
802
884
|
return _asyncToGenerator(function* () {
|
|
885
|
+
var _ref8 = _arguments1.length > 1 && _arguments1[1] !== undefined ? _arguments1[1] : {},
|
|
886
|
+
_ref8$keep_type = _ref8.keep_type,
|
|
887
|
+
keep_type = _ref8$keep_type === void 0 ? true : _ref8$keep_type,
|
|
888
|
+
_ref8$columns = _ref8.columns,
|
|
889
|
+
columns = _ref8$columns === void 0 ? [] : _ref8$columns;
|
|
803
890
|
if (keys.length == 0) {
|
|
804
891
|
return [];
|
|
805
892
|
}
|
|
806
|
-
|
|
807
893
|
if (_.isArray(keys) && keys.length > 0) {
|
|
808
894
|
var useLookupIn = columns.length > 0;
|
|
809
|
-
var collection =
|
|
810
|
-
|
|
895
|
+
var collection = _this21.use_bucket_name ? _this21.connection : _this21.collection;
|
|
811
896
|
if (!useLookupIn) {
|
|
812
897
|
// Regular get - no batching needed
|
|
813
898
|
var functions = _.map(keys, key => collection.get(key));
|
|
814
|
-
|
|
815
899
|
var returned_all = yield Promise.allSettled(functions);
|
|
816
|
-
|
|
817
|
-
_this15.recordSettled(returned_all);
|
|
818
|
-
|
|
900
|
+
_this21.recordSettled(returned_all);
|
|
819
901
|
var responses = _.flatten(_.map(returned_all, returned_obj => returned_obj === null || returned_obj === void 0 ? void 0 : returned_obj.value));
|
|
820
|
-
|
|
821
902
|
var _final_responses = _.compact(_.map(responses, (res, index) => {
|
|
822
903
|
if (res) {
|
|
823
904
|
var value = res.value;
|
|
824
|
-
|
|
825
905
|
if (!keep_type && value) {
|
|
826
906
|
delete value.type;
|
|
827
907
|
}
|
|
828
|
-
|
|
829
908
|
if (value) {
|
|
830
909
|
value.id = keys[index];
|
|
831
910
|
}
|
|
832
|
-
|
|
833
911
|
return value;
|
|
834
912
|
}
|
|
835
913
|
}));
|
|
836
|
-
|
|
837
914
|
return _final_responses;
|
|
838
|
-
}
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
var columnBatches = _.chunk(columns, LOOKUP_IN_MAX_SPECS); // For each key, we need to fetch all column batches and merge the results
|
|
915
|
+
}
|
|
842
916
|
|
|
917
|
+
// lookupIn with batching for columns > 16
|
|
918
|
+
var columnBatches = _.chunk(columns, LOOKUP_IN_MAX_SPECS);
|
|
843
919
|
|
|
844
|
-
|
|
845
|
-
/*#__PURE__*/
|
|
846
|
-
|
|
847
|
-
var _ref = _asyncToGenerator(function* (key) {
|
|
920
|
+
// For each key, we need to fetch all column batches and merge the results
|
|
921
|
+
var keyPromises = _.map(keys, /*#__PURE__*/function () {
|
|
922
|
+
var _ref9 = _asyncToGenerator(function* (key) {
|
|
848
923
|
try {
|
|
849
924
|
var value = {
|
|
850
925
|
id: key
|
|
851
|
-
};
|
|
926
|
+
};
|
|
852
927
|
|
|
928
|
+
// Execute all batch requests for this key in parallel
|
|
853
929
|
var batchPromises = columnBatches.map(batch => {
|
|
854
930
|
var specs = batch.map(col => couchbase.LookupInSpec.get(col));
|
|
855
931
|
return collection.lookupIn(key, specs);
|
|
856
932
|
});
|
|
857
933
|
var batchResults = yield Promise.allSettled(batchPromises);
|
|
934
|
+
_this21.recordSettled(batchResults);
|
|
858
935
|
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
936
|
+
// Merge results from all batches
|
|
862
937
|
batchResults.forEach((result, batchIndex) => {
|
|
863
938
|
if (result.status === "fulfilled" && result.value) {
|
|
864
939
|
var batch = columnBatches[batchIndex];
|
|
@@ -869,75 +944,56 @@ module.exports = class Database {
|
|
|
869
944
|
});
|
|
870
945
|
}
|
|
871
946
|
});
|
|
872
|
-
|
|
873
947
|
if (!keep_type && value) {
|
|
874
948
|
delete value.type;
|
|
875
949
|
}
|
|
876
|
-
|
|
877
950
|
return value;
|
|
878
951
|
} catch (e) {
|
|
879
952
|
return null;
|
|
880
953
|
}
|
|
881
954
|
});
|
|
882
|
-
|
|
883
955
|
return function (_x) {
|
|
884
|
-
return
|
|
956
|
+
return _ref9.apply(this, arguments);
|
|
885
957
|
};
|
|
886
958
|
}());
|
|
887
|
-
|
|
888
959
|
var results = yield Promise.allSettled(keyPromises);
|
|
889
|
-
|
|
890
960
|
var final_responses = _.compact(_.map(results, result => {
|
|
891
961
|
if (result.status === "fulfilled" && result.value) {
|
|
892
962
|
// Only return if we got at least some data beyond just the id
|
|
893
963
|
return Object.keys(result.value).length > 1 ? result.value : null;
|
|
894
964
|
}
|
|
895
|
-
|
|
896
965
|
return null;
|
|
897
966
|
}));
|
|
898
|
-
|
|
899
967
|
return final_responses;
|
|
900
968
|
} else {
|
|
901
969
|
return [];
|
|
902
970
|
}
|
|
903
971
|
})();
|
|
904
972
|
}
|
|
905
|
-
|
|
906
973
|
get_view(view, design_doc, search_query, limit, offset) {
|
|
907
974
|
var that = this;
|
|
908
|
-
return new Promise(
|
|
909
|
-
|
|
910
|
-
function () {
|
|
911
|
-
var _ref2 = _asyncToGenerator(function* (resolve, reject) {
|
|
975
|
+
return new Promise(/*#__PURE__*/function () {
|
|
976
|
+
var _ref0 = _asyncToGenerator(function* (resolve, reject) {
|
|
912
977
|
var ViewQuery = couchbase.ViewQuery;
|
|
913
978
|
var query = ViewQuery.from(design_doc, view);
|
|
914
|
-
|
|
915
979
|
if (!limit) {
|
|
916
980
|
limit = 0;
|
|
917
981
|
}
|
|
918
|
-
|
|
919
982
|
if (!offset) {
|
|
920
983
|
offset = 0;
|
|
921
984
|
}
|
|
922
|
-
|
|
923
985
|
if (limit > 0) {
|
|
924
986
|
query.limit(limit);
|
|
925
987
|
}
|
|
926
|
-
|
|
927
988
|
query.skip(offset);
|
|
928
|
-
|
|
929
989
|
if (search_query) {
|
|
930
990
|
if (search_query.length > 0) {
|
|
931
991
|
query.range(search_query, search_query);
|
|
932
992
|
}
|
|
933
993
|
}
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
/*#__PURE__*/
|
|
937
|
-
function () {
|
|
938
|
-
var _ref3 = _asyncToGenerator(function* (err, results, matrix) {
|
|
994
|
+
that.connection.query(query, /*#__PURE__*/function () {
|
|
995
|
+
var _ref1 = _asyncToGenerator(function* (err, results, matrix) {
|
|
939
996
|
var return_value = [];
|
|
940
|
-
|
|
941
997
|
if (err) {
|
|
942
998
|
reject(err);
|
|
943
999
|
} else {
|
|
@@ -948,7 +1004,6 @@ module.exports = class Database {
|
|
|
948
1004
|
return_value.push(results[i].key);
|
|
949
1005
|
}
|
|
950
1006
|
}
|
|
951
|
-
|
|
952
1007
|
try {
|
|
953
1008
|
var created = yield that.multi_get(return_value);
|
|
954
1009
|
resolve(created);
|
|
@@ -957,42 +1012,38 @@ module.exports = class Database {
|
|
|
957
1012
|
}
|
|
958
1013
|
}
|
|
959
1014
|
});
|
|
960
|
-
|
|
961
1015
|
return function (_x4, _x5, _x6) {
|
|
962
|
-
return
|
|
1016
|
+
return _ref1.apply(this, arguments);
|
|
963
1017
|
};
|
|
964
1018
|
}());
|
|
965
1019
|
});
|
|
966
|
-
|
|
967
1020
|
return function (_x2, _x3) {
|
|
968
|
-
return
|
|
1021
|
+
return _ref0.apply(this, arguments);
|
|
969
1022
|
};
|
|
970
1023
|
}());
|
|
971
1024
|
}
|
|
972
|
-
|
|
973
1025
|
search(type) {
|
|
974
|
-
var
|
|
975
|
-
|
|
976
|
-
var columns = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : "";
|
|
977
|
-
var query = arguments.length > 2 ? arguments[2] : undefined;
|
|
978
|
-
var limit = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0;
|
|
979
|
-
var offset = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 0;
|
|
980
|
-
var order_by = arguments.length > 5 ? arguments[5] : undefined;
|
|
981
|
-
var do_count = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : "false";
|
|
982
|
-
var query_scope = arguments.length > 7 ? arguments[7] : undefined;
|
|
983
|
-
var join = arguments.length > 8 && arguments[8] !== undefined ? arguments[8] : "";
|
|
984
|
-
var query_options = arguments.length > 9 && arguments[9] !== undefined ? arguments[9] : {};
|
|
1026
|
+
var _arguments10 = arguments,
|
|
1027
|
+
_this22 = this;
|
|
985
1028
|
return _asyncToGenerator(function* () {
|
|
986
|
-
var
|
|
987
|
-
var
|
|
988
|
-
|
|
1029
|
+
var columns = _arguments10.length > 1 && _arguments10[1] !== undefined ? _arguments10[1] : "";
|
|
1030
|
+
var query = _arguments10.length > 2 ? _arguments10[2] : undefined;
|
|
1031
|
+
var limit = _arguments10.length > 3 && _arguments10[3] !== undefined ? _arguments10[3] : 0;
|
|
1032
|
+
var offset = _arguments10.length > 4 && _arguments10[4] !== undefined ? _arguments10[4] : 0;
|
|
1033
|
+
var order_by = _arguments10.length > 5 ? _arguments10[5] : undefined;
|
|
1034
|
+
var do_count = _arguments10.length > 6 && _arguments10[6] !== undefined ? _arguments10[6] : "false";
|
|
1035
|
+
var query_scope = _arguments10.length > 7 ? _arguments10[7] : undefined;
|
|
1036
|
+
var join = _arguments10.length > 8 && _arguments10[8] !== undefined ? _arguments10[8] : "";
|
|
1037
|
+
var query_options = _arguments10.length > 9 && _arguments10[9] !== undefined ? _arguments10[9] : {};
|
|
1038
|
+
var is_legacy = _this22.scope_name !== "_default" && _this22.collection_name !== "_default";
|
|
1039
|
+
var bucket = "`".concat(_this22.use_bucket_name ? _this22.bucket : _this22.collection_name, "`");
|
|
989
1040
|
function processColumns(columnsStr) {
|
|
990
1041
|
// If columns is empty or *, return default
|
|
991
1042
|
if (columnsStr === "*" || !columnsStr) {
|
|
992
1043
|
return "(".concat(bucket, ").*, META(").concat(bucket, ").id AS id");
|
|
993
|
-
}
|
|
994
|
-
|
|
1044
|
+
}
|
|
995
1045
|
|
|
1046
|
+
// Split by comma and process each column
|
|
996
1047
|
var columnList = columnsStr.split(",").map(col => col.trim());
|
|
997
1048
|
var hasId = false;
|
|
998
1049
|
var processedColumns = columnList.map(col => {
|
|
@@ -1000,10 +1051,9 @@ module.exports = class Database {
|
|
|
1000
1051
|
// Patterns: `collection`.`id`, collection.id, `id`, id
|
|
1001
1052
|
var idPattern = /^(?:`?(\w+)`?\.)?`?id`?$/i;
|
|
1002
1053
|
var match = col.match(idPattern);
|
|
1003
|
-
|
|
1004
1054
|
if (match) {
|
|
1005
|
-
hasId = true;
|
|
1006
|
-
|
|
1055
|
+
hasId = true;
|
|
1056
|
+
// match[1] contains the collection name if present
|
|
1007
1057
|
if (match[1]) {
|
|
1008
1058
|
// Has collection prefix: `collection`.`id` or collection.id
|
|
1009
1059
|
return "META(`".concat(match[1], "`).id AS id");
|
|
@@ -1011,52 +1061,47 @@ module.exports = class Database {
|
|
|
1011
1061
|
// Just id without collection prefix
|
|
1012
1062
|
return "META(".concat(bucket, ").id AS id");
|
|
1013
1063
|
}
|
|
1014
|
-
}
|
|
1015
|
-
|
|
1064
|
+
}
|
|
1016
1065
|
|
|
1066
|
+
// Return the column as-is
|
|
1017
1067
|
return col;
|
|
1018
|
-
});
|
|
1068
|
+
});
|
|
1019
1069
|
|
|
1070
|
+
// If no id column was provided, add it automatically
|
|
1020
1071
|
if (!hasId) {
|
|
1021
1072
|
processedColumns.push("META(".concat(bucket, ").id AS id"));
|
|
1022
1073
|
}
|
|
1023
|
-
|
|
1024
1074
|
return processedColumns.join(", ");
|
|
1025
1075
|
}
|
|
1026
|
-
|
|
1027
1076
|
try {
|
|
1028
1077
|
columns = processColumns(columns);
|
|
1029
1078
|
var front_query = "SELECT ".concat(columns, " FROM ").concat(bucket, " ").concat(join);
|
|
1030
1079
|
var conditions = [];
|
|
1031
|
-
|
|
1032
|
-
if (_this16.use_bucket_name) {
|
|
1080
|
+
if (_this22.use_bucket_name) {
|
|
1033
1081
|
conditions.push("type = '".concat(type, "'"));
|
|
1034
1082
|
}
|
|
1035
|
-
|
|
1036
1083
|
if (query) {
|
|
1037
1084
|
conditions.push(query);
|
|
1038
|
-
}
|
|
1085
|
+
}
|
|
1086
|
+
|
|
1087
|
+
// The conditions are folded into `where_clause` itself so that both queries below are built
|
|
1039
1088
|
// from one string. Previously this held the bare literal " WHERE " and only the data query
|
|
1040
1089
|
// appended `conditions.join(" AND ")`, which left the count query emitting a dangling
|
|
1041
1090
|
// `WHERE` — Couchbase answered "syntax error - at end of input" and every `do_count=true`
|
|
1042
1091
|
// request failed with a 400, on every resource.
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
1092
|
var where_clause = conditions.length ? " WHERE ".concat(conditions.join(" AND ")) : "";
|
|
1046
1093
|
query = front_query + where_clause;
|
|
1047
|
-
var count_query = "SELECT count(meta(".concat(bucket, ").id) as count FROM ").concat(bucket, " ").concat(join, " ").concat(where_clause);
|
|
1048
|
-
|
|
1094
|
+
var count_query = "SELECT count(meta(".concat(bucket, ").id) as count FROM ").concat(bucket, " ").concat(join, " ").concat(where_clause);
|
|
1095
|
+
//console.log("Cound Query::", count_query);
|
|
1049
1096
|
var order_by_clause = order_by ? " ORDER BY ".concat(order_by) : "";
|
|
1050
1097
|
var limit_clause = limit > 0 ? " LIMIT ".concat(limit) : "";
|
|
1051
1098
|
var offset_clause = offset > 0 ? " OFFSET ".concat(offset) : "";
|
|
1052
1099
|
query = query + order_by_clause + limit_clause + offset_clause;
|
|
1053
|
-
var active_query_space = query_scope || (!
|
|
1054
|
-
var queries = [
|
|
1055
|
-
|
|
1100
|
+
var active_query_space = query_scope || (!_this22.use_bucket_name ? "scope" : "cluster");
|
|
1101
|
+
var queries = [_this22.getData(query, active_query_space, query_options)];
|
|
1056
1102
|
if (do_count != "false") {
|
|
1057
|
-
queries.push(
|
|
1103
|
+
queries.push(_this22.getCount(count_query, active_query_space, query_options));
|
|
1058
1104
|
}
|
|
1059
|
-
|
|
1060
1105
|
var responses = yield Promise.all(queries);
|
|
1061
1106
|
return {
|
|
1062
1107
|
data: responses[0],
|
|
@@ -1067,38 +1112,28 @@ module.exports = class Database {
|
|
|
1067
1112
|
}
|
|
1068
1113
|
})();
|
|
1069
1114
|
}
|
|
1070
|
-
|
|
1071
1115
|
full_text_search(type, search_query, limit) {
|
|
1072
1116
|
var offset = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0;
|
|
1073
1117
|
var full_text_index = arguments.length > 4 ? arguments[4] : undefined;
|
|
1074
1118
|
var that = this;
|
|
1075
|
-
return new Promise(
|
|
1076
|
-
|
|
1077
|
-
function () {
|
|
1078
|
-
var _ref4 = _asyncToGenerator(function* (resolve, reject) {
|
|
1119
|
+
return new Promise(/*#__PURE__*/function () {
|
|
1120
|
+
var _ref10 = _asyncToGenerator(function* (resolve, reject) {
|
|
1079
1121
|
try {
|
|
1080
1122
|
var full_query = search_query + " AND type:" + type;
|
|
1081
1123
|
var query = that.SearchQuery.new(that.full_text_index || full_text_index, that.SearchQuery.queryString(full_query));
|
|
1082
|
-
|
|
1083
1124
|
if (!limit) {
|
|
1084
1125
|
limit = 0;
|
|
1085
1126
|
}
|
|
1086
|
-
|
|
1087
1127
|
if (!offset) {
|
|
1088
1128
|
offset = 0;
|
|
1089
1129
|
}
|
|
1090
|
-
|
|
1091
1130
|
if (limit > 0) {
|
|
1092
1131
|
query.limit(limit);
|
|
1093
1132
|
}
|
|
1094
|
-
|
|
1095
1133
|
query.skip(offset);
|
|
1096
|
-
that.cluster.query(query,
|
|
1097
|
-
|
|
1098
|
-
function () {
|
|
1099
|
-
var _ref5 = _asyncToGenerator(function* (err, results, matrix) {
|
|
1134
|
+
that.cluster.query(query, /*#__PURE__*/function () {
|
|
1135
|
+
var _ref11 = _asyncToGenerator(function* (err, results, matrix) {
|
|
1100
1136
|
var return_value = [];
|
|
1101
|
-
|
|
1102
1137
|
if (err) {
|
|
1103
1138
|
reject(err);
|
|
1104
1139
|
} else {
|
|
@@ -1109,7 +1144,6 @@ module.exports = class Database {
|
|
|
1109
1144
|
return_value.push(results[i].id);
|
|
1110
1145
|
}
|
|
1111
1146
|
}
|
|
1112
|
-
|
|
1113
1147
|
try {
|
|
1114
1148
|
var getted = yield that.multi_get(return_value);
|
|
1115
1149
|
resolve({
|
|
@@ -1121,64 +1155,58 @@ module.exports = class Database {
|
|
|
1121
1155
|
}
|
|
1122
1156
|
}
|
|
1123
1157
|
});
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
return _ref5.apply(this, arguments);
|
|
1158
|
+
return function (_x9, _x0, _x1) {
|
|
1159
|
+
return _ref11.apply(this, arguments);
|
|
1127
1160
|
};
|
|
1128
1161
|
}());
|
|
1129
1162
|
} catch (e) {
|
|
1130
1163
|
reject(e);
|
|
1131
1164
|
}
|
|
1132
1165
|
});
|
|
1133
|
-
|
|
1134
1166
|
return function (_x7, _x8) {
|
|
1135
|
-
return
|
|
1167
|
+
return _ref10.apply(this, arguments);
|
|
1136
1168
|
};
|
|
1137
1169
|
}());
|
|
1138
1170
|
}
|
|
1139
|
-
|
|
1140
1171
|
getCount(count_query_element) {
|
|
1141
|
-
var
|
|
1142
|
-
|
|
1143
|
-
var query_scope = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : "cluster";
|
|
1144
|
-
var query_options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
|
|
1172
|
+
var _arguments11 = arguments,
|
|
1173
|
+
_this23 = this;
|
|
1145
1174
|
return _asyncToGenerator(function* () {
|
|
1146
|
-
var
|
|
1175
|
+
var query_scope = _arguments11.length > 1 && _arguments11[1] !== undefined ? _arguments11[1] : "cluster";
|
|
1176
|
+
var query_options = _arguments11.length > 2 && _arguments11[2] !== undefined ? _arguments11[2] : {};
|
|
1177
|
+
var response = yield _this23.query(count_query_element, query_scope, query_options);
|
|
1147
1178
|
return response.rows[0].count;
|
|
1148
1179
|
})();
|
|
1149
1180
|
}
|
|
1150
|
-
|
|
1151
1181
|
getData(query_element) {
|
|
1152
|
-
var
|
|
1153
|
-
|
|
1154
|
-
var query_scope = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : "cluster";
|
|
1155
|
-
var query_options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
|
|
1182
|
+
var _arguments12 = arguments,
|
|
1183
|
+
_this24 = this;
|
|
1156
1184
|
return _asyncToGenerator(function* () {
|
|
1157
|
-
var
|
|
1185
|
+
var query_scope = _arguments12.length > 1 && _arguments12[1] !== undefined ? _arguments12[1] : "cluster";
|
|
1186
|
+
var query_options = _arguments12.length > 2 && _arguments12[2] !== undefined ? _arguments12[2] : {};
|
|
1187
|
+
var response = yield _this24.query(query_element, query_scope, query_options);
|
|
1158
1188
|
return response.rows;
|
|
1159
1189
|
})();
|
|
1160
1190
|
}
|
|
1191
|
+
|
|
1161
1192
|
/**
|
|
1162
1193
|
* Recursively checks if the query plan contains a Fetch operator
|
|
1163
1194
|
* @param {Object} plan - The EXPLAIN query plan
|
|
1164
1195
|
* @returns {boolean} - True if Fetch operator is found
|
|
1165
1196
|
*/
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
1197
|
hasFetchOperator(plan) {
|
|
1169
1198
|
if (!plan || typeof plan !== "object") {
|
|
1170
1199
|
return false;
|
|
1171
|
-
}
|
|
1172
|
-
|
|
1200
|
+
}
|
|
1173
1201
|
|
|
1202
|
+
// Check if this node is a Fetch operator
|
|
1174
1203
|
if (plan["#operator"] === "Fetch") {
|
|
1175
1204
|
return true;
|
|
1176
|
-
}
|
|
1177
|
-
|
|
1205
|
+
}
|
|
1178
1206
|
|
|
1207
|
+
// Recursively check all child properties
|
|
1179
1208
|
for (var key of Object.keys(plan)) {
|
|
1180
1209
|
var value = plan[key];
|
|
1181
|
-
|
|
1182
1210
|
if (Array.isArray(value)) {
|
|
1183
1211
|
for (var item of value) {
|
|
1184
1212
|
if (this.hasFetchOperator(item)) {
|
|
@@ -1191,63 +1219,57 @@ module.exports = class Database {
|
|
|
1191
1219
|
}
|
|
1192
1220
|
}
|
|
1193
1221
|
}
|
|
1194
|
-
|
|
1195
1222
|
return false;
|
|
1196
1223
|
}
|
|
1197
|
-
|
|
1198
1224
|
optimize(query, exec_scope) {
|
|
1199
|
-
var
|
|
1200
|
-
|
|
1225
|
+
var _this25 = this;
|
|
1201
1226
|
return _asyncToGenerator(function* () {
|
|
1202
1227
|
optimizeState.inFlight += 1;
|
|
1203
|
-
|
|
1204
1228
|
try {
|
|
1205
|
-
var _explainResponse$rows, _indexResponse$meta
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
indexSatisfiesCoveringDefinition
|
|
1210
|
-
} = require("./utility"); // 1. Run EXPLAIN to get the query plan
|
|
1211
|
-
|
|
1229
|
+
var _explainResponse$rows, _indexResponse$meta;
|
|
1230
|
+
var _require5 = require("./utility"),
|
|
1231
|
+
buildCoveringIndexDefinition = _require5.buildCoveringIndexDefinition,
|
|
1232
|
+
indexSatisfiesCoveringDefinition = _require5.indexSatisfiesCoveringDefinition;
|
|
1212
1233
|
|
|
1234
|
+
// 1. Run EXPLAIN to get the query plan
|
|
1213
1235
|
var explainQuery = "EXPLAIN ".concat(query);
|
|
1214
|
-
var explainResponse = yield exec_scope.query(explainQuery);
|
|
1215
|
-
|
|
1216
|
-
var plan = explainResponse === null || explainResponse === void 0 ? void 0 : (_explainResponse$rows = explainResponse.rows) === null || _explainResponse$rows === void 0 ? void 0 : _explainResponse$rows[0];
|
|
1236
|
+
var explainResponse = yield exec_scope.query(explainQuery);
|
|
1217
1237
|
|
|
1218
|
-
if (
|
|
1238
|
+
// 2. Check if query plan contains Fetch operator (not covered)
|
|
1239
|
+
var plan = explainResponse === null || explainResponse === void 0 || (_explainResponse$rows = explainResponse.rows) === null || _explainResponse$rows === void 0 ? void 0 : _explainResponse$rows[0];
|
|
1240
|
+
if (!_this25.hasFetchOperator(plan)) {
|
|
1219
1241
|
// Query is already covered, no need to create index
|
|
1220
1242
|
return;
|
|
1221
|
-
}
|
|
1222
|
-
|
|
1243
|
+
}
|
|
1223
1244
|
|
|
1245
|
+
// 3. Build covering index from the query
|
|
1224
1246
|
var candidate = buildCoveringIndexDefinition(query, {
|
|
1225
|
-
bucket:
|
|
1226
|
-
scope:
|
|
1227
|
-
collection:
|
|
1247
|
+
bucket: _this25.bucket,
|
|
1248
|
+
scope: _this25.scope_name,
|
|
1249
|
+
collection: _this25.collection_name
|
|
1228
1250
|
});
|
|
1229
|
-
if (!candidate) return;
|
|
1251
|
+
if (!candidate) return;
|
|
1252
|
+
|
|
1253
|
+
// EXPLAIN only describes the chosen plan. An equivalent or wider index
|
|
1230
1254
|
// may already exist under an older name, or still be deferred/building.
|
|
1231
1255
|
// Include both catalog representations of the default collection.
|
|
1232
|
-
|
|
1233
1256
|
var indexResponse = yield exec_scope.query("SELECT RAW i FROM system:indexes AS i\n WHERE i.namespace_id = 'default'\n AND ((i.bucket_id = $bucket AND i.scope_id = $scope AND i.keyspace_id = $collection)\n OR ($scope = '_default' AND $collection = '_default'\n AND i.bucket_id IS MISSING AND i.scope_id IS MISSING AND i.keyspace_id = $bucket))", {
|
|
1234
1257
|
parameters: {
|
|
1235
1258
|
bucket: candidate.bucket,
|
|
1236
1259
|
scope: candidate.scope,
|
|
1237
1260
|
collection: candidate.collection
|
|
1238
1261
|
}
|
|
1239
|
-
});
|
|
1240
|
-
|
|
1241
|
-
if (!Array.isArray(indexResponse === null || indexResponse === void 0 ? void 0 : indexResponse.rows) ||
|
|
1262
|
+
});
|
|
1263
|
+
// A failed or incomplete inventory is not evidence that no index exists.
|
|
1264
|
+
if (!Array.isArray(indexResponse === null || indexResponse === void 0 ? void 0 : indexResponse.rows) || indexResponse !== null && indexResponse !== void 0 && (_indexResponse$meta = indexResponse.meta) !== null && _indexResponse$meta !== void 0 && (_indexResponse$meta = _indexResponse$meta.warnings) !== null && _indexResponse$meta !== void 0 && _indexResponse$meta.length) {
|
|
1242
1265
|
throw new Error("Cannot verify existing indexes before auto-optimization");
|
|
1243
1266
|
}
|
|
1244
|
-
|
|
1245
1267
|
if (indexResponse.rows.some(index => index.name === candidate.name || indexSatisfiesCoveringDefinition(index, candidate))) {
|
|
1246
1268
|
return;
|
|
1247
|
-
}
|
|
1248
|
-
// per-keyspace name uniqueness also prevents a concurrent duplicate.
|
|
1249
|
-
|
|
1269
|
+
}
|
|
1250
1270
|
|
|
1271
|
+
// Identical definitions use the same name across processes. Couchbase's
|
|
1272
|
+
// per-keyspace name uniqueness also prevents a concurrent duplicate.
|
|
1251
1273
|
yield exec_scope.query(candidate.statement);
|
|
1252
1274
|
} catch (e) {
|
|
1253
1275
|
// A failure here (timeout, index already exists, cluster busy) almost
|
|
@@ -1259,44 +1281,90 @@ module.exports = class Database {
|
|
|
1259
1281
|
}
|
|
1260
1282
|
})();
|
|
1261
1283
|
}
|
|
1262
|
-
|
|
1263
1284
|
query(query, query_scope) {
|
|
1264
|
-
var
|
|
1265
|
-
|
|
1266
|
-
var query_options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
|
|
1285
|
+
var _arguments13 = arguments,
|
|
1286
|
+
_this26 = this;
|
|
1267
1287
|
return _asyncToGenerator(function* () {
|
|
1288
|
+
var query_options = _arguments13.length > 2 && _arguments13[2] !== undefined ? _arguments13[2] : {};
|
|
1268
1289
|
if (query_scope && typeof query_scope === "object" && !Array.isArray(query_scope)) {
|
|
1269
1290
|
query_options = query_scope;
|
|
1270
1291
|
query_scope = undefined;
|
|
1271
1292
|
}
|
|
1272
|
-
|
|
1273
|
-
var scope = query_scope || _this20.query_scope || "cluster";
|
|
1293
|
+
var scope = query_scope || _this26.query_scope || "cluster";
|
|
1274
1294
|
var sanitizedQuery = query; //sanitization step to be implemented
|
|
1275
|
-
|
|
1276
1295
|
var exec_scope = null;
|
|
1277
|
-
|
|
1278
1296
|
if (scope === "cluster") {
|
|
1279
|
-
exec_scope =
|
|
1297
|
+
exec_scope = _this26.cluster;
|
|
1280
1298
|
} else {
|
|
1281
|
-
exec_scope =
|
|
1282
|
-
}
|
|
1283
|
-
|
|
1284
|
-
|
|
1299
|
+
exec_scope = _this26.scope;
|
|
1300
|
+
}
|
|
1301
|
+
// Start time
|
|
1285
1302
|
var startTime = process.hrtime.bigint();
|
|
1286
1303
|
var options = buildQueryOptions(query_options);
|
|
1287
|
-
var response
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1304
|
+
var response;
|
|
1305
|
+
try {
|
|
1306
|
+
response = yield _this26.observe(exec_scope.query(sanitizedQuery, options));
|
|
1307
|
+
} catch (error) {
|
|
1308
|
+
// `scan_wait_fallback`: a request_plus read that could not get a fresh
|
|
1309
|
+
// index snapshot within `scan_wait` runs once more without waiting, for
|
|
1310
|
+
// callers that prefer a slightly stale answer to an error.
|
|
1311
|
+
if (!wantsScanWaitFallback(query_options) || options.scanConsistency !== QueryScanConsistency.RequestPlus || options.scanWait === undefined || !isScanWaitExpired(error)) {
|
|
1312
|
+
throw error;
|
|
1313
|
+
}
|
|
1314
|
+
console.warn("[spice-couchbase] index not current within ".concat(options.scanWait, "ms; reading without waiting"));
|
|
1315
|
+
response = yield _this26.observe(exec_scope.query(sanitizedQuery, _objectSpread(_objectSpread({}, _.omit(options, ["scanWait"])), {}, {
|
|
1316
|
+
scanConsistency: QueryScanConsistency.NotBounded
|
|
1317
|
+
})));
|
|
1318
|
+
}
|
|
1319
|
+
// End time
|
|
1320
|
+
var endTime = process.hrtime.bigint();
|
|
1321
|
+
// Calculate duration in microseconds
|
|
1322
|
+
var durationInMilliseconds = (endTime - startTime) / BigInt(1000000);
|
|
1323
|
+
// Optimise slow statements in the background, subject to the guard rails
|
|
1292
1324
|
// declared at the top of this file. Never awaited, never allowed to reject.
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1325
|
+
if (durationInMilliseconds > SLOW_QUERY_MS && _this26.shouldAutoOptimize(sanitizedQuery, query_options)) {
|
|
1326
|
+
if (slowStatementHandler) {
|
|
1327
|
+
if (shouldOptimize(statementShape(sanitizedQuery))) {
|
|
1328
|
+
_this26.reportSlowStatement(sanitizedQuery, exec_scope, Number(durationInMilliseconds)).catch(() => {});
|
|
1329
|
+
}
|
|
1330
|
+
} else if (shouldOptimize(sanitizedQuery)) {
|
|
1331
|
+
_this26.optimize(sanitizedQuery, exec_scope).catch(() => {});
|
|
1332
|
+
}
|
|
1296
1333
|
}
|
|
1297
|
-
|
|
1298
1334
|
return response;
|
|
1299
1335
|
})();
|
|
1300
1336
|
}
|
|
1301
1337
|
|
|
1338
|
+
/**
|
|
1339
|
+
* Hands a slow statement to the application's handler, unless its plan is
|
|
1340
|
+
* already served by a managed index. Runs under the same in-flight and
|
|
1341
|
+
* cooldown guard rails as `optimize()`.
|
|
1342
|
+
*/
|
|
1343
|
+
reportSlowStatement(query, exec_scope, duration_ms) {
|
|
1344
|
+
var _this27 = this;
|
|
1345
|
+
return _asyncToGenerator(function* () {
|
|
1346
|
+
optimizeState.inFlight += 1;
|
|
1347
|
+
try {
|
|
1348
|
+
var _explainResponse$rows2;
|
|
1349
|
+
var explainResponse = yield exec_scope.query("EXPLAIN ".concat(query));
|
|
1350
|
+
var plan = explainResponse === null || explainResponse === void 0 || (_explainResponse$rows2 = explainResponse.rows) === null || _explainResponse$rows2 === void 0 ? void 0 : _explainResponse$rows2[0];
|
|
1351
|
+
if (usesManagedIndex(plan)) return;
|
|
1352
|
+
yield slowStatementHandler({
|
|
1353
|
+
statement: query,
|
|
1354
|
+
shape: statementShape(query),
|
|
1355
|
+
plan,
|
|
1356
|
+
resources: [..._this27.statementResources(query)],
|
|
1357
|
+
connection: _this27.connection_name,
|
|
1358
|
+
bucket: _this27.bucket,
|
|
1359
|
+
scope: _this27.scope_name,
|
|
1360
|
+
collection: _this27.collection_name,
|
|
1361
|
+
duration_ms
|
|
1362
|
+
});
|
|
1363
|
+
} catch (e) {
|
|
1364
|
+
optimizeState.cooldownUntil = Date.now() + OPTIMIZE_FAILURE_COOLDOWN_MS;
|
|
1365
|
+
} finally {
|
|
1366
|
+
optimizeState.inFlight -= 1;
|
|
1367
|
+
}
|
|
1368
|
+
})();
|
|
1369
|
+
}
|
|
1302
1370
|
};
|