UNPKG

@cyber-rom/nestjs-scylladb

Version:

Based on https://www.npmjs.com/package/@ouato/nestjs-express-cassandra With support columns name mapping

263 lines (224 loc) 9.11 kB
import {map} from 'rxjs/operators'; import {Type} from '@nestjs/common'; import {types} from 'cassandra-driver'; import {Observable, defer, Subject} from 'rxjs'; // noinspection ES6PreferShortImport import { BaseModel, FindQuery, SaveOptionsStatic, UpdateOptionsStatic, DeleteOptionsStatic, FindQueryOptionsStatic, } from '../interfaces/externals/scylla.interface'; import {transformKey, transformQuery, transformQuerySelect} from '../utils/transform-query.utils'; import {transformFrom, transformTo} from '../utils/transform-entity.utils'; import {EntityNotFoundError} from '../errors'; import {ReturnQueryBuilder} from './builder/return-query.builder'; const defaultOptions = { findOptions: {raw: true}, updateOptions: {if_exists: true}, deleteOptions: {if_exists: true}, }; export class Repository<Entity = any> { readonly model: BaseModel<Entity>; readonly target: Type<Entity>; readonly returnQueryBuilder: ReturnQueryBuilder<Entity>; create(entity?: Partial<Entity>): Entity; create(entities: Partial<Entity>[]): Entity[]; create(entityLike?: any): Entity | Entity[] { return transformFrom(this.target, entityLike); } count(query: FindQuery<Entity>, key?: String | keyof Entity): Observable<number> { return new Observable((subscriber) => { this.model.execute_query( `SELECT COUNT(${ key ? transformKey(this.target, key) : '*' }) FROM "${this.model.get_keyspace_name()}"."${this.model.get_table_name()}";`, [], (error, value) => { if (error) { return subscriber.error(error); } subscriber.next(parseInt(value, 10) || 0); subscriber.complete(); }, ); }); } findOne(query: FindQuery<Entity>, options?: FindQueryOptionsStatic<Entity>): Observable<Entity>; findOne(query: FindQuery<Entity>, options: FindQueryOptionsStatic<Entity> = {}): Observable<Entity> { return defer(() => this.model.findOneAsync(transformQuery(this.target, query), { ...options, ...defaultOptions.findOptions, select: transformQuerySelect(this.target, options?.select), }), ).pipe(map((x) => x && transformFrom(this.target, x))); } findOneOrFail(query: FindQuery<Entity>, options?: FindQueryOptionsStatic<Entity>): Observable<Entity>; findOneOrFail(query: FindQuery<Entity>, maybeOptions: FindQueryOptionsStatic<Entity> = {}): Observable<Entity> { return this.findOne(transformQuery(this.target, query), maybeOptions).pipe( map((entity) => { if (entity === undefined) { throw new EntityNotFoundError(this.target, query); } return entity; }), ); } find(query: FindQuery<Entity>, options?: FindQueryOptionsStatic<Entity>): Observable<Entity[]>; find(query: FindQuery<Entity>, options: FindQueryOptionsStatic<Entity> = {}): Observable<Entity[]> { return defer(() => this.model.findAsync(transformQuery(this.target, query), { ...options, ...defaultOptions.findOptions, select: transformQuerySelect(this.target, options?.select), }), ).pipe(map((x) => transformFrom(this.target, x))); } findAndCount( query: FindQuery<Entity>, options: FindQueryOptionsStatic<Entity> = {}, ): Observable<[Entity[], number]> { return defer(() => this.model.findAsync(transformQuery(this.target, query), { ...(options as any), ...defaultOptions.findOptions, select: transformQuerySelect(this.target, options?.select), }), ).pipe( map((x) => transformFrom(this.target, x)), map((entities) => [entities, entities.length] as [Entity[], number]), ); } save(entity: Partial<Entity>, options?: SaveOptionsStatic): Observable<Entity>; save(entities: Partial<Entity>[], options?: SaveOptionsStatic): Observable<Entity[]>; save( entityLike: Partial<Entity> | Partial<Entity>[], options: SaveOptionsStatic = {}, ): Observable<Entity> | Observable<Entity[]> { const saveFunc = async (entity) => { const model = new this.model(transformTo(this.target, entity)); await model.saveAsync(options); return transformFrom(this.target, model.toJSON()); }; const saveMultipleFunc = (arrayLike: Entity[]) => Promise.all(arrayLike.map((x) => saveFunc(x))); return Array.isArray(entityLike) ? defer(() => saveMultipleFunc(entityLike as any)) : defer(() => saveFunc(entityLike as any)); } update( query: FindQuery<Entity>, updateValue: Partial<Entity>, options?: UpdateOptionsStatic<Entity>, ): Observable<any>; update( query: FindQuery<Entity>, updateValue: Partial<Entity>, options: UpdateOptionsStatic<Entity> = {}, ): Observable<any> { return defer(() => this.model.updateAsync(transformQuery(this.target, query), transformQuery(this.target, updateValue), { ...defaultOptions.updateOptions, ...options, }), ); } remove(entity: Entity, options?: DeleteOptionsStatic): Observable<Entity>; remove(entity: Entity[], options?: DeleteOptionsStatic): Observable<Entity[]>; remove(entityOrEntities: Entity | Entity[], options: DeleteOptionsStatic = {}): Observable<Entity | Entity[]> { const removeFunc = (entity) => new this.model(transformTo(this.target, entity)).deleteAsync({ ...defaultOptions.deleteOptions, ...options, }); const promiseArray = Array.isArray(entityOrEntities) ? entityOrEntities.map((x) => removeFunc(x)) : [removeFunc(entityOrEntities)]; return defer(() => Promise.all(promiseArray)).pipe(map(() => entityOrEntities)); } delete(query: FindQuery<Entity>, options?: DeleteOptionsStatic): Observable<any>; delete(query = {}, options = {}) { return defer(() => this.model.deleteAsync(transformQuery(this.target, query), { ...defaultOptions.deleteOptions, ...options, }), ); } truncate(): Observable<any> { return defer(() => this.model.truncateAsync()); } stream(query: FindQuery<Entity>, options: FindQueryOptionsStatic<Entity> = {}): Observable<Entity> { const reader$ = new Subject<any>(); const onRead = (reader): void => { while (true) { const row = reader.readRow(); if (row === null) { break; } reader$.next(transformFrom(this.target, row)); } }; const onDone = (error): void => { if (error) { reader$.error(error); } reader$.complete(); return; }; this.model.stream( transformQuery(this.target, query), { ...options, ...defaultOptions.findOptions, select: transformQuerySelect(this.target, options?.select), }, onRead, onDone, ); return reader$.asObservable(); } eachRow(query: FindQuery<Entity>, options: FindQueryOptionsStatic<Entity> = {}): EachRowArgument { const reader$ = new Subject<any>(); const done$ = new Subject<any>(); const getReader = () => reader$.asObservable(); const getDone = () => done$.asObservable(); const onRow = (n, row): void => reader$.next(transformFrom(this.target, row)); const onDone = (err: Error, result: any): void => { if (err) { reader$.error(err); done$.error(err); } else { done$.next(result); } reader$.complete(); done$.complete(); }; this.model.eachRow( transformQuery(this.target, query), { ...options, ...defaultOptions.findOptions, select: transformQuerySelect(this.target, options?.select), }, onRow, onDone, ); return {getReader, getDone}; } get getModelRef(): BaseModel<Entity> { return this.model; } getReturnQueryBuilder(): ReturnQueryBuilder<Entity> { return this.returnQueryBuilder; } doBatch(queries): Promise<any> { return this.model.execute_batchAsync(queries); } } export interface EachRowArgument { getReader<T = any>(): Observable<T>; getDone(): Observable<types.ResultSet>; }