couchbase
Version:
The official Couchbase Node.js Client Library.
554 lines (553 loc) • 20.5 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.QueryIndexManager = exports.CollectionQueryIndexManager = exports.QueryIndex = void 0;
const bindingutilities_1 = require("./bindingutilities");
const errors_1 = require("./errors");
const utilities_1 = require("./utilities");
/**
* Contains a specific index configuration for the query service.
*
* @category Management
*/
class QueryIndex {
/**
* @internal
*/
constructor(data) {
this.name = data.name;
this.isPrimary = data.isPrimary;
this.type = data.type;
this.state = data.state;
this.indexKey = data.indexKey;
this.condition = data.condition;
this.partition = data.partition;
this.collectionName = data.collectionName;
this.scopeName = data.scopeName;
this.bucketName = data.bucketName;
}
}
exports.QueryIndex = QueryIndex;
/**
* @internal
*/
class InternalQueryIndexManager {
/**
* @internal
*/
constructor(cluster) {
this._cluster = cluster;
this._queryContext = {
bucket_name: '',
scope_name: '',
};
}
/**
* @internal
*/
async createIndex(bucketName, isPrimary, options, callback) {
const timeout = options.timeout || this._cluster.managementTimeout;
return utilities_1.PromiseHelper.wrap((wrapCallback) => {
this._cluster.conn.managementQueryIndexCreate({
bucket_name: bucketName,
scope_name: options.scopeName || '',
collection_name: options.collectionName || '',
index_name: options.name || '',
keys: options.keys || [],
query_ctx: options.queryContext || this._queryContext,
is_primary: isPrimary,
ignore_if_exists: options.ignoreIfExists || false,
deferred: options.deferred,
num_replicas: options.numReplicas,
timeout: timeout,
condition: undefined,
}, (cppErr) => {
const err = (0, bindingutilities_1.errorFromCpp)(cppErr);
if (err) {
return wrapCallback(err, null);
}
wrapCallback(err);
});
}, callback);
}
/**
* @internal
*/
async dropIndex(bucketName, isPrimary, options, callback) {
const timeout = options.timeout || this._cluster.managementTimeout;
// BUG(JSCBC-1066): We need to use a normal drop index for named primary indexes.
if (options.name) {
isPrimary = false;
}
return utilities_1.PromiseHelper.wrap((wrapCallback) => {
this._cluster.conn.managementQueryIndexDrop({
bucket_name: bucketName,
scope_name: options.scopeName || '',
collection_name: options.collectionName || '',
index_name: options.name || '',
query_ctx: options.queryContext || this._queryContext,
is_primary: isPrimary,
ignore_if_does_not_exist: options.ignoreIfNotExists || false,
timeout: timeout,
}, (cppErr) => {
const err = (0, bindingutilities_1.errorFromCpp)(cppErr);
if (err) {
return wrapCallback(err, null);
}
wrapCallback(err);
});
}, callback);
}
/**
* @internal
*/
async getAllIndexes(bucketName, options, callback) {
const timeout = options.timeout || this._cluster.managementTimeout;
return utilities_1.PromiseHelper.wrap((wrapCallback) => {
this._cluster.conn.managementQueryIndexGetAll({
bucket_name: bucketName,
scope_name: options.scopeName || '',
collection_name: options.collectionName || '',
query_ctx: options.queryContext || this._queryContext,
timeout: timeout,
}, (cppErr, resp) => {
const err = (0, bindingutilities_1.errorFromCpp)(cppErr);
if (err) {
return wrapCallback(err, null);
}
const indexes = resp.indexes.map((row) => new QueryIndex({
isPrimary: row.is_primary,
name: row.name,
state: row.state,
type: row.type,
indexKey: row.index_key,
partition: row.partition,
condition: row.condition,
bucketName: row.bucket_name,
scopeName: row.scope_name,
collectionName: row.collection_name,
}));
wrapCallback(null, indexes);
});
}, callback);
}
/**
* @internal
*/
async buildDeferredIndexes(bucketName, options, callback) {
const timeout = options.timeout || this._cluster.managementTimeout;
return utilities_1.PromiseHelper.wrap((wrapCallback) => {
this._cluster.conn.managementQueryIndexBuildDeferred({
bucket_name: bucketName,
scope_name: options.scopeName || '',
collection_name: options.collectionName || '',
query_ctx: options.queryContext || this._queryContext,
timeout: timeout,
}, (cppErr) => {
const err = (0, bindingutilities_1.errorFromCpp)(cppErr);
if (err) {
return wrapCallback(err, null);
}
wrapCallback(null, null);
});
}, callback);
}
/**
* @internal
*/
async watchIndexes(bucketName, indexNames, timeout, options, callback) {
if (options.watchPrimary) {
indexNames = [...indexNames, '#primary'];
}
const timer = new utilities_1.CompoundTimeout(timeout);
return utilities_1.PromiseHelper.wrapAsync(async () => {
let curInterval = 50;
for (;;) {
// Get all the indexes that are currently registered
const foundIdxs = await this.getAllIndexes(bucketName, {
timeout: timer.left(),
});
const foundIndexNames = foundIdxs.map((idx) => idx.name);
const onlineIdxs = foundIdxs.filter((idx) => idx.state === 'online');
const onlineIdxNames = onlineIdxs.map((idx) => idx.name);
// Check if all the indexes we want are online
let allOnline = true;
indexNames.forEach((indexName) => {
if (!foundIndexNames.includes(indexName)) {
throw new errors_1.IndexNotFoundError(new Error(`Cannot find index with name ${indexName}`));
}
allOnline = allOnline && onlineIdxNames.indexOf(indexName) !== -1;
});
// If all the indexes are online, we've succeeded
if (allOnline) {
break;
}
// Add 500 to our interval to a max of 1000
curInterval = Math.min(1000, curInterval + 500);
// Make sure we don't go past our user-specified duration
const userTimeLeft = timer.left();
if (userTimeLeft !== undefined) {
curInterval = Math.min(curInterval, userTimeLeft);
}
if (curInterval <= 0) {
throw new errors_1.CouchbaseError('Failed to find all indexes online within the alloted time.');
}
// Wait until curInterval expires
await new Promise((resolve) => setTimeout(() => resolve(true), curInterval));
}
}, callback);
}
}
/**
* CollectionQueryIndexManager provides an interface for managing the
* query indexes on the collection.
*
* @category Management
*/
class CollectionQueryIndexManager {
/**
* @internal
*/
constructor(collection) {
this._bucketName = collection.scope.bucket.name;
this._collectionName = collection.name;
this._scopeName = collection.scope.name;
this._manager = new InternalQueryIndexManager(collection.cluster);
}
/**
* Creates a new query index.
*
* @param indexName The name of the new index.
* @param keys The keys which this index should cover.
* @param options Optional parameters for this operation.
* @param callback A node-style callback to be invoked after execution.
*/
async createIndex(indexName, keys, options, callback) {
if (options instanceof Function) {
callback = arguments[2];
options = undefined;
}
if (!options) {
options = {};
}
return this._manager.createIndex(this._bucketName, false, {
collectionName: this._collectionName,
scopeName: this._scopeName,
name: indexName,
keys: keys,
ignoreIfExists: options.ignoreIfExists,
numReplicas: options.numReplicas,
deferred: options.deferred,
timeout: options.timeout,
}, callback);
}
/**
* Creates a new primary query index.
*
* @param options Optional parameters for this operation.
* @param callback A node-style callback to be invoked after execution.
*/
async createPrimaryIndex(options, callback) {
if (options instanceof Function) {
callback = arguments[0];
options = undefined;
}
if (!options) {
options = {};
}
return this._manager.createIndex(this._bucketName, true, {
collectionName: this._collectionName,
scopeName: this._scopeName,
name: options.name,
ignoreIfExists: options.ignoreIfExists,
deferred: options.deferred,
timeout: options.timeout,
}, callback);
}
/**
* Drops an existing query index.
*
* @param indexName The name of the index to drop.
* @param options Optional parameters for this operation.
* @param callback A node-style callback to be invoked after execution.
*/
async dropIndex(indexName, options, callback) {
if (options instanceof Function) {
callback = arguments[1];
options = undefined;
}
if (!options) {
options = {};
}
return this._manager.dropIndex(this._bucketName, false, {
collectionName: this._collectionName,
scopeName: this._scopeName,
name: indexName,
ignoreIfNotExists: options.ignoreIfNotExists,
timeout: options.timeout,
}, callback);
}
/**
* Drops an existing primary index.
*
* @param options Optional parameters for this operation.
* @param callback A node-style callback to be invoked after execution.
*/
async dropPrimaryIndex(options, callback) {
if (options instanceof Function) {
callback = arguments[0];
options = undefined;
}
if (!options) {
options = {};
}
return this._manager.dropIndex(this._bucketName, true, {
collectionName: this._collectionName,
scopeName: this._scopeName,
name: options.name,
ignoreIfNotExists: options.ignoreIfNotExists,
timeout: options.timeout,
}, callback);
}
/**
* Returns a list of indexes for a specific bucket.
*
* @param options Optional parameters for this operation.
* @param callback A node-style callback to be invoked after execution.
*/
async getAllIndexes(options, callback) {
if (options instanceof Function) {
callback = arguments[0];
options = undefined;
}
if (!options) {
options = {};
}
return this._manager.getAllIndexes(this._bucketName, {
collectionName: this._collectionName,
scopeName: this._scopeName,
timeout: options.timeout,
}, callback);
}
/**
* Starts building any indexes which were previously created with deferred=true.
*
* @param options Optional parameters for this operation.
* @param callback A node-style callback to be invoked after execution.
*/
async buildDeferredIndexes(options, callback) {
if (options instanceof Function) {
callback = arguments[0];
options = undefined;
}
if (!options) {
options = {};
}
return this._manager.buildDeferredIndexes(this._bucketName, {
collectionName: this._collectionName,
scopeName: this._scopeName,
timeout: options.timeout,
}, callback);
}
/**
* Waits for a number of indexes to finish creation and be ready to use.
*
* @param indexNames The names of the indexes to watch.
* @param timeout The maximum time to wait for the index, expressed in milliseconds.
* @param options Optional parameters for this operation.
* @param callback A node-style callback to be invoked after execution.
*/
async watchIndexes(indexNames, timeout, options, callback) {
if (options instanceof Function) {
callback = arguments[2];
options = undefined;
}
if (!options) {
options = {};
}
return this._manager.watchIndexes(this._bucketName, indexNames, timeout, {
collectionName: this._collectionName,
scopeName: this._scopeName,
watchPrimary: options.watchPrimary,
}, callback);
}
}
exports.CollectionQueryIndexManager = CollectionQueryIndexManager;
/**
* QueryIndexManager provides an interface for managing the
* query indexes on the cluster.
*
* @category Management
*/
class QueryIndexManager {
/**
* @internal
*/
constructor(cluster) {
this._manager = new InternalQueryIndexManager(cluster);
}
/**
* Creates a new query index.
*
* @param bucketName The name of the bucket this index is for.
* @param indexName The name of the new index.
* @param keys The keys which this index should cover.
* @param options Optional parameters for this operation.
* @param callback A node-style callback to be invoked after execution.
*/
async createIndex(bucketName, indexName, keys, options, callback) {
if (options instanceof Function) {
callback = arguments[3];
options = undefined;
}
if (!options) {
options = {};
}
return this._manager.createIndex(bucketName, false, {
collectionName: options.collectionName,
scopeName: options.scopeName,
name: indexName,
keys: keys,
ignoreIfExists: options.ignoreIfExists,
numReplicas: options.numReplicas,
deferred: options.deferred,
timeout: options.timeout,
}, callback);
}
/**
* Creates a new primary query index.
*
* @param bucketName The name of the bucket this index is for.
* @param options Optional parameters for this operation.
* @param callback A node-style callback to be invoked after execution.
*/
async createPrimaryIndex(bucketName, options, callback) {
if (options instanceof Function) {
callback = arguments[1];
options = undefined;
}
if (!options) {
options = {};
}
return this._manager.createIndex(bucketName, true, {
collectionName: options.collectionName,
scopeName: options.scopeName,
name: options.name,
ignoreIfExists: options.ignoreIfExists,
deferred: options.deferred,
timeout: options.timeout,
}, callback);
}
/**
* Drops an existing query index.
*
* @param bucketName The name of the bucket containing the index to drop.
* @param indexName The name of the index to drop.
* @param options Optional parameters for this operation.
* @param callback A node-style callback to be invoked after execution.
*/
async dropIndex(bucketName, indexName, options, callback) {
if (options instanceof Function) {
callback = arguments[2];
options = undefined;
}
if (!options) {
options = {};
}
return this._manager.dropIndex(bucketName, false, {
collectionName: options.collectionName,
scopeName: options.scopeName,
name: indexName,
ignoreIfNotExists: options.ignoreIfNotExists,
timeout: options.timeout,
}, callback);
}
/**
* Drops an existing primary index.
*
* @param bucketName The name of the bucket containing the primary index to drop.
* @param options Optional parameters for this operation.
* @param callback A node-style callback to be invoked after execution.
*/
async dropPrimaryIndex(bucketName, options, callback) {
if (options instanceof Function) {
callback = arguments[1];
options = undefined;
}
if (!options) {
options = {};
}
return this._manager.dropIndex(bucketName, true, {
collectionName: options.collectionName,
scopeName: options.scopeName,
name: options.name,
ignoreIfNotExists: options.ignoreIfNotExists,
timeout: options.timeout,
}, callback);
}
/**
* Returns a list of indexes for a specific bucket.
*
* @param bucketName The name of the bucket to fetch indexes for.
* @param options Optional parameters for this operation.
* @param callback A node-style callback to be invoked after execution.
*/
async getAllIndexes(bucketName, options, callback) {
if (options instanceof Function) {
callback = arguments[1];
options = undefined;
}
if (!options) {
options = {};
}
return this._manager.getAllIndexes(bucketName, {
collectionName: options.collectionName,
scopeName: options.scopeName,
timeout: options.timeout,
}, callback);
}
/**
* Starts building any indexes which were previously created with deferred=true.
*
* @param bucketName The name of the bucket to perform the build on.
* @param options Optional parameters for this operation.
* @param callback A node-style callback to be invoked after execution.
*/
async buildDeferredIndexes(bucketName, options, callback) {
if (options instanceof Function) {
callback = arguments[1];
options = undefined;
}
if (!options) {
options = {};
}
return this._manager.buildDeferredIndexes(bucketName, {
collectionName: options.collectionName,
scopeName: options.scopeName,
timeout: options.timeout,
}, callback);
}
/**
* Waits for a number of indexes to finish creation and be ready to use.
*
* @param bucketName The name of the bucket to watch for indexes on.
* @param indexNames The names of the indexes to watch.
* @param timeout The maximum time to wait for the index, expressed in milliseconds.
* @param options Optional parameters for this operation.
* @param callback A node-style callback to be invoked after execution.
*/
async watchIndexes(bucketName, indexNames, timeout, options, callback) {
if (options instanceof Function) {
callback = arguments[3];
options = undefined;
}
if (!options) {
options = {};
}
return this._manager.watchIndexes(bucketName, indexNames, timeout, {
collectionName: options.collectionName,
scopeName: options.scopeName,
watchPrimary: options.watchPrimary,
}, callback);
}
}
exports.QueryIndexManager = QueryIndexManager;