UNPKG

nest-arango

Version:

ArangoDB driver module for NestJS with a built-in CLI tool for creating and running migration scripts

764 lines 30.5 kB
"use strict"; var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.ArangoRepository = void 0; const common_1 = require("@nestjs/common"); const arangojs_1 = require("arangojs"); const error_1 = require("arangojs/error"); const __1 = require(".."); const repository_types_1 = require("../interfaces/repository.types"); const event_metadata_storage_1 = require("../metadata/storages/event-metadata.storage"); const type_metadata_storage_1 = require("../metadata/storages/type-metadata.storage"); const listener_type_1 = require("../metadata/types/listener.type"); let ArangoRepository = class ArangoRepository { arangoManager; targetMetadata; collection; eventListeners; get collectionName() { return this.targetMetadata.collection; } constructor(arangoManager, target) { this.arangoManager = arangoManager; this.targetMetadata = type_metadata_storage_1.TypeMetadataStorage.getMetadata(target.constructor.name); this.eventListeners = event_metadata_storage_1.EventListenerMetadataStorage.getMetadata(target.constructor.name); this.collection = this.arangoManager.database.collection(this.collectionName); } getIdFor(_key) { return `${this.collectionName}/${_key}`; } getKeyFrom(_id) { return _id.split('/')[1]; } async documentExists(key, documentExistsOptions = {}) { if (documentExistsOptions.transaction) { return await documentExistsOptions.transaction.step(() => this.collection.documentExists(key)); } else { return await this.collection.documentExists(key, documentExistsOptions); } } async documentsExist(documents, documentsExistOptions = {}) { const keys = documents.map((selector) => { const curSelector = selector; if (curSelector['_id']) return curSelector._id.split('/')[1]; if (curSelector['_key']) return curSelector._key; const split = curSelector.split('/'); if (split.length > 1) return split[1]; return curSelector; }); const aqlQuery = ` FOR key IN @keys RETURN !IS_NULL(DOCUMENT(CONCAT('${this.collection.name}', '/', key))) `; const bindVars = { keys: keys }; if (documentsExistOptions.transaction) { const cursor = await documentsExistOptions.transaction.step(() => this.arangoManager.query(aqlQuery, bindVars)); return await documentsExistOptions.transaction.step(() => cursor.all()); } else { const cursor = await this.arangoManager.query(aqlQuery, bindVars); return await cursor.all(); } } async getDocumentCountBy(bindVars, findManyByOptions = {}) { const filter = Object.entries(bindVars) ?.map(([k]) => `FILTER d.${k} == @${k}`) .join(' '); const aqlQuery = `WITH ${this.collection.name} FOR d IN ${this.collection.name} ${filter} COLLECT WITH COUNT INTO length RETURN length`; if (findManyByOptions.transaction) { const cursor = await findManyByOptions.transaction.step(() => this.arangoManager.query(aqlQuery, bindVars)); return ((await findManyByOptions.transaction.step(() => cursor.next())) ?? 0); } else { const cursor = await this.arangoManager.query(aqlQuery, bindVars); return (await cursor.next()) ?? 0; } } async findOne(key, findOneOptions = {}) { if (findOneOptions.transaction) { return await findOneOptions.transaction.step(() => this.collection.document(key, { graceful: true })); } else { return await this.collection.document(key, { graceful: true }); } } async findOneBy(bindVars, findOneByOptions = {}) { const filter = Object.entries(bindVars) ?.map(([k]) => `FILTER d.${k} == @${k}`) .join(' '); const aqlQuery = `WITH ${this.collection.name} FOR d IN ${this.collection.name} ${filter} RETURN d`; if (findOneByOptions.transaction) { const cursor = await findOneByOptions.transaction.step(() => this.arangoManager.query(aqlQuery, bindVars)); return await findOneByOptions.transaction.step(() => cursor.next()); } else { const cursor = await this.arangoManager.query(aqlQuery, bindVars); return await cursor.next(); } } async findMany(keys, findManyOptions = {}) { if (findManyOptions.transaction) { const docs = await findManyOptions.transaction.step(() => this.collection.documents(keys)); return this.findManyInternal(docs); } else { return this.findManyInternal(await this.collection.documents(keys)); } } findManyInternal(docs) { return docs.filter((doc) => { if (doc.error) { return doc.errorNum !== 1202; } else { return true; } }); } async findManyBy(bindVars, findManyByOptions = {}) { const filter = Object.entries(bindVars) ?.map(([k]) => `FILTER d.${k} == @${k}`) .join(' '); let limit = ''; if (findManyByOptions.page !== undefined && findManyByOptions.pageSize !== undefined) { limit = `LIMIT ${findManyByOptions.page * findManyByOptions.pageSize}, ${findManyByOptions.pageSize}`; } const sort = Object.entries(findManyByOptions.sort ?? {}) ?.map(([k, v]) => `SORT d.${k} ${v}`) .join(' '); const aqlQuery = `WITH ${this.collection.name} FOR d IN ${this.collection.name} ${filter} ${limit} ${sort} RETURN d`; if (findManyByOptions.transaction) { const cursor = await findManyByOptions.transaction.step(() => this.arangoManager.query(aqlQuery, bindVars, { fullCount: true, })); const results = await findManyByOptions.transaction.step(() => cursor.all()); return { totalCount: cursor.extra.stats?.fullCount ?? results.length, results: results, }; } else { const cursor = await this.arangoManager.query(aqlQuery, bindVars, { fullCount: true }); const results = await cursor.all(); return { totalCount: cursor.extra.stats?.fullCount ?? results.length, results: results, }; } } async findAll(findAllOptions = {}) { let limit = ''; if (findAllOptions.page !== undefined && findAllOptions.pageSize !== undefined) { limit = `LIMIT ${findAllOptions.page * findAllOptions.pageSize}, ${findAllOptions.pageSize}`; } const sort = Object.entries(findAllOptions.sort ?? {}) ?.map(([k, v]) => `SORT d.${k} ${v}`) .join(' '); const aqlQuery = `WITH ${this.collection.name} FOR d IN ${this.collection.name} ${limit} ${sort} RETURN d`; if (findAllOptions.transaction) { const cursor = await findAllOptions.transaction.step(() => this.arangoManager.query(aqlQuery, {}, { fullCount: true, })); const results = await findAllOptions.transaction.step(() => cursor.all()); return { totalCount: cursor.extra.stats?.fullCount ?? results.length, results: results, }; } else { const cursor = await this.arangoManager.query(aqlQuery, {}, { fullCount: true }); const results = await cursor.all(); return { totalCount: cursor.extra.stats?.fullCount ?? results.length, results: results, }; } } async save(document, saveOptions = {}) { saveOptions = { emitEvents: true, ...saveOptions }; let context; if (saveOptions?.emitEvents) { context = { database: this.arangoManager.database, transaction: saveOptions?.transaction, info: { current: 0, }, data: saveOptions?.data, repository: this, }; await this.eventListeners ?.get(listener_type_1.EventListenerType.BEFORE_SAVE) ?.call(document, context); } let newEntity; if (saveOptions.transaction) { newEntity = (await saveOptions.transaction.step(() => this.collection.save(document, { returnNew: true, }))).new; } else { newEntity = (await this.collection.save(document, { returnNew: true, })).new; } if (saveOptions?.emitEvents) { context.new = newEntity; await this.eventListeners ?.get(listener_type_1.EventListenerType.AFTER_SAVE) ?.call(document, context); } return newEntity; } async saveAll(documents, saveAllOptions = {}) { saveAllOptions = { emitEvents: true, ...saveAllOptions }; let context; if (saveAllOptions?.emitEvents) { context = { database: this.arangoManager.database, transaction: saveAllOptions?.transaction, info: { current: 0, }, data: saveAllOptions?.data, repository: this, }; if (this.eventListeners?.get(listener_type_1.EventListenerType.BEFORE_SAVE)) { await Promise.all(documents.map(async (document, index) => { context.info.current = index; await this.eventListeners ?.get(listener_type_1.EventListenerType.BEFORE_SAVE) ?.call(document, context); })); } } let result; if (saveAllOptions.transaction) { result = await saveAllOptions.transaction.step(() => { return this.collection.saveAll(documents, { returnNew: true, }); }); } else { result = await this.collection.saveAll(documents, { returnNew: true, }); } return Promise.all(result.map(async (document, index) => { if (saveAllOptions?.emitEvents) { context.info.current = index; context.new = document.new; await this.eventListeners ?.get(listener_type_1.EventListenerType.AFTER_SAVE) ?.call(document, context); } return document.new; })); } async update(document, updateOptions = {}) { updateOptions = { returnOld: true, emitEvents: true, ...updateOptions, }; const { transaction, emitEvents, data, ...options } = updateOptions; let context; if (emitEvents) { context = { database: this.arangoManager.database, transaction: transaction, info: { current: 0, }, data: data, repository: this, }; await this.eventListeners ?.get(listener_type_1.EventListenerType.BEFORE_UPDATE) ?.call(document, context); } let result; if (transaction) { result = await transaction.step(() => this.collection.update(document, document, { returnNew: true, ...updateOptions, })); } else { result = await this.collection.update(document, document, { returnNew: true, ...updateOptions, }); } if (updateOptions?.emitEvents) { context.new = result.new; context.old = result.old; await this.eventListeners ?.get(listener_type_1.EventListenerType.AFTER_UPDATE) ?.call(document, context); } return new repository_types_1.ArangoNewOldResult(result?.new, result?.old); } async updateWithAql(document, updateOptions = {}) { updateOptions = { returnOld: true, emitEvents: true, ...updateOptions, }; const { transaction, emitEvents, data, ...options } = updateOptions; let context; if (emitEvents) { context = { database: this.arangoManager.database, transaction: transaction, info: { current: 0, }, data: data, repository: this, }; await this.eventListeners ?.get(listener_type_1.EventListenerType.BEFORE_UPDATE) ?.call(document, context); } let result; const _aql = (0, __1.aqlConcat)((0, __1.aqlPart) `WITH ${this.collection} `, `UPDATE ${JSON.stringify({ _key: document._key, _id: document._id, })} WITH `, (0, __1.documentAQLBuilder)(document), (0, __1.aqlPart) ` IN ${this.collection} `, `OPTIONS ${JSON.stringify(options)} RETURN { 'new': NEW, 'old': OLD }`); const aqlQuery = (0, arangojs_1.aql)(_aql.templateStrings, ..._aql.args); let cursor; if (transaction) { cursor = await transaction.step(() => this.arangoManager.query(aqlQuery)); result = await transaction.step(() => cursor.next()); } else { cursor = await this.arangoManager.query(aqlQuery); result = await cursor.next(); } if (!result) { throw new error_1.ArangoError({ code: 404, error: true, errorMessage: 'document not found', errorNum: 1202, }); } if (updateOptions?.emitEvents) { context.new = result.new; context.old = result.old; await this.eventListeners ?.get(listener_type_1.EventListenerType.AFTER_UPDATE) ?.call(document, context); } return new repository_types_1.ArangoNewOldResult(result?.new, result?.old); } async updateAll(documents, updateAllOptions = {}) { updateAllOptions = { returnOld: true, emitEvents: true, ...updateAllOptions, }; let context; if (updateAllOptions?.emitEvents) { context = { database: this.arangoManager.database, transaction: updateAllOptions?.transaction, info: { current: 0, }, data: updateAllOptions?.data, repository: this, }; if (this.eventListeners?.get(listener_type_1.EventListenerType.BEFORE_UPDATE)) { await Promise.all(documents.map(async (document, index) => { context.info.current = index; await this.eventListeners ?.get(listener_type_1.EventListenerType.BEFORE_UPDATE) ?.call(document, context); })); } } let results; if (updateAllOptions.transaction) { results = await updateAllOptions.transaction.step(() => this.collection.updateAll(documents, { returnNew: true, ...updateAllOptions, })); } else { results = await this.collection.updateAll(documents, { returnNew: true, ...updateAllOptions, }); } return await Promise.all(results.map(async (document, index) => { if (updateAllOptions?.emitEvents) { context.new = document.new; context.old = document.old; context.info.current = index; await this.eventListeners ?.get(listener_type_1.EventListenerType.AFTER_UPDATE) ?.call(document, context); } return new repository_types_1.ArangoNewOldResult(document.new, document.old); })); } async replace(selector, document, replaceOptions = {}) { replaceOptions = { returnOld: true, emitEvents: true, ...replaceOptions }; let context; if (replaceOptions?.emitEvents) { context = { database: this.arangoManager.database, transaction: replaceOptions?.transaction, info: { current: 0, }, data: replaceOptions?.data, repository: this, }; await this.eventListeners ?.get(listener_type_1.EventListenerType.BEFORE_REPLACE) ?.call(document, context); } let result; if (replaceOptions.transaction) { result = await replaceOptions.transaction.step(() => this.collection.replace(selector, document, { returnNew: true, ...replaceOptions, })); } else { result = await this.collection.replace(selector, document, { returnNew: true, ...replaceOptions, }); } if (replaceOptions?.emitEvents) { context.new = result.new; context.old = result.old; await this.eventListeners ?.get(listener_type_1.EventListenerType.AFTER_REPLACE) ?.call(document, context); } return new repository_types_1.ArangoNewOldResult(result.new, result.old); } async replaceAll(documents, replaceAllOptions = {}) { replaceAllOptions = { returnOld: true, emitEvents: true, ...replaceAllOptions, }; let context; if (replaceAllOptions?.emitEvents) { context = { database: this.arangoManager.database, transaction: replaceAllOptions?.transaction, info: { current: 0, }, data: replaceAllOptions?.data, repository: this, }; if (this.eventListeners?.get(listener_type_1.EventListenerType.BEFORE_REPLACE)) { await Promise.all(documents.map(async (document, index) => { context.info.current = index; await this.eventListeners ?.get(listener_type_1.EventListenerType.BEFORE_REPLACE) ?.call(document, context); })); } } let results; if (replaceAllOptions.transaction) { results = await replaceAllOptions.transaction.step(() => this.collection.replaceAll(documents, { returnNew: true, ...replaceAllOptions, })); } else { results = await this.collection.replaceAll(documents, { returnNew: true, ...replaceAllOptions, }); } return await Promise.all(results.map(async (document, index) => { if (replaceAllOptions?.emitEvents) { context.new = document.new; context.old = document.old; context.info.current = index; await this.eventListeners ?.get(listener_type_1.EventListenerType.AFTER_REPLACE) ?.call(document, context); } return new repository_types_1.ArangoNewOldResult(document.new, document.old); })); } async upsert(upsert, insert, update, upsertOptions = {}) { upsertOptions = { emitEvents: true, ...upsertOptions, }; const { transaction, emitEvents, data, ...options } = upsertOptions; let context; if (emitEvents) { context = { database: this.arangoManager.database, transaction: transaction, info: { current: 0, }, data: data, repository: this, }; await this.eventListeners ?.get(listener_type_1.EventListenerType.BEFORE_SAVE) ?.call(insert, context); await this.eventListeners ?.get(listener_type_1.EventListenerType.BEFORE_UPDATE) ?.call(update, context); } let result; let cursor; const _aql = (0, __1.aqlConcat)((0, __1.aqlPart) `WITH ${this.collection} `, (0, __1.aqlPart) `UPSERT ${upsert} `, (0, __1.aqlPart) `INSERT ${insert} `, (0, __1.aqlPart) `UPDATE ${update} `, (0, __1.aqlPart) `IN ${this.collection} `, `OPTIONS ${JSON.stringify(options)} `, `RETURN { 'new': NEW, 'old': OLD }`); const aqlQuery = (0, arangojs_1.aql)(_aql.templateStrings, ..._aql.args); if (transaction) { cursor = await transaction.step(() => this.arangoManager.query(aqlQuery)); result = await transaction.step(() => cursor.next()); } else { cursor = await this.arangoManager.query(aqlQuery); result = await cursor.next(); } if (!result) { throw new error_1.ArangoError({ code: 404, error: true, errorMessage: 'document not found', errorNum: 1202, }); } if (emitEvents) { context.new = result.new; context.old = result.old; if (result.old) { await this.eventListeners ?.get(listener_type_1.EventListenerType.AFTER_UPDATE) ?.call(update, context); } else { await this.eventListeners ?.get(listener_type_1.EventListenerType.AFTER_SAVE) ?.call(insert, context); } } return new repository_types_1.ArangoNewOldResult(result.new, result.old); } async upsertWithAql(upsert, insert, update, upsertOptions = {}) { upsertOptions = { emitEvents: true, ...upsertOptions, }; const { transaction, emitEvents, data, ...options } = upsertOptions; let context; if (emitEvents) { context = { database: this.arangoManager.database, transaction: transaction, info: { current: 0, }, data: data, repository: this, }; await this.eventListeners ?.get(listener_type_1.EventListenerType.BEFORE_SAVE) ?.call(insert, context); await this.eventListeners ?.get(listener_type_1.EventListenerType.BEFORE_UPDATE) ?.call(update, context); } let result; let cursor; const _aql = (0, __1.aqlConcat)((0, __1.aqlPart) `WITH ${this.collection} `, (0, __1.aqlPart) `UPSERT ${upsert} `, (0, __1.aqlPart) `INSERT ${insert} `, `UPDATE `, (0, __1.documentAQLBuilder)(update), (0, __1.aqlPart) `IN ${this.collection} `, `OPTIONS ${JSON.stringify(options)} `, `RETURN { 'new': NEW, 'old': OLD }`); const aqlQuery = (0, arangojs_1.aql)(_aql.templateStrings, ..._aql.args); if (transaction) { cursor = await transaction.step(() => this.arangoManager.query(aqlQuery)); result = await transaction.step(() => cursor.next()); } else { cursor = await this.arangoManager.query(aqlQuery); result = await cursor.next(); } if (!result) { throw new error_1.ArangoError({ code: 404, error: true, errorMessage: 'document not found', errorNum: 1202, }); } if (emitEvents) { context.new = result.new; context.old = result.old; if (result.old) { await this.eventListeners ?.get(listener_type_1.EventListenerType.AFTER_UPDATE) ?.call(update, context); } else { await this.eventListeners ?.get(listener_type_1.EventListenerType.AFTER_SAVE) ?.call(insert, context); } } return new repository_types_1.ArangoNewOldResult(result.new, result.old); } async remove(key, removeOptions = {}) { removeOptions = { emitEvents: true, ...removeOptions, }; let context; if (removeOptions?.emitEvents) { context = { database: this.arangoManager.database, transaction: removeOptions?.transaction, info: { current: 0, }, data: removeOptions?.data, repository: this, }; } let result; if (removeOptions.transaction) { result = (await removeOptions.transaction.step(() => this.collection.remove(key, { returnOld: true }))).old; } else { result = (await this.collection.remove(key, { returnOld: true })).old; } if (removeOptions?.emitEvents) { context.old = result; await this.eventListeners ?.get(listener_type_1.EventListenerType.AFTER_REMOVE) ?.call(result, context); } return result; } /** * Removes documents containing matching fields with the provided bind variables. * * @remarks * This method does NOT emit the event that invokes `@BeforeRemove` decorated methods. */ async removeBy(bindVars, removeByOptions = {}) { removeByOptions = { emitEvents: true, ...removeByOptions, }; const entries = Object.entries(bindVars); if (entries?.length < 1) { throw new Error('No bindVars were specified!'); } let context; if (removeByOptions?.emitEvents) { context = { database: this.arangoManager.database, transaction: removeByOptions?.transaction, info: { current: 0, }, data: removeByOptions?.data, repository: this, }; } let results; const filter = entries ?.map(([k]) => `FILTER d.${k} == @${k}`) .join(' '); const aqlQuery = `WITH ${this.collection.name} FOR d IN ${this.collection.name} ${filter} REMOVE d IN ${this.collection.name} RETURN OLD`; if (removeByOptions.transaction) { const cursor = await removeByOptions.transaction.step(() => this.arangoManager.query(aqlQuery, bindVars)); results = await removeByOptions.transaction.step(() => cursor.all()); } else { const cursor = await this.arangoManager.query(aqlQuery, bindVars); results = await cursor.all(); } return await Promise.all(results.map(async (document, index) => { if (removeByOptions?.emitEvents) { context.old = document; context.info.current = index; await this.eventListeners ?.get(listener_type_1.EventListenerType.AFTER_REMOVE) ?.call(document, context); } return document; })); } async removeAll(keys, removeAllOptions = {}) { removeAllOptions = { emitEvents: true, ...removeAllOptions, }; let context; if (removeAllOptions?.emitEvents) { context = { database: this.arangoManager.database, transaction: removeAllOptions?.transaction, info: { current: 0, }, data: removeAllOptions?.data, repository: this, }; } let results; if (removeAllOptions.transaction) { results = (await removeAllOptions.transaction.step(() => this.collection.removeAll(keys, { returnOld: true }))).map((item) => { return item.old; }); } else { results = (await this.collection.removeAll(keys, { returnOld: true })).map((item) => { return item.old; }); } return results.map((document, index) => { if (removeAllOptions?.emitEvents) { context.old = document; context.info.current = index; this.eventListeners ?.get(listener_type_1.EventListenerType.AFTER_REMOVE) ?.call(document, context); } return document; }); } async truncate(truncateOptions = {}) { if (truncateOptions.transaction) { await truncateOptions.transaction.step(() => this.collection.truncate()); } else { await this.collection.truncate(); } } }; exports.ArangoRepository = ArangoRepository; exports.ArangoRepository = ArangoRepository = __decorate([ (0, common_1.Injectable)() ], ArangoRepository); //# sourceMappingURL=arango.repository.js.map