import hashObject from 'object-hash'; import EntityRepository from './EntityRepository'; export default class CachableRepository extends EntityRepository{ repositoryName: string; cache: any; /** * Constructor method. * @param {Knex} knex * @param {Cache} cache */ constructor(knex, cache) { super(knex); this.cache = cache; this.repositoryName = this.constructor.name; } /** * Retrieve the cache key of the method name and arguments. * @param {string} method * @param {...any} args * @return {string} */ getCacheKey(method, ...args) { const hashArgs = hashObject({ ...args }); const repositoryName = this.repositoryName; return `${repositoryName}-${method}-${hashArgs}`; } /** * Retrieve all entries with specified relations. * @param withRelations */ all(withRelations?) { const cacheKey = this.getCacheKey('all', withRelations); return this.cache.get(cacheKey, () => { return super.all(withRelations); }); } /** * Finds list of entities with specified attributes * @param {Object} attributeValues - values to filter retrieved entities by * @param {string || string[]} [withRelations] - name of relation(s) to eagerly retrieve. * @returns {Promise} - query builder. You can chain additional methods to it or call "await" or then() on it to execute */ find(attributeValues = {}, withRelations?) { const cacheKey = this.getCacheKey('find', attributeValues, withRelations); return this.cache.get(cacheKey, () => { return super.find(attributeValues, withRelations); }); } /** * Finds list of entities with attribute values that are different from specified ones * @param {Object} attributeValues - values to filter retrieved entities by * @param {string || string[]} [withRelations] - name of relation(s) to eagerly retrieve, as defined in model relationMappings() * @returns {Promise} - query builder. You can chain additional methods to it or call "await" or then() on it to execute */ findWhereNot(attributeValues = {}, withRelations?) { const cacheKey = this.getCacheKey('findWhereNot', attributeValues, withRelations); return this.cache.get(cacheKey, () => { return super.findWhereNot(attributeValues, withRelations); }); } /** * Finds list of entities with specified attributes (any of multiple specified values) * Supports both ('attrName', ['value1', 'value2]) and ({attrName: ['value1', 'value2']} formats) * * @param {string|Object} searchParam - attribute name or search criteria object * @param {*[]} [attributeValues] - attribute values to filter retrieved entities by * @param {string || string[]} [withRelations] - name of relation(s) to eagerly retrieve, as defined in model relationMappings() * @returns {PromiseLike} - query builder. You can chain additional methods to it or call "await" or then() on it to execute */ findWhereIn(searchParam, attributeValues, withRelations?) { const cacheKey = this.getCacheKey('findWhereIn', attributeValues, withRelations); return this.cache.get(cacheKey, () => { return super.findWhereIn(searchParam, attributeValues, withRelations); }); } /** * Finds first entity by given parameters * * @param {Object} attributeValues - values to filter retrieved entities by * @param {string || string[]} [withRelations] - name of relation(s) to eagerly retrieve, as defined in model relationMappings() * @returns {Promise} */ findOne(attributeValues = {}, withRelations?) { const cacheKey = this.getCacheKey('findOne', attributeValues, withRelations); return this.cache.get(cacheKey, () => { return super.findOne(attributeValues, withRelations); }); } /** * Finds first entity by given parameters * * @param {string || number} id - value of id column of the entity * @param {string || string[]} [withRelations] - name of relation(s) to eagerly retrieve, as defined in model relationMappings() * @returns {Promise} */ findOneById(id, withRelations?) { const cacheKey = this.getCacheKey('findOneById', id, withRelations); return this.cache.get(cacheKey, () => { return super.findOneById(id, withRelations); }); } /** * Persists new entity or an array of entities. * This method does not recursively persist related entities, use createRecursively (to be implemented) for that. * Batch insert only works on PostgreSQL * @param {Object} entity - model instance or parameters for a new entity * @returns {Promise} - query builder. You can chain additional methods to it or call "await" or then() on it to execute */ async create(entity) { const result = await super.create(entity); // Flushes the repository cache after insert operation. this.flushCache(); return result; } /** * Persists updated entity. If previously set fields are not present, performs an incremental update (does not remove fields unless explicitly set to null) * * @param {Object} entity - single entity instance * @param {Object} [trx] - knex transaction instance. If not specified, new implicit transaction will be used. * @returns {Promise} number of affected rows */ async update(entity, whereAttributes?) { const result = await super.update(entity, whereAttributes); // Flushes the repository cache after update operation. this.flushCache(); return result; } /** * @param {Object} attributeValues - values to filter deleted entities by * @param {Object} [trx] * @returns {Promise} Query builder. After promise is resolved, returns count of deleted rows */ async deleteBy(attributeValues) { const result = await super.deleteBy(attributeValues); this.flushCache(); return result; } /** * @param {string || number} id - value of id column of the entity * @returns {Promise} Query builder. After promise is resolved, returns count of deleted rows */ deleteById(id: number|string) { const result = super.deleteById(id); // Flushes the repository cache after insert operation. this.flushCache(); return result; } /** * * @param {string|number[]} values - */ async deleteWhereIn(values: string | number[]) { const result = await super.deleteWhereIdIn(values); // Flushes the repository cache after delete operation. this.flushCache(); return result; } /** * * @param graph * @param options */ async upsertGraph(graph, options) { const result = await super.upsertGraph(graph, options); // Flushes the repository cache after insert operation. this.flushCache(); return result; } /** * * @param {} whereAttributes * @param {string} field * @param {number} amount */ async changeNumber(whereAttributes, field: string, amount: number) { const result = await super.changeNumber(whereAttributes, field, amount); // Flushes the repository cache after update operation. this.flushCache(); return result; } /** * Flush repository cache. */ flushCache(): void { this.cache.delStartWith(this.repositoryName); } }