quaerateum
Version:
Simple typescript ORM for node.js based on data-mapper, unit-of-work and identity-map patterns. Supports MongoDB, MySQL, PostgreSQL and SQLite databases as well as usage with vanilla JS.
116 lines (89 loc) • 2.95 kB
text/typescript
import { EntityProperty, IEntity, IEntityType, IPrimaryKey } from '../decorators';
import { MetadataStorage } from '../metadata';
export class ArrayCollection<T extends IEntityType<T>> {
[k: number]: T;
protected readonly items: T[] = [];
private _property: EntityProperty;
constructor(readonly owner: IEntityType<any>, items?: T[]) {
if (items) {
this.items = items;
Object.assign(this, items);
}
}
getItems(): T[] {
return [...this.items];
}
toArray(parent: IEntity = this.owner): Record<string, any>[] {
return this.getItems().map(item => {
const meta = MetadataStorage.getMetadata(item.constructor.name);
const args = [...meta.toJsonParams.map(() => undefined), parent, true];
return item.toJSON(...args);
});
}
getIdentifiers(field?: string): IPrimaryKey[] {
const items = this.getItems();
if (items.length === 0) {
return [];
}
field = field || this.items[0].__serializedPrimaryKeyField;
return this.getItems().map(i => i[field as keyof T]);
}
add(...items: T[]): void {
for (const item of items) {
if (!this.contains(item)) {
this.handleInverseSide(item, 'add');
this.items.push(item);
}
}
Object.assign(this, items);
}
set(items: T[]): void {
this.removeAll();
this.add(...items);
}
remove(...items: T[]): void {
for (const item of items) {
this.handleInverseSide(item, 'remove');
const idx = this.items.findIndex(i => i.__serializedPrimaryKey === item.__serializedPrimaryKey);
if (idx !== -1) {
delete this[this.items.length - 1]; // remove last item
this.items.splice(idx, 1);
Object.assign(this, this.items); // reassign array access
}
}
}
removeAll(): void {
this.remove(...this.items);
}
contains(item: T): boolean {
return !!this.items.find(i => {
const objectIdentity = i === item;
const primaryKeyIdentity = i.__primaryKey && item.__primaryKey && i.__serializedPrimaryKey === item.__serializedPrimaryKey;
return !!(objectIdentity || primaryKeyIdentity);
});
}
count(): number {
return this.items.length;
}
get length(): number {
return this.count();
}
* [Symbol.iterator](): IterableIterator<T> {
for (const item of this.items) {
yield item;
}
}
protected handleInverseSide(item: T, method: 'add' | 'remove'): void {
if (this.property.owner && this.property.inversedBy && item[this.property.inversedBy as keyof T].isInitialized()) {
item[this.property.inversedBy as keyof T][method](this.owner);
}
}
protected get property() {
if (!this._property) {
const meta = MetadataStorage.getMetadata(this.owner.constructor.name);
const field = Object.keys(meta.properties).find(k => this.owner[k] === this);
this._property = meta.properties[field!];
}
return this._property;
}
}