UNPKG

@obsidize/rx-map

Version:

ES6 Map with rxjs extensions for change detection

94 lines (93 loc) 3.45 kB
import { filter, map, share, startWith, tap } from 'rxjs/operators'; import { isUndefined } from '../common/utility'; import { pluckValueChanges } from '../common/operators'; import { OneToManyContext } from './one-to-many-context'; /** * Cache map for tracking model foreign key relationships so that we have rapid lookup speed for associated models. * * @example * * ```typescript * // Has many orders * interface Product { * id: number; // is the primary key * } * * // Has exactly one product * interface ProductOrder { * id: number; * productId: number; // is the foreign key * } * ``` */ export class OneToManyRelationship { constructor(entityMap, selectForeignKey) { this.entityMap = entityMap; this.selectForeignKey = selectForeignKey; this.store = new Map(); /** * Changes for foriegn entity relations usually happen on the foreign entity itself, rather than the primary entity. * * For example: * ProductOrder has 'id' and 'productId' - a Product can have many orders associated with it, but the value * being changed is 'productId' on ProductOrder, not 'id' on Product. */ this.changes = this.entityMap.changes.pipe(pluckValueChanges(ev => this.selectForeignKey(ev)), filter(ev => ev.currentValue !== ev.previousValue), tap(ev => this.consume(ev)), share()); } getPrimaryKeys() { return Array.from(this.store.keys()); } clear() { this.store.forEach(context => context.clear()); this.store.clear(); } associate(id, fk) { const context = this.getPrimaryKeyContext(id); context === null || context === void 0 ? void 0 : context.foreignKeySet.add(fk); } disassociate(id, fk) { const context = this.getPrimaryKeyContext(id); context === null || context === void 0 ? void 0 : context.foreignKeySet.delete(fk); } hasAssociation(id, fk) { const context = this.getPrimaryKeyContext(id); return !!context && context.foreignKeySet.has(fk); } getRelatedKeys(id) { const context = this.getPrimaryKeyContext(id); return context ? context.getForeignKeys() : []; } getRelatedKeyCount(id) { const context = this.getPrimaryKeyContext(id); return context ? context.foreignKeySet.size : 0; } hasAnyAssociation(id) { return this.getRelatedKeyCount(id) > 0; } getRelatedValues(id) { return this.entityMap.getManyExisting(this.getRelatedKeys(id)); } consume(ev) { if (!ev) return; this.disassociate(ev.previousValue, ev.entityId); this.associate(ev.currentValue, ev.entityId); } watchPrimaryKey(id) { return this.changes.pipe(filter(ev => !!ev && (ev.previousValue === id || ev.currentValue === id)), map(() => this.getRelatedValues(id)), startWith(this.getRelatedValues(id))); } deletePrimaryKeyContext(id) { const context = this.store.get(id); if (context) context.clear(); return this.store.delete(id); } getPrimaryKeyContext(id) { let result = this.store.get(id); if (!result && !isUndefined(id)) { result = new OneToManyContext(id); this.store.set(id, result); } return result; } }