couchbase
Version:
The official Couchbase Node.js Client Library.
1,287 lines • 56.6 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Collection = void 0;
const binarycollection_1 = require("./binarycollection");
const binding_1 = __importStar(require("./binding"));
const bindingutilities_1 = require("./bindingutilities");
const crudoptypes_1 = require("./crudoptypes");
const datastructures_1 = require("./datastructures");
const errors_1 = require("./errors");
const generaltypes_1 = require("./generaltypes");
const queryindexmanager_1 = require("./queryindexmanager");
const rangeScan_1 = require("./rangeScan");
const sdspecs_1 = require("./sdspecs");
const sdutils_1 = require("./sdutils");
const streamablepromises_1 = require("./streamablepromises");
const utilities_1 = require("./utilities");
/**
* Exposes the operations which are available to be performed against a collection.
* Namely the ability to perform KV operations.
*
* @category Core
*/
class Collection {
/**
* @internal
*/
static get DEFAULT_NAME() {
return '_default';
}
/**
@internal
*/
constructor(scope, collectionName) {
this._scope = scope;
this._name = collectionName;
this._conn = scope.conn;
this._kvScanTimeout = 75000;
this._scanBatchByteLimit = 15000;
this._scanBatchItemLimit = 50;
}
/**
@internal
*/
get conn() {
return this._conn;
}
/**
@internal
*/
get cluster() {
return this._scope.bucket.cluster;
}
/**
@internal
*/
get scope() {
return this._scope;
}
/**
@internal
*/
get transcoder() {
return this._scope.transcoder;
}
/**
@internal
*/
_mutationTimeout(durabilityLevel) {
if (durabilityLevel !== undefined &&
durabilityLevel !== null &&
durabilityLevel !== generaltypes_1.DurabilityLevel.None) {
return this.cluster.kvDurableTimeout;
}
return this.cluster.kvTimeout;
}
/**
* @internal
*/
_cppDocId(key) {
return {
bucket: this.scope.bucket.name,
scope: this.scope.name || '_default',
collection: this.name || '_default',
key: key,
};
}
/**
* @internal
*/
_encodeDoc(transcoder, value, callback) {
try {
const [bytesBuf, flagsOut] = transcoder.encode(value);
callback(null, bytesBuf, flagsOut);
}
catch (e) {
return callback(e, Buffer.alloc(0), 0);
}
}
/**
* @internal
*/
_decodeDoc(transcoder, bytes, flags, callback) {
try {
const content = transcoder.decode(bytes, flags);
callback(null, content);
}
catch (e) {
return callback(e, null);
}
}
/**
* @internal
*/
_subdocEncode(value) {
return Buffer.from(value);
}
/**
* @internal
*/
_subdocDecode(bytes) {
try {
return JSON.parse(bytes.toString('utf8'));
}
catch (e) {
// If we encounter a parse error, assume that we need
// to return bytes instead of an object.
return bytes;
}
}
/**
* The name of the collection this Collection object references.
*/
get name() {
return this._name;
}
/**
* Retrieves the value of a document from the collection.
*
* @param key The document key to retrieve.
* @param options Optional parameters for this operation.
* @param callback A node-style callback to be invoked after execution.
*/
get(key, options, callback) {
if (options instanceof Function) {
callback = arguments[1];
options = undefined;
}
if (!options) {
options = {};
}
if (options.project || options.withExpiry) {
return this._projectedGet(key, options, callback);
}
const transcoder = options.transcoder || this.transcoder;
const timeout = options.timeout || this.cluster.kvTimeout;
return utilities_1.PromiseHelper.wrap((wrapCallback) => {
this._conn.get({
id: this._cppDocId(key),
timeout,
partition: 0,
opaque: 0,
}, (cppErr, resp) => {
const err = (0, bindingutilities_1.errorFromCpp)(cppErr);
if (err) {
return wrapCallback(err, null);
}
this._decodeDoc(transcoder, resp.value, resp.flags, (err, content) => {
if (err) {
return wrapCallback(err, null);
}
wrapCallback(null, new crudoptypes_1.GetResult({
content: content,
cas: resp.cas,
}));
});
});
}, callback);
}
_projectedGet(key, options, callback) {
let expiryStart = -1;
let projStart = -1;
let paths = [];
let spec = [];
let needReproject = false;
if (options.withExpiry) {
expiryStart = spec.length;
spec.push(sdspecs_1.LookupInSpec.get(sdspecs_1.LookupInMacro.Expiry));
}
projStart = spec.length;
if (!options.project) {
paths = [''];
spec.push(sdspecs_1.LookupInSpec.get(''));
}
else {
let projects = options.project;
if (!Array.isArray(projects)) {
projects = [projects];
}
for (let i = 0; i < projects.length; ++i) {
paths.push(projects[i]);
spec.push(sdspecs_1.LookupInSpec.get(projects[i]));
}
}
// The following code relies on the projections being
// the last segment of the specs array, this way we handle
// an overburdened operation in a single area.
if (spec.length > 16) {
spec = spec.splice(0, projStart);
spec.push(sdspecs_1.LookupInSpec.get(''));
needReproject = true;
}
return utilities_1.PromiseHelper.wrapAsync(async () => {
const res = await this.lookupIn(key, spec, {
...options,
});
let content = null;
let expiry = undefined;
if (expiryStart >= 0) {
const expiryRes = res.content[expiryStart];
expiry = expiryRes.value;
}
if (projStart >= 0) {
if (!needReproject) {
for (let i = 0; i < paths.length; ++i) {
const projPath = paths[i];
const projRes = res.content[projStart + i];
if (!projRes.error) {
content = sdutils_1.SdUtils.insertByPath(content, projPath, projRes.value);
}
}
}
else {
content = {};
const reprojRes = res.content[projStart];
for (let j = 0; j < paths.length; ++j) {
const reprojPath = paths[j];
const value = sdutils_1.SdUtils.getByPath(reprojRes.value, reprojPath);
content = sdutils_1.SdUtils.insertByPath(content, reprojPath, value);
}
}
}
return new crudoptypes_1.GetResult({
content: content,
cas: res.cas,
expiryTime: expiry,
});
}, callback);
}
/**
* Checks whether a specific document exists or not.
*
* @param key The document key to check for existence.
* @param options Optional parameters for this operation.
* @param callback A node-style callback to be invoked after execution.
*/
exists(key, options, callback) {
if (options instanceof Function) {
callback = arguments[1];
options = undefined;
}
if (!options) {
options = {};
}
const timeout = options.timeout || this.cluster.kvTimeout;
return utilities_1.PromiseHelper.wrap((wrapCallback) => {
this._conn.exists({
id: this._cppDocId(key),
partition: 0,
opaque: 0,
timeout,
}, (cppErr, resp) => {
const err = (0, bindingutilities_1.errorFromCpp)(cppErr);
if (err) {
return wrapCallback(err, null);
}
if (resp.deleted) {
return wrapCallback(null, new crudoptypes_1.ExistsResult({
cas: undefined,
exists: false,
}));
}
wrapCallback(null, new crudoptypes_1.ExistsResult({
cas: resp.cas,
exists: resp.document_exists,
}));
});
}, callback);
}
/**
* @internal
*/
_getReplica(key, getAllReplicas, options, callback) {
if (options instanceof Function) {
callback = arguments[2];
options = undefined;
}
if (!options) {
options = {};
}
const emitter = new streamablepromises_1.StreamableReplicasPromise((replicas) => replicas);
const transcoder = options.transcoder || this.transcoder;
const timeout = options.timeout || this.cluster.kvTimeout;
if (getAllReplicas) {
this._conn.getAllReplicas({
id: this._cppDocId(key),
timeout: timeout,
}, (cppErr, resp) => {
const err = (0, bindingutilities_1.errorFromCpp)(cppErr);
if (err) {
emitter.emit('error', err);
emitter.emit('end');
return;
}
resp.entries.forEach((replica) => {
this._decodeDoc(transcoder, replica.value, replica.flags, (err, content) => {
if (err) {
emitter.emit('error', err);
emitter.emit('end');
return;
}
emitter.emit('replica', new crudoptypes_1.GetReplicaResult({
content: content,
cas: replica.cas,
isReplica: replica.replica,
}));
});
});
emitter.emit('end');
return;
});
}
else {
this._conn.getAnyReplica({
id: this._cppDocId(key),
timeout: timeout,
}, (cppErr, resp) => {
const err = (0, bindingutilities_1.errorFromCpp)(cppErr);
if (err) {
emitter.emit('error', err);
emitter.emit('end');
return;
}
this._decodeDoc(transcoder, resp.value, resp.flags, (err, content) => {
if (err) {
emitter.emit('error', err);
emitter.emit('end');
return;
}
emitter.emit('replica', new crudoptypes_1.GetReplicaResult({
content: content,
cas: resp.cas,
isReplica: resp.replica,
}));
});
emitter.emit('end');
return;
});
}
return utilities_1.PromiseHelper.wrapAsync(() => emitter, callback);
}
/**
* Retrieves the value of the document from any of the available replicas. This
* will return as soon as the first response is received from any replica node.
*
* @param key The document key to retrieve.
* @param options Optional parameters for this operation.
* @param callback A node-style callback to be invoked after execution.
*/
getAnyReplica(key, options, callback) {
if (options instanceof Function) {
callback = arguments[1];
options = undefined;
}
return utilities_1.PromiseHelper.wrapAsync(async () => {
const replicas = await this._getReplica(key, false, options);
return replicas[0];
}, callback);
}
/**
* Retrieves the value of the document from all available replicas. Note that
* as replication is asynchronous, each node may return a different value.
*
* @param key The document key to retrieve.
* @param options Optional parameters for this operation.
* @param callback A node-style callback to be invoked after execution.
*/
getAllReplicas(key, options, callback) {
return this._getReplica(key, true, options, callback);
}
/**
* Inserts a new document to the collection, failing if the document already exists.
*
* @param key The document key to insert.
* @param value The value of the document to insert.
* @param options Optional parameters for this operation.
* @param callback A node-style callback to be invoked after execution.
*/
insert(key, value, options, callback) {
if (options instanceof Function) {
callback = arguments[2];
options = undefined;
}
if (!options) {
options = {};
}
const expiry = options.expiry ? (0, utilities_1.expiryToTimestamp)(options.expiry) : 0;
const transcoder = options.transcoder || this.transcoder;
const durabilityLevel = options.durabilityLevel;
const persistTo = options.durabilityPersistTo;
const replicateTo = options.durabilityReplicateTo;
const timeout = options.timeout || this._mutationTimeout(durabilityLevel);
return utilities_1.PromiseHelper.wrap((wrapCallback) => {
this._encodeDoc(transcoder, value, (err, bytes, flags) => {
if (err) {
return wrapCallback(err, null);
}
const insertReq = {
id: this._cppDocId(key),
value: bytes,
flags,
expiry: expiry,
timeout,
partition: 0,
opaque: 0,
};
const insertCallback = (cppErr, resp) => {
const err = (0, bindingutilities_1.errorFromCpp)(cppErr);
if (err) {
return wrapCallback(err, null);
}
wrapCallback(err, new crudoptypes_1.MutationResult({
cas: resp.cas,
token: resp.token,
}));
};
if (persistTo || replicateTo) {
this._conn.insertWithLegacyDurability({
...insertReq,
persist_to: (0, bindingutilities_1.persistToToCpp)(persistTo),
replicate_to: (0, bindingutilities_1.replicateToToCpp)(replicateTo),
}, insertCallback);
}
else {
this._conn.insert({
...insertReq,
durability_level: (0, bindingutilities_1.durabilityToCpp)(durabilityLevel),
}, insertCallback);
}
});
}, callback);
}
/**
* Upserts a document to the collection. This operation succeeds whether or not the
* document already exists.
*
* @param key The document key to upsert.
* @param value The new value for the document.
* @param options Optional parameters for this operation.
* @param callback A node-style callback to be invoked after execution.
*/
upsert(key, value, options, callback) {
if (options instanceof Function) {
callback = arguments[2];
options = undefined;
}
if (!options) {
options = {};
}
const expiry = options.expiry ? (0, utilities_1.expiryToTimestamp)(options.expiry) : 0;
const preserve_expiry = options.preserveExpiry;
const transcoder = options.transcoder || this.transcoder;
const durabilityLevel = options.durabilityLevel;
const persistTo = options.durabilityPersistTo;
const replicateTo = options.durabilityReplicateTo;
const timeout = options.timeout || this._mutationTimeout(durabilityLevel);
return utilities_1.PromiseHelper.wrap((wrapCallback) => {
this._encodeDoc(transcoder, value, (err, bytes, flags) => {
if (err) {
return wrapCallback(err, null);
}
const upsertReq = {
id: this._cppDocId(key),
value: bytes,
flags,
expiry: expiry,
preserve_expiry: preserve_expiry || false,
timeout,
partition: 0,
opaque: 0,
};
const upsertCallback = (cppErr, resp) => {
const err = (0, bindingutilities_1.errorFromCpp)(cppErr);
if (err) {
return wrapCallback(err, null);
}
wrapCallback(err, new crudoptypes_1.MutationResult({
cas: resp.cas,
token: resp.token,
}));
};
if (persistTo || replicateTo) {
this._conn.upsertWithLegacyDurability({
...upsertReq,
persist_to: (0, bindingutilities_1.persistToToCpp)(persistTo),
replicate_to: (0, bindingutilities_1.replicateToToCpp)(replicateTo),
}, upsertCallback);
}
else {
this._conn.upsert({
...upsertReq,
durability_level: (0, bindingutilities_1.durabilityToCpp)(durabilityLevel),
}, upsertCallback);
}
});
}, callback);
}
/**
* Replaces the value of an existing document. Failing if the document does not exist.
*
* @param key The document key to replace.
* @param value The new value for the document.
* @param options Optional parameters for this operation.
* @param callback A node-style callback to be invoked after execution.
*/
replace(key, value, options, callback) {
if (options instanceof Function) {
callback = arguments[2];
options = undefined;
}
if (!options) {
options = {};
}
const expiry = options.expiry ? (0, utilities_1.expiryToTimestamp)(options.expiry) : 0;
const cas = options.cas;
const preserve_expiry = options.preserveExpiry;
const transcoder = options.transcoder || this.transcoder;
const durabilityLevel = options.durabilityLevel;
const persistTo = options.durabilityPersistTo;
const replicateTo = options.durabilityReplicateTo;
const timeout = options.timeout || this._mutationTimeout(durabilityLevel);
return utilities_1.PromiseHelper.wrap((wrapCallback) => {
this._encodeDoc(transcoder, value, (err, bytes, flags) => {
if (err) {
return wrapCallback(err, null);
}
const replaceReq = {
id: this._cppDocId(key),
value: bytes,
flags,
expiry,
cas: cas || binding_1.zeroCas,
preserve_expiry: preserve_expiry || false,
timeout,
partition: 0,
opaque: 0,
};
const replaceCallback = (cppErr, resp) => {
const err = (0, bindingutilities_1.errorFromCpp)(cppErr);
if (err) {
return wrapCallback(err, null);
}
wrapCallback(err, new crudoptypes_1.MutationResult({
cas: resp.cas,
token: resp.token,
}));
};
if (persistTo || replicateTo) {
this._conn.replaceWithLegacyDurability({
...replaceReq,
persist_to: (0, bindingutilities_1.persistToToCpp)(persistTo),
replicate_to: (0, bindingutilities_1.replicateToToCpp)(replicateTo),
}, replaceCallback);
}
else {
this._conn.replace({
...replaceReq,
durability_level: (0, bindingutilities_1.durabilityToCpp)(durabilityLevel),
}, replaceCallback);
}
});
}, callback);
}
/**
* Remove an existing document from the collection.
*
* @param key The document key to remove.
* @param options Optional parameters for this operation.
* @param callback A node-style callback to be invoked after execution.
*/
remove(key, options, callback) {
if (options instanceof Function) {
callback = arguments[1];
options = undefined;
}
if (!options) {
options = {};
}
const cas = options.cas;
const durabilityLevel = options.durabilityLevel;
const persistTo = options.durabilityPersistTo;
const replicateTo = options.durabilityReplicateTo;
const timeout = options.timeout || this._mutationTimeout(durabilityLevel);
return utilities_1.PromiseHelper.wrap((wrapCallback) => {
const removeReq = {
id: this._cppDocId(key),
cas: cas || binding_1.zeroCas,
timeout,
partition: 0,
opaque: 0,
};
const removeCallback = (cppErr, resp) => {
const err = (0, bindingutilities_1.errorFromCpp)(cppErr);
if (err) {
return wrapCallback(err, null);
}
wrapCallback(err, new crudoptypes_1.MutationResult({
cas: resp.cas,
token: resp.token,
}));
};
if (persistTo || replicateTo) {
this._conn.removeWithLegacyDurability({
...removeReq,
persist_to: (0, bindingutilities_1.persistToToCpp)(persistTo),
replicate_to: (0, bindingutilities_1.replicateToToCpp)(replicateTo),
}, removeCallback);
}
else {
this._conn.remove({
...removeReq,
durability_level: (0, bindingutilities_1.durabilityToCpp)(durabilityLevel),
}, removeCallback);
}
}, callback);
}
/**
* Retrieves the value of the document and simultanously updates the expiry time
* for the same document.
*
* @param key The document to fetch and touch.
* @param expiry The new expiry to apply to the document, specified in seconds.
* @param options Optional parameters for this operation.
* @param callback A node-style callback to be invoked after execution.
*/
getAndTouch(key, expiry, options, callback) {
if (options instanceof Function) {
callback = arguments[2];
options = undefined;
}
if (!options) {
options = {};
}
const transcoder = options.transcoder || this.transcoder;
const timeout = options.timeout || this.cluster.kvTimeout;
return utilities_1.PromiseHelper.wrap((wrapCallback) => {
this._conn.getAndTouch({
id: this._cppDocId(key),
expiry: (0, utilities_1.expiryToTimestamp)(expiry),
timeout,
partition: 0,
opaque: 0,
}, (cppErr, resp) => {
const err = (0, bindingutilities_1.errorFromCpp)(cppErr);
if (err) {
return wrapCallback(err, null);
}
this._decodeDoc(transcoder, resp.value, resp.flags, (err, content) => {
if (err) {
return wrapCallback(err, null);
}
wrapCallback(err, new crudoptypes_1.GetResult({
content: content,
cas: resp.cas,
}));
});
});
}, callback);
}
/**
* Updates the expiry on an existing document.
*
* @param key The document key to touch.
* @param expiry The new expiry to set for the document, specified in seconds.
* @param options Optional parameters for this operation.
* @param callback A node-style callback to be invoked after execution.
*/
touch(key, expiry, options, callback) {
if (options instanceof Function) {
callback = arguments[2];
options = undefined;
}
if (!options) {
options = {};
}
const timeout = options.timeout || this.cluster.kvTimeout;
return utilities_1.PromiseHelper.wrap((wrapCallback) => {
this._conn.touch({
id: this._cppDocId(key),
expiry: (0, utilities_1.expiryToTimestamp)(expiry),
timeout,
partition: 0,
opaque: 0,
}, (cppErr, resp) => {
const err = (0, bindingutilities_1.errorFromCpp)(cppErr);
if (err) {
return wrapCallback(err, null);
}
wrapCallback(err, new crudoptypes_1.MutationResult({
cas: resp.cas,
}));
});
}, callback);
}
/**
* Locks a document and retrieves the value of that document at the time it is locked.
*
* @param key The document key to retrieve and lock.
* @param lockTime The amount of time to lock the document for, specified in seconds.
* @param options Optional parameters for this operation.
* @param callback A node-style callback to be invoked after execution.
*/
getAndLock(key, lockTime, options, callback) {
if (options instanceof Function) {
callback = arguments[2];
options = undefined;
}
if (!options) {
options = {};
}
const transcoder = options.transcoder || this.transcoder;
const timeout = options.timeout || this.cluster.kvTimeout;
return utilities_1.PromiseHelper.wrap((wrapCallback) => {
this._conn.getAndLock({
id: this._cppDocId(key),
lock_time: lockTime,
timeout,
partition: 0,
opaque: 0,
}, (cppErr, resp) => {
const err = (0, bindingutilities_1.errorFromCpp)(cppErr);
if (err) {
return wrapCallback(err, null);
}
this._decodeDoc(transcoder, resp.value, resp.flags, (err, content) => {
if (err) {
return wrapCallback(err, null);
}
wrapCallback(err, new crudoptypes_1.GetResult({
cas: resp.cas,
content: content,
}));
});
});
}, callback);
}
/**
* Unlocks a previously locked document.
*
* @param key The document key to unlock.
* @param cas The CAS of the document, used to validate lock ownership.
* @param options Optional parameters for this operation.
* @param callback A node-style callback to be invoked after execution.
*/
unlock(key, cas, options, callback) {
if (options instanceof Function) {
callback = arguments[2];
options = undefined;
}
if (!options) {
options = {};
}
const timeout = options.timeout || this.cluster.kvTimeout;
return utilities_1.PromiseHelper.wrap((wrapCallback) => {
this._conn.unlock({
id: this._cppDocId(key),
cas,
timeout,
partition: 0,
opaque: 0,
}, (cppErr) => {
const err = (0, bindingutilities_1.errorFromCpp)(cppErr);
if (err) {
return wrapCallback(err);
}
wrapCallback(null);
});
}, callback);
}
/**
* @internal
*/
_continueScan(iterator, transcoder, emitter) {
iterator.next((cppErr, resp) => {
const err = (0, bindingutilities_1.errorFromCpp)(cppErr);
if (err) {
emitter.emit('error', err);
emitter.emit('end');
return;
}
if (typeof resp === 'undefined') {
emitter.emit('end');
return;
}
const key = resp.key;
if (typeof resp.body !== 'undefined') {
const cas = resp.body.cas;
const expiry = resp.body.expiry;
this._decodeDoc(transcoder, resp.body.value, resp.body.flags, (err, content) => {
if (err) {
emitter.emit('error', err);
emitter.emit('end');
return;
}
emitter.emit('result', new crudoptypes_1.ScanResult({
id: key,
content: content,
cas: cas,
expiryTime: expiry,
}));
});
}
else {
emitter.emit('result', new crudoptypes_1.ScanResult({
id: key,
}));
}
if (emitter.cancelRequested && !iterator.cancelled) {
iterator.cancel();
}
this._continueScan(iterator, transcoder, emitter);
return;
});
}
/**
* @internal
*/
_doScan(scanType, options, transcoder, callback) {
const bucketName = this._scope.bucket.name;
const scopeName = this._scope.name;
const collectionName = this._name;
return utilities_1.PromiseHelper.wrapAsync(() => {
const { cppErr, result } = this._conn.scan(bucketName, scopeName, collectionName, scanType.getScanType(), (0, bindingutilities_1.scanTypeToCpp)(scanType), options);
const err = (0, bindingutilities_1.errorFromCpp)(cppErr);
if (err) {
throw err;
}
const emitter = new streamablepromises_1.StreamableScanPromise((results) => results);
this._continueScan(result, transcoder, emitter);
return emitter;
}, callback);
}
/**
* Performs a key-value scan operation.
*
* Use this API for low concurrency batch queries where latency is not a critical as the system
* may have to scan a lot of documents to find the matching documents.
* For low latency range queries, it is recommended that you use SQL++ with the necessary indexes.
*
* @param scanType The type of scan to execute.
* @param options Optional parameters for the scan operation.
* @param callback A node-style callback to be invoked after execution.
*/
scan(scanType, options, callback) {
if (options instanceof Function) {
callback = arguments[2];
options = undefined;
}
if (!options) {
options = {};
}
const transcoder = options.transcoder || this.transcoder;
const timeout = options.timeout || this._kvScanTimeout;
const idsOnly = options.idsOnly || false;
const batchByteLimit = options.batchByteLimit || this._scanBatchByteLimit;
const batchItemLimit = options.batchByteLimit || this._scanBatchItemLimit;
if (typeof options.concurrency !== 'undefined' && options.concurrency < 1) {
throw new errors_1.InvalidArgumentError(new Error('Concurrency option must be positive'));
}
const concurrency = options.concurrency || 1;
if (scanType instanceof rangeScan_1.SamplingScan && scanType.limit < 1) {
throw new errors_1.InvalidArgumentError(new Error('Sampling scan limit must be positive'));
}
const orchestratorOptions = {
ids_only: idsOnly,
consistent_with: (0, bindingutilities_1.mutationStateToCpp)(options.consistentWith),
batch_item_limit: batchItemLimit,
batch_byte_limit: batchByteLimit,
concurrency: concurrency,
timeout: timeout,
};
return this._doScan(scanType, orchestratorOptions, transcoder, callback);
}
/**
* Performs a lookup-in operation against a document, fetching individual fields or
* information about specific fields inside the document value.
*
* @param key The document key to look in.
* @param specs A list of specs describing the data to fetch from the document.
* @param options Optional parameters for this operation.
* @param callback A node-style callback to be invoked after execution.
*/
lookupIn(key, specs, options, callback) {
if (options instanceof Function) {
callback = arguments[2];
options = undefined;
}
if (!options) {
options = {};
}
const cppSpecs = [];
for (let i = 0; i < specs.length; ++i) {
cppSpecs.push({
opcode_: specs[i]._op,
flags_: specs[i]._flags,
path_: specs[i]._path,
original_index_: i,
});
}
const timeout = options.timeout || this.cluster.kvTimeout;
const accessDeleted = options.accessDeleted || false;
return utilities_1.PromiseHelper.wrap((wrapCallback) => {
this._conn.lookupIn({
id: this._cppDocId(key),
specs: cppSpecs,
timeout,
partition: 0,
opaque: 0,
access_deleted: accessDeleted,
}, (cppErr, resp) => {
const err = (0, bindingutilities_1.errorFromCpp)(cppErr);
if (resp && resp.fields) {
const content = [];
for (let i = 0; i < resp.fields.length; ++i) {
const itemRes = resp.fields[i];
const error = (0, bindingutilities_1.errorFromCpp)(itemRes.ec);
let value = undefined;
if (itemRes.value && itemRes.value.length > 0) {
value = this._subdocDecode(itemRes.value);
}
if (itemRes.opcode === binding_1.default.protocol_subdoc_opcode.exists) {
value = itemRes.exists;
}
content.push(new crudoptypes_1.LookupInResultEntry({
error,
value,
}));
}
wrapCallback(err, new crudoptypes_1.LookupInResult({
content: content,
cas: resp.cas,
}));
return;
}
wrapCallback(err, null);
});
}, callback);
}
/**
* @internal
*/
_lookupInReplica(key, lookupInAllReplicas, specs, options, callback) {
if (options instanceof Function) {
callback = arguments[3];
options = undefined;
}
if (!options) {
options = {};
}
const emitter = new streamablepromises_1.StreamableReplicasPromise((replicas) => replicas);
const cppSpecs = [];
for (let i = 0; i < specs.length; ++i) {
cppSpecs.push({
opcode_: specs[i]._op,
flags_: specs[i]._flags,
path_: specs[i]._path,
original_index_: i,
});
}
const timeout = options.timeout || this.cluster.kvTimeout;
if (lookupInAllReplicas) {
this._conn.lookupInAllReplicas({
id: this._cppDocId(key),
specs: cppSpecs,
timeout: timeout,
}, (cppErr, resp) => {
const err = (0, bindingutilities_1.errorFromCpp)(cppErr);
if (err) {
emitter.emit('error', err);
emitter.emit('end');
return;
}
resp.entries.forEach((replica) => {
const content = [];
for (let i = 0; i < replica.fields.length; ++i) {
const itemRes = replica.fields[i];
const error = (0, bindingutilities_1.errorFromCpp)(itemRes.ec);
let value = undefined;
if (itemRes.value && itemRes.value.length > 0) {
value = this._subdocDecode(itemRes.value);
}
if (itemRes.opcode === binding_1.default.protocol_subdoc_opcode.exists) {
value = itemRes.exists;
}
content.push(new crudoptypes_1.LookupInResultEntry({
error,
value,
}));
}
emitter.emit('replica', new crudoptypes_1.LookupInReplicaResult({
content: content,
cas: replica.cas,
isReplica: replica.is_replica,
}));
});
emitter.emit('end');
return;
});
}
else {
this._conn.lookupInAnyReplica({
id: this._cppDocId(key),
specs: cppSpecs,
timeout: timeout,
}, (cppErr, resp) => {
const err = (0, bindingutilities_1.errorFromCpp)(cppErr);
if (err) {
emitter.emit('error', err);
emitter.emit('end');
return;
}
const content = [];
for (let i = 0; i < resp.fields.length; ++i) {
const itemRes = resp.fields[i];
const error = (0, bindingutilities_1.errorFromCpp)(itemRes.ec);
let value = undefined;
if (itemRes.value && itemRes.value.length > 0) {
value = this._subdocDecode(itemRes.value);
}
if (itemRes.opcode === binding_1.default.protocol_subdoc_opcode.exists) {
value = itemRes.exists;
}
content.push(new crudoptypes_1.LookupInResultEntry({
error,
value,
}));
}
emitter.emit('replica', new crudoptypes_1.LookupInReplicaResult({
content: content,
cas: resp.cas,
isReplica: resp.is_replica,
}));
emitter.emit('end');
return;
});
}
return utilities_1.PromiseHelper.wrapAsync(() => emitter, callback);
}
/**
* Performs a lookup-in operation against a document, fetching individual fields or
* information about specific fields inside the document value from any of the available
* replicas in the cluster.
*
* @param key The document key to look in.
* @param specs A list of specs describing the data to fetch from the document.
* @param options Optional parameters for this operation.
* @param callback A node-style callback to be invoked after execution.
*/
lookupInAnyReplica(key, specs, options, callback) {
if (options instanceof Function) {
callback = arguments[2];
options = undefined;
}
return utilities_1.PromiseHelper.wrapAsync(async () => {
const replicas = await this._lookupInReplica(key, false, specs, options);
return replicas[0];
}, callback);
}
/**
* Performs a lookup-in operation against a document, fetching individual fields or
* information about specific fields inside the document value from all available replicas.
* Note that as replication is asynchronous, each node may return a different value.
*
* @param key The document key to look in.
* @param specs A list of specs describing the data to fetch from the document.
* @param options Optional parameters for this operation.
* @param callback A node-style callback to be invoked after execution.
*/
lookupInAllReplicas(key, specs, options, callback) {
return this._lookupInReplica(key, true, specs, options, callback);
}
/**
* Performs a mutate-in operation against a document. Allowing atomic modification of
* specific fields within a document. Also enables access to document extended-attributes.
*
* @param key The document key to mutate.
* @param specs A list of specs describing the operations to perform on the document.
* @param options Optional parameters for this operation.
* @param callback A node-style callback to be invoked after execution.
*/
mutateIn(key, specs, options, callback) {
if (options instanceof Function) {
callback = arguments[2];
options = undefined;
}
if (!options) {
options = {};
}
const cppSpecs = [];
for (let i = 0; i < specs.length; ++i) {
cppSpecs.push({
opcode_: specs[i]._op,
flags_: specs[i]._flags,
path_: specs[i]._path,
value_: specs[i]._data
? this._subdocEncode(specs[i]._data)
: specs[i]._data,
original_index_: 0,
});
}
const storeSemantics = options.upsertDocument
? generaltypes_1.StoreSemantics.Upsert
: options.storeSemantics;
const expiry = options.expiry
? (0, utilities_1.expiryToTimestamp)(options.expiry)
: undefined;
const preserveExpiry = options.preserveExpiry;
const cas = options.cas;
const durabilityLevel = options.durabilityLevel;
const persistTo = options.durabilityPersistTo;
const replicateTo = options.durabilityReplicateTo;
const timeout = options.timeout || this._mutationTimeout(durabilityLevel);
return utilities_1.PromiseHelper.wrap((wrapCallback) => {
const mutateInReq = {
id: this._cppDocId(key),
store_semantics: (0, bindingutilities_1.storeSemanticToCpp)(storeSemantics),
specs: cppSpecs,
expiry,
preserve_expiry: preserveExpiry || false,
cas: cas || binding_1.zeroCas,
timeout,
partition: 0,
opaque: 0,
access_deleted: false,
create_as_deleted: false,
};
const mutateInCallback = (cppErr, resp) => {
const err = (0, bindingutilities_1.errorFromCpp)(cppErr);
if (resp && resp.fields) {
const content = [];
for (let i = 0; i < resp.fields.length; ++i) {
const itemRes = resp.fields[i];
let value = undefined;
if (itemRes.value && itemRes.value.length > 0) {
value = this._subdocDecode(itemRes.value);
}
content.push(new crudoptypes_1.MutateInResultEntry({
value,
}));
}
wrapCallback(err, new crudoptypes_1.MutateInResult({
content: content,
cas: resp.cas,
token: resp.token,
}));
return;
}
wrapCallback(err, null);
};
if (persistTo || replicateTo) {
this._conn.mutateInWithLegacyDurability({
...mutateInReq,
persist_to: (0, bindingutilities_1.persistToToCpp)(persistTo),
replicate_to: (0, bindingutilities_1.replicateToToCpp)(replicateTo),
}, mutateInCallback);
}
else {
this._conn.mutateIn({
...mutateInReq,
durability_level: (0, bindingutilities_1.durabilityToCpp)(durabilityLevel),
}, mutateInCallback);
}
}, callback);
}
/**
* Returns a CouchbaseList permitting simple list storage in a document.
*
* @param key The document key the data-structure resides in.
*/
list(key) {
return new datastructures_1.CouchbaseList(this, key);
}
/**
* Returns a CouchbaseQueue permitting simple queue storage in a document.
*
* @param key The document key the data-structure resides in.
*/
queue(key) {
return new datastructures_1.CouchbaseQueue(this, key);
}
/**
* Returns a CouchbaseMap permitting simple map storage in a document.
*
* @param key The document key the data-structure resides in.
*/
map(key) {
return new datastructures_1.CouchbaseMap(this, key);
}
/**
* Returns a CouchbaseSet permitting simple set storage in a document.
*
* @param key The document key the data-structure resides in.
*/
set(key) {
return new datastructures_1.CouchbaseSet(this, key);
}
/**
* Returns a BinaryCollection object reference, allowing access to various
* binary operations possible against a collection.
*/
binary() {
return new binarycollection_1.BinaryCollection(this);
}
/**
* @internal
*/
_binaryIncrement(key, delta, options, callback) {
if (options instanceof Function) {
callback = arguments[2];
options = undefined;
}
if (!options) {
options = {};
}
const initial_value = options.initial;
const expiry = options.expiry ? (0, utilities_1.expiryToTimestamp)(options.expiry) : 0;
const durabilityLevel = options.durabilityLevel;
const persistTo = options.durabilityPersistTo;
const replicateTo = options.durabilityReplicateTo;
const timeout = options.timeout || this.cluster.kvTimeout;
return utilities_1.PromiseHelper.wrap((wrapCallback) => {
const incrementReq = {
id: this._cppDocId(key),
delta,
initial_value,
expiry: expiry,
timeout,
partition: 0,
opaque: 0,
};
const incrementCallback = (cppErr, resp) => {
const err = (0, bindingutilities_1.errorFromCpp)(cppErr);
if (err) {
return wrapCallback(err, null);
}
wrapCallback(err, new crudoptypes_1.CounterResult({
cas: resp.cas,
token: resp.token,
value: resp.content,
}));
};
if (persistTo || replicateTo) {
this._conn.incrementWithLegacyDurability({
...incrementReq,
persist_to: (0, bindingutilitie