import { ClientSession, MongoClient, TransactionOptions } from 'mongodb' import { Storage, StorageCursor, StorageSession } from '../storage' import { ErrEntityHasNoUniqKeyValue } from '../entity' /** @public */ export class MongoStorage extends Storage { private readonly _client: MongoClient _session: ClientSession constructor(dsn: string) { super(dsn) this._client = new MongoClient(dsn) this._session = null } async init() { await this._connect() } async _connect() { await this._client.connect() } async find(collectionName: string, query: Record): Promise { await this._connect() const coll = await this._client.db().collection(collectionName) return coll.find(query) } async count(collectionName: string, query: Record): Promise { await this._connect() const coll = await this._client.db().collection(collectionName) return coll.countDocuments(query) } async save(collectionName: string, uniqKey: string, data: Record): Promise { await this._connect() const id = data[uniqKey] const coll = await this._client.db().collection(collectionName) if (id !== null && id !== undefined) { const filter = { [uniqKey]: id } const result = await coll.findOneAndReplace(filter, data, { upsert: true }) if (result.lastErrorObject) { if (result.lastErrorObject.updatedExisting) { return result.value._id } return result.lastErrorObject.upserted } throw new TypeError(`can not save data to ${collectionName} with result ${result}`) } else { // Нельзя сохранить сущность без значения уникального ключа // const result = await coll.insertOne(data) // return result.insertedId._id.toHexString() throw new ErrEntityHasNoUniqKeyValue() } } async _getCollection(name: string) { await this._connect() return this._client.db().collection(name) } createSession(): MongoStorageSession { return new MongoStorageSession(this._client) } get client(): MongoClient { return this._client } async remove(collectionName: string, uniqKeyName: string, uniqKey: string): Promise { const coll = await this._client.db().collection(collectionName) const result = await coll.deleteOne({ [uniqKeyName]: uniqKey }) return Promise.resolve(result.acknowledged) } } export class MongoStorageSession extends StorageSession { _client: MongoClient _session: ClientSession constructor(client: MongoClient) { super() this._client = client } async start() { if (this._session) { await this._session.endSession() } this._session = this._client.startSession() } async commit(fn, options?: TransactionOptions) { try { await this._session.withTransaction(fn, options) } catch (error) { throw error } await this._session.endSession() } }