@datastax/astra-db-ts
Version:
Data API TypeScript client
289 lines (288 loc) • 12.7 kB
JavaScript
// Copyright Datastax, Inc
// SPDX-License-Identifier: Apache-2.0
import { insertManyOrdered, insertManyUnordered } from '../../documents/commands/helpers/insertion.js';
import { coalesceUpsertIntoUpdateResult, mkUpdateResult } from '../../documents/commands/helpers/updates.js';
import { mkDistinctPathExtractor, pullSafeProjection4Distinct } from '../../documents/commands/helpers/distinct.js';
import stableStringify from 'safe-stable-stringify';
import { SerDesTarget } from '../../lib/api/ser-des/ctx.js';
export class CommandImpls {
constructor(parent, httpClient, serdes) {
Object.defineProperty(this, "_httpClient", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "_serdes", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "_parent", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
this._httpClient = httpClient;
this._serdes = serdes;
this._parent = parent;
}
async insertOne(_document, options) {
const document = this._serdes.serialize(_document, SerDesTarget.Record);
const command = mkBasicCmd('insertOne', {
document: document[0],
});
const raw = await this._httpClient.executeCommand(command, {
timeoutManager: this._httpClient.tm.single('generalMethodTimeoutMs', options),
bigNumsPresent: document[1],
});
return {
insertedId: this._serdes.deserialize(raw.status.insertedIds[0], raw, SerDesTarget.InsertedId),
};
}
async insertMany(docs, options, err) {
const chunkSize = options?.chunkSize ?? 50;
const timeoutManager = this._httpClient.tm.multipart('generalMethodTimeoutMs', options);
const insertedIds = (options?.ordered)
? await insertManyOrdered(this._httpClient, this._serdes, docs, chunkSize, timeoutManager, err)
: await insertManyUnordered(this._httpClient, this._serdes, docs, options?.concurrency ?? 8, chunkSize, timeoutManager, err);
return {
insertedCount: insertedIds.length,
insertedIds: insertedIds,
};
}
async updateOne(_filter, _update, options) {
const filter = this._serdes.serialize(_filter, SerDesTarget.Filter);
const update = this._serdes.serialize(_update, SerDesTarget.Update);
const command = mkCmdWithSortProj('updateOne', options, {
filter: filter[0],
update: update[0],
options: {
upsert: options?.upsert,
},
});
const resp = await this._httpClient.executeCommand(command, {
timeoutManager: this._httpClient.tm.single('generalMethodTimeoutMs', options),
bigNumsPresent: filter[1] || update[1],
});
return coalesceUpsertIntoUpdateResult(this._serdes, mkUpdateResult(resp), resp);
}
async updateMany(_filter, _update, options, mkError) {
const filter = this._serdes.serialize(_filter, SerDesTarget.Filter);
const update = this._serdes.serialize(_update, SerDesTarget.Update);
const command = mkBasicCmd('updateMany', {
filter: filter[0],
update: update[0],
options: {
pageState: undefined,
upsert: options?.upsert,
},
});
const timeoutManager = this._httpClient.tm.multipart('generalMethodTimeoutMs', options);
const commonResult = mkUpdateResult();
let resp;
try {
while (!resp || resp.status?.nextPageState) {
resp = await this._httpClient.executeCommand(command, {
bigNumsPresent: filter[1] || update[1],
timeoutManager,
});
command.updateMany.options.pageState = resp.status?.nextPageState;
commonResult.modifiedCount += resp.status?.modifiedCount;
commonResult.matchedCount += resp.status?.matchedCount;
}
}
catch (e) {
throw mkError(e, coalesceUpsertIntoUpdateResult(this._serdes, commonResult, {}));
}
return coalesceUpsertIntoUpdateResult(this._serdes, commonResult, resp);
}
async replaceOne(_filter, _replacement, options) {
const filter = this._serdes.serialize(_filter, SerDesTarget.Filter);
const replacement = this._serdes.serialize(_replacement, SerDesTarget.Record);
const command = mkCmdWithSortProj('findOneAndReplace', options, {
filter: filter[0],
replacement: replacement[0],
options: {
returnDocument: 'before',
upsert: options?.upsert,
},
projection: { '*': 0 },
});
const resp = await this._httpClient.executeCommand(command, {
timeoutManager: this._httpClient.tm.single('generalMethodTimeoutMs', options),
bigNumsPresent: filter[1] || replacement[1],
});
return coalesceUpsertIntoUpdateResult(this._serdes, mkUpdateResult(resp), resp);
}
async deleteOne(_filter, options) {
const filter = this._serdes.serialize(_filter, SerDesTarget.Filter);
const command = mkCmdWithSortProj('deleteOne', options, {
filter: filter[0],
});
const deleteOneResp = await this._httpClient.executeCommand(command, {
timeoutManager: this._httpClient.tm.single('generalMethodTimeoutMs', options),
bigNumsPresent: filter[1],
});
return {
deletedCount: deleteOneResp.status?.deletedCount,
};
}
async deleteMany(_filter, options, mkError) {
const filter = this._serdes.serialize(_filter, SerDesTarget.Filter);
const command = mkBasicCmd('deleteMany', {
filter: filter[0],
});
const isDeleteAll = Object.keys(_filter).length === 0;
const timeoutManager = this._httpClient.tm.multipart('generalMethodTimeoutMs', options);
let resp, numDeleted = 0;
try {
while (!resp || resp.status?.moreData) {
resp = await this._httpClient.executeCommand(command, {
timeoutManager,
bigNumsPresent: filter[1],
extraLogInfo: isDeleteAll ? { deleteAll: true } : undefined,
});
numDeleted += resp.status?.deletedCount ?? 0;
}
}
catch (e) {
throw mkError(e, { deletedCount: numDeleted });
}
return {
deletedCount: numDeleted,
};
}
find(filter, options, cursor) {
return new cursor(this._parent, this._serdes, this._serdes.serialize(structuredClone(filter), SerDesTarget.Filter), structuredClone(options));
}
findAndRerank(filter, options, cursor) {
return new cursor(this._parent, this._serdes, this._serdes.serialize(structuredClone(filter), SerDesTarget.Filter), structuredClone(options));
}
async findOne(_filter, options) {
const filter = this._serdes.serialize(_filter, SerDesTarget.Filter);
const command = mkCmdWithSortProj('findOne', options, {
filter: filter[0],
options: {
includeSimilarity: options?.includeSimilarity,
},
});
const resp = await this._httpClient.executeCommand(command, {
timeoutManager: this._httpClient.tm.single('generalMethodTimeoutMs', options),
bigNumsPresent: filter[1],
});
return this._serdes.deserialize(resp.data?.document, resp, SerDesTarget.Record);
}
async findOneAndReplace(_filter, _replacement, options) {
const filter = this._serdes.serialize(_filter, SerDesTarget.Filter);
const replacement = this._serdes.serialize(_replacement, SerDesTarget.Record);
const command = mkCmdWithSortProj('findOneAndReplace', options, {
filter: filter[0],
replacement: replacement[0],
options: {
returnDocument: options?.returnDocument,
upsert: options?.upsert,
},
});
const resp = await this._httpClient.executeCommand(command, {
timeoutManager: this._httpClient.tm.single('generalMethodTimeoutMs', options),
bigNumsPresent: filter[1] || replacement[1],
});
return this._serdes.deserialize(resp.data.document, resp, SerDesTarget.Record);
}
async findOneAndDelete(_filter, options) {
const filter = this._serdes.serialize(_filter, SerDesTarget.Filter);
const command = mkCmdWithSortProj('findOneAndDelete', options, {
filter: filter[0],
});
const resp = await this._httpClient.executeCommand(command, {
timeoutManager: this._httpClient.tm.single('generalMethodTimeoutMs', options),
bigNumsPresent: filter[1],
});
return this._serdes.deserialize(resp.data.document, resp, SerDesTarget.Record);
}
async findOneAndUpdate(_filter, _update, options) {
const filter = this._serdes.serialize(_filter, SerDesTarget.Filter);
const update = this._serdes.serialize(_update, SerDesTarget.Update);
const command = mkCmdWithSortProj('findOneAndUpdate', options, {
filter: filter[0],
update: update[0],
options: {
returnDocument: options?.returnDocument,
upsert: options?.upsert,
},
});
const resp = await this._httpClient.executeCommand(command, {
timeoutManager: this._httpClient.tm.single('generalMethodTimeoutMs', options),
bigNumsPresent: filter[1] || update[1],
});
return this._serdes.deserialize(resp.data.document, resp, SerDesTarget.Record);
}
async distinct(key, filter, options, mkCursor) {
const projection = pullSafeProjection4Distinct(key);
const cursor = this.find(filter, { projection: { _id: 0, [projection]: 1 }, timeout: options?.timeout }, mkCursor);
const seen = new Set();
const ret = [];
const extract = mkDistinctPathExtractor(key);
for await (const doc of cursor) {
const values = extract(doc);
for (let i = 0, n = values.length; i < n; i++) {
const value = values[i];
const key = (typeof value === 'object')
? stableStringify(value)
: value;
if (!seen.has(key)) {
ret.push(value);
seen.add(key);
}
}
}
return ret;
}
async countDocuments(_filter, upperBound, options, error) {
if (!upperBound) {
throw new Error('upperBound is required');
}
if (upperBound < 0) {
throw new Error('upperBound must be >= 0');
}
const [filter, bigNumsPresent] = this._serdes.serialize(_filter, SerDesTarget.Filter);
const command = mkBasicCmd('countDocuments', {
filter: filter,
});
const resp = await this._httpClient.executeCommand(command, {
timeoutManager: this._httpClient.tm.single('generalMethodTimeoutMs', options),
extraLogInfo: { upperBound },
bigNumsPresent,
});
if (resp.status?.moreData) {
throw new error(resp.status.count, true);
}
if (resp.status?.count > upperBound) {
throw new error(upperBound, false);
}
return resp.status?.count;
}
async estimatedDocumentCount(options) {
const command = mkBasicCmd('estimatedDocumentCount', {});
const resp = await this._httpClient.executeCommand(command, {
timeoutManager: this._httpClient.tm.single('generalMethodTimeoutMs', options),
});
return resp.status?.count;
}
}
const mkBasicCmd = (name, body) => ({ [name]: body });
const mkCmdWithSortProj = (name, options, body) => {
const command = mkBasicCmd(name, body);
if (options) {
if (options.sort && Object.keys(options.sort).length > 0) {
body.sort = options.sort;
}
if (options.projection && Object.keys(options.projection).length > 0) {
body.projection = options.projection;
}
}
return command;
};