UNPKG

@datastax/astra-db-ts

Version:
289 lines (288 loc) 12.7 kB
// 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; };