Skip to content

Commit

Permalink
perf(core): reduce usage of wrap helper internally
Browse files Browse the repository at this point in the history
Related: #732
  • Loading branch information
B4nan committed Aug 13, 2020
1 parent f232bb3 commit 66ffc3b
Show file tree
Hide file tree
Showing 25 changed files with 204 additions and 212 deletions.
76 changes: 38 additions & 38 deletions packages/core/src/EntityManager.ts

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions packages/core/src/drivers/DatabaseDriver.ts
Expand Up @@ -5,7 +5,7 @@ import { Connection, QueryResult, Transaction } from '../connections';
import { Configuration, ConnectionOptions, Utils } from '../utils';
import { QueryOrder, QueryOrderMap } from '../enums';
import { Platform } from '../platforms';
import { Collection, ReferenceType, wrap } from '../entity';
import { Collection, ReferenceType } from '../entity';
import { DriverException, EntityManager, LockMode } from '../index';

export abstract class DatabaseDriver<C extends Connection> implements IDatabaseDriver<C> {
Expand Down Expand Up @@ -50,7 +50,7 @@ export abstract class DatabaseDriver<C extends Connection> implements IDatabaseD
async syncCollection<T extends AnyEntity<T>, O extends AnyEntity<O>>(coll: Collection<T, O>, ctx?: Transaction): Promise<void> {
const pk = this.metadata.find(coll.property.type)!.primaryKeys[0];
const data = { [coll.property.name]: coll.getIdentifiers(pk) } as EntityData<T>;
await this.nativeUpdate<T>(coll.owner.constructor.name, wrap(coll.owner, true).__primaryKey, data, ctx);
await this.nativeUpdate<T>(coll.owner.constructor.name, coll.owner.__helper!.__primaryKey, data, ctx);
}

mapResult<T extends AnyEntity<T>>(result: EntityData<T>, meta: EntityMetadata, populate: PopulateOptions<T>[] = []): T | null {
Expand Down Expand Up @@ -178,7 +178,7 @@ export abstract class DatabaseDriver<C extends Connection> implements IDatabaseD
throw new Error(`Pessimistic locks are not supported by ${this.constructor.name} driver`);
}

protected shouldHaveColumn<T>(prop: EntityProperty<T>, populate: PopulateOptions<T>[], includeFormulas = true): boolean {
protected shouldHaveColumn<T extends AnyEntity<T>>(prop: EntityProperty<T>, populate: PopulateOptions<T>[], includeFormulas = true): boolean {
if (prop.formula) {
return includeFormulas;
}
Expand Down
14 changes: 7 additions & 7 deletions packages/core/src/drivers/IDatabaseDriver.ts
Expand Up @@ -27,24 +27,24 @@ export interface IDatabaseDriver<C extends Connection = Connection> {
/**
* Finds selection of entities
*/
find<T>(entityName: string, where: FilterQuery<T>, options?: FindOptions<T>, ctx?: Transaction): Promise<T[]>;
find<T extends AnyEntity<T>>(entityName: string, where: FilterQuery<T>, options?: FindOptions<T>, ctx?: Transaction): Promise<T[]>;

/**
* Finds single entity (table row, document)
*/
findOne<T>(entityName: string, where: FilterQuery<T>, options?: FindOneOptions<T>, ctx?: Transaction): Promise<T | null>;
findOne<T extends AnyEntity<T>>(entityName: string, where: FilterQuery<T>, options?: FindOneOptions<T>, ctx?: Transaction): Promise<T | null>;

nativeInsert<T>(entityName: string, data: EntityData<T>, ctx?: Transaction): Promise<QueryResult>;
nativeInsert<T extends AnyEntity<T>>(entityName: string, data: EntityData<T>, ctx?: Transaction): Promise<QueryResult>;

nativeInsertMany<T>(entityName: string, data: EntityData<T>[], ctx?: Transaction): Promise<QueryResult>;
nativeInsertMany<T extends AnyEntity<T>>(entityName: string, data: EntityData<T>[], ctx?: Transaction): Promise<QueryResult>;

nativeUpdate<T>(entityName: string, where: FilterQuery<T>, data: EntityData<T>, ctx?: Transaction): Promise<QueryResult>;
nativeUpdate<T extends AnyEntity<T>>(entityName: string, where: FilterQuery<T>, data: EntityData<T>, ctx?: Transaction): Promise<QueryResult>;

nativeDelete<T>(entityName: string, where: FilterQuery<T>, ctx?: Transaction): Promise<QueryResult>;
nativeDelete<T extends AnyEntity<T>>(entityName: string, where: FilterQuery<T>, ctx?: Transaction): Promise<QueryResult>;

syncCollection<T extends AnyEntity<T>, O extends AnyEntity<O>>(collection: Collection<T, O>, ctx?: Transaction): Promise<void>;

count<T>(entityName: string, where: FilterQuery<T>, ctx?: Transaction): Promise<number>;
count<T extends AnyEntity<T>>(entityName: string, where: FilterQuery<T>, ctx?: Transaction): Promise<number>;

aggregate(entityName: string, pipeline: any[]): Promise<any[]>;

Expand Down
10 changes: 5 additions & 5 deletions packages/core/src/entity/ArrayCollection.ts
Expand Up @@ -28,7 +28,7 @@ export class ArrayCollection<T extends AnyEntity<T>, O extends AnyEntity<O>> {

toArray(): Dictionary[] {
return this.getItems().map(item => {
const meta = wrap(item, true).__meta;
const meta = item.__helper!.__meta;
const args = [...meta.toJsonParams.map(() => undefined), [this.property.name]];

return wrap(item).toJSON(...args);
Expand All @@ -46,7 +46,7 @@ export class ArrayCollection<T extends AnyEntity<T>, O extends AnyEntity<O>> {
return [];
}

field = field || wrap(this.items[0], true).__meta.serializedPrimaryKey;
field = field || this.items[0].__helper!.__meta.serializedPrimaryKey;

return this.getItems().map(i => i[field as keyof T]) as unknown as U[];
}
Expand Down Expand Up @@ -80,7 +80,7 @@ export class ArrayCollection<T extends AnyEntity<T>, O extends AnyEntity<O>> {
remove(...items: (T | Reference<T>)[]): void {
for (const item of items) {
const entity = Reference.unwrapReference(item);
const idx = this.items.findIndex(i => wrap(i, true).__serializedPrimaryKey === wrap(entity, true).__serializedPrimaryKey);
const idx = this.items.findIndex(i => i.__helper!.__serializedPrimaryKey === entity.__helper!.__serializedPrimaryKey);

if (idx !== -1) {
delete this[this.items.length - 1]; // remove last item
Expand All @@ -101,7 +101,7 @@ export class ArrayCollection<T extends AnyEntity<T>, O extends AnyEntity<O>> {

return !!this.items.find(i => {
const objectIdentity = i === entity;
const primaryKeyIdentity = !!wrap(i, true).__primaryKey && !!wrap(entity, true).__primaryKey && wrap(i, true).__serializedPrimaryKey === wrap(entity, true).__serializedPrimaryKey;
const primaryKeyIdentity = i.__helper!.__primaryKey && entity.__helper!.__primaryKey && i.__helper!.__serializedPrimaryKey === entity.__helper!.__serializedPrimaryKey;

return objectIdentity || primaryKeyIdentity;
});
Expand All @@ -126,7 +126,7 @@ export class ArrayCollection<T extends AnyEntity<T>, O extends AnyEntity<O>> {
*/
get property(): EntityProperty<T> {
if (!this._property) {
const meta = wrap(this.owner, true).__meta;
const meta = this.owner.__helper!.__meta;
const field = Object.keys(meta.properties).find(k => this.owner[k] === this);
this._property = meta.properties[field!];
}
Expand Down
13 changes: 6 additions & 7 deletions packages/core/src/entity/BaseEntity.ts
@@ -1,24 +1,23 @@
import { wrap } from './wrap';
import { IdentifiedReference, Reference } from './Reference';
import { Dictionary, EntityData, IWrappedEntity, LoadedReference, Populate } from '../typings';
import { AnyEntity, Dictionary, EntityData, IWrappedEntity, LoadedReference, Populate } from '../typings';
import { AssignOptions, EntityAssigner } from './EntityAssigner';

export abstract class BaseEntity<T, PK extends keyof T> implements IWrappedEntity<T, PK> {
export abstract class BaseEntity<T extends AnyEntity<T>, PK extends keyof T> implements IWrappedEntity<T, PK> {

isInitialized(): boolean {
return wrap(this, true).isInitialized();
return (this as unknown as T).__helper!.isInitialized();
}

populated(populated = true): void {
wrap(this, true).populated(populated);
(this as unknown as T).__helper!.populated(populated);
}

toReference<PK2 extends PK = never, P extends Populate<T> = never>(): IdentifiedReference<T, PK2> & LoadedReference<T, P> {
return Reference.create<T, PK>(this as unknown as T) as IdentifiedReference<T, PK> & LoadedReference<T>;
}

toObject(ignoreFields: string[] = []): Dictionary {
return wrap(this, true).toObject(ignoreFields) as EntityData<T>;
return (this as unknown as T).__helper!.toObject(ignoreFields) as EntityData<T>;
}

toJSON(...args: any[]): Dictionary {
Expand All @@ -30,7 +29,7 @@ export abstract class BaseEntity<T, PK extends keyof T> implements IWrappedEntit
}

init(populated = true): Promise<T> {
return wrap(this as unknown as T, true).init(populated);
return (this as unknown as T).__helper!.init(populated);
}

}
29 changes: 14 additions & 15 deletions packages/core/src/entity/Collection.ts
Expand Up @@ -4,7 +4,6 @@ import { ReferenceType } from './enums';
import { Utils, ValidationError } from '../utils';
import { QueryOrder, QueryOrderMap } from '../enums';
import { Reference } from './Reference';
import { wrap } from './wrap';

export class Collection<T extends AnyEntity<T>, O extends AnyEntity<O> = AnyEntity> extends ArrayCollection<T, O> {

Expand Down Expand Up @@ -108,7 +107,7 @@ export class Collection<T extends AnyEntity<T>, O extends AnyEntity<O> = AnyEnti
remove(...items: (T | Reference<T>)[]): void {
const unwrapped = items.map(i => Reference.unwrapReference(i));
this.modify('remove', unwrapped);
const em = wrap(this.owner, true).__em;
const em = this.owner.__helper!.__em;

if (this.property.orphanRemoval && em) {
for (const item of unwrapped) {
Expand All @@ -132,7 +131,7 @@ export class Collection<T extends AnyEntity<T>, O extends AnyEntity<O> = AnyEnti

isInitialized(fully = false): boolean {
if (fully) {
return this.initialized && this.items.every(item => wrap(item, true).isInitialized());
return this.initialized && this.items.every(item => item.__helper!.isInitialized());
}

return this.initialized;
Expand All @@ -159,15 +158,15 @@ export class Collection<T extends AnyEntity<T>, O extends AnyEntity<O> = AnyEnti
async init(populate?: string[], where?: FilterQuery<T>, orderBy?: QueryOrderMap): Promise<this>;
async init(populate: string[] | InitOptions<T> = [], where?: FilterQuery<T>, orderBy?: QueryOrderMap): Promise<this> {
const options = Utils.isObject<InitOptions<T>>(populate) ? populate : { populate, where, orderBy };
const em = wrap(this.owner, true).__em;
const em = this.owner.__helper!.__em;

if (!em) {
throw ValidationError.entityNotManaged(this.owner);
}

if (!this.initialized && this.property.reference === ReferenceType.MANY_TO_MANY && em.getDriver().getPlatform().usesPivotTable()) {
const map = await em.getDriver().loadFromPivotTable<T, O>(this.property, [wrap(this.owner, true).__primaryKeys], options.where, options.orderBy);
this.hydrate(map[wrap(this.owner, true).__serializedPrimaryKey].map(item => em.merge<T>(this.property.type, item)));
const map = await em.getDriver().loadFromPivotTable<T, O>(this.property, [this.owner.__helper!.__primaryKeys], options.where, options.orderBy);
this.hydrate(map[this.owner.__helper!.__serializedPrimaryKey].map(item => em.merge<T>(this.property.type, item)));
this._lazyInitialized = true;

return this;
Expand Down Expand Up @@ -219,7 +218,7 @@ export class Collection<T extends AnyEntity<T>, O extends AnyEntity<O> = AnyEnti

private createCondition<T extends AnyEntity<T>>(cond: FilterQuery<T> = {}): FilterQuery<T> {
if (this.property.reference === ReferenceType.ONE_TO_MANY) {
cond[this.property.mappedBy as string] = wrap(this.owner, true).__primaryKey;
cond[this.property.mappedBy as string] = this.owner.__helper!.__primaryKey;
} else { // MANY_TO_MANY
this.createManyToManyCondition(cond as Dictionary);
}
Expand All @@ -240,11 +239,11 @@ export class Collection<T extends AnyEntity<T>, O extends AnyEntity<O> = AnyEnti
}

private createManyToManyCondition(cond: Dictionary) {
if (this.property.owner || wrap(this.owner, true).__internal.platform.usesPivotTable()) {
const pk = wrap(this.items[0], true).__meta.primaryKeys[0]; // we know there is at least one item as it was checked in load method
cond[pk] = { $in: this.items.map(item => wrap(item, true).__primaryKey) };
if (this.property.owner || this.owner.__helper!.__internal.platform.usesPivotTable()) {
const pk = this.items[0].__helper!.__meta.primaryKeys[0]; // we know there is at least one item as it was checked in load method
cond[pk] = { $in: this.items.map(item => item.__helper!.__primaryKey) };
} else {
cond[this.property.mappedBy] = wrap(this.owner, true).__primaryKey;
cond[this.property.mappedBy] = this.owner.__helper!.__primaryKey;
}
}

Expand All @@ -260,7 +259,7 @@ export class Collection<T extends AnyEntity<T>, O extends AnyEntity<O> = AnyEnti

private checkInitialized(): void {
if (!this.isInitialized()) {
throw new Error(`Collection<${this.property.type}> of entity ${this.owner.constructor.name}[${wrap(this.owner, true).__primaryKey}] not initialized`);
throw new Error(`Collection<${this.property.type}> of entity ${this.owner.constructor.name}[${this.owner.__helper!.__primaryKey}] not initialized`);
}
}

Expand All @@ -274,7 +273,7 @@ export class Collection<T extends AnyEntity<T>, O extends AnyEntity<O> = AnyEnti
}

private cancelOrphanRemoval(items: T[]): void {
const em = wrap(this.owner, true).__em;
const em = this.owner.__helper!.__em;

if (!em) {
return;
Expand All @@ -293,12 +292,12 @@ export class Collection<T extends AnyEntity<T>, O extends AnyEntity<O> = AnyEnti

private validateModification(items: T[]): void {
// currently we allow persisting to inverse sides only in SQL drivers
if (wrap(this.owner, true).__internal.platform.usesPivotTable() || !this.property.mappedBy) {
if (this.owner.__helper!.__internal.platform.usesPivotTable() || !this.property.mappedBy) {
return;
}

const check = (item: T) => {
if (wrap(item).isInitialized()) {
if (item.__helper!.isInitialized()) {
return false;
}

Expand Down
5 changes: 2 additions & 3 deletions packages/core/src/entity/EntityAssigner.ts
Expand Up @@ -6,15 +6,14 @@ import { AnyEntity, EntityData, EntityMetadata, EntityProperty } from '../typing
import { Utils } from '../utils';
import { ReferenceType } from './enums';
import { Reference } from './Reference';
import { wrap } from './wrap';

export class EntityAssigner {

static assign<T extends AnyEntity<T>>(entity: T, data: EntityData<T>, options?: AssignOptions): T;
static assign<T extends AnyEntity<T>>(entity: T, data: EntityData<T>, onlyProperties?: boolean): T;
static assign<T extends AnyEntity<T>>(entity: T, data: EntityData<T>, onlyProperties: AssignOptions | boolean = false): T {
const options = (typeof onlyProperties === 'boolean' ? { onlyProperties } : onlyProperties);
const wrapped = wrap(entity, true);
const wrapped = entity.__helper!;
const em = options.em || wrapped.__em;
const meta = wrapped.__meta;
const root = Utils.getRootEntity(wrapped.__internal.metadata, meta);
Expand Down Expand Up @@ -76,7 +75,7 @@ export class EntityAssigner {
return;
}

const meta2 = wrap(entity[prop.name], true).__meta as EntityMetadata;
const meta2 = entity[prop.name].__helper!.__meta as EntityMetadata;
const prop2 = meta2.properties[prop.inversedBy || prop.mappedBy];

if (prop2 && !entity[prop.name][prop2.name]) {
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/entity/EntityFactory.ts
Expand Up @@ -25,7 +25,7 @@ export class EntityFactory {
const meta = this.metadata.get(entityName);
meta.primaryKeys.forEach(pk => this.denormalizePrimaryKey(data, pk, meta.properties[pk]));
const entity = this.createEntity(data, meta);
const wrapped = wrap(entity, true);
const wrapped = entity.__helper!;

if (initialized && !Utils.isEntity(data)) {
this.hydrator.hydrate(entity, meta, data, newEntity);
Expand Down
18 changes: 8 additions & 10 deletions packages/core/src/entity/EntityHelper.ts
Expand Up @@ -6,16 +6,14 @@ import { EntityTransformer } from './EntityTransformer';
import { LockMode } from '../unit-of-work';
import { Reference } from './Reference';
import { Platform } from '../platforms';
import { ValidationError } from '../utils';
import { Utils, ValidationError } from '../utils';
import { ReferenceType } from './enums';
import { Collection } from './Collection';
import { wrap } from './wrap';
import { WrappedEntity } from './WrappedEntity';

export class EntityHelper {

static async init<T extends AnyEntity<T>>(entity: T, populated = true, lockMode?: LockMode): Promise<T> {
const wrapped = wrap(entity, true);
const wrapped = entity.__helper!;
const em = wrapped.__em;

if (!em) {
Expand Down Expand Up @@ -111,7 +109,7 @@ export class EntityHelper {
let name = meta.name;

// distinguish not initialized entities
if (!wrap(this).isInitialized()) {
if (!this.__helper!.isInitialized()) {
name = `Ref<${name}>`;
}

Expand All @@ -135,19 +133,19 @@ export class EntityHelper {
ref[prop.name] = val as T[string & keyof T];
}

private static propagate<T>(entity: T, owner: T, prop: EntityProperty<T>): void {
private static propagate<T extends AnyEntity<T>, O extends AnyEntity<O>>(entity: T, owner: O, prop: EntityProperty<O>): void {
const inverse = entity && entity[prop.inversedBy || prop.mappedBy];

if (prop.reference === ReferenceType.MANY_TO_ONE && inverse && wrap(inverse, true).isInitialized()) {
(inverse as Collection<T>).add(owner);
if (prop.reference === ReferenceType.MANY_TO_ONE && Utils.isCollection<O, T>(inverse) && inverse.isInitialized()) {
inverse.add(owner);
}

if (prop.reference === ReferenceType.ONE_TO_ONE && entity && wrap(entity, true).isInitialized() && Reference.unwrapReference(inverse) !== owner) {
if (prop.reference === ReferenceType.ONE_TO_ONE && entity && entity.__helper!.isInitialized() && Reference.unwrapReference(inverse) !== owner) {
EntityHelper.propagateOneToOne(entity, owner, prop);
}
}

private static propagateOneToOne<T>(entity: T, owner: T, prop: EntityProperty<T>): void {
private static propagateOneToOne<T, O>(entity: T, owner: O, prop: EntityProperty<O>): void {
const inverse = entity[prop.inversedBy || prop.mappedBy];

if (Reference.isReference(inverse)) {
Expand Down
17 changes: 10 additions & 7 deletions packages/core/src/entity/EntityLoader.ts
Expand Up @@ -5,7 +5,6 @@ import { Utils, ValidationError } from '../utils';
import { Collection } from './Collection';
import { QueryOrder, QueryOrderMap } from '../enums';
import { Reference } from './Reference';
import { wrap } from './wrap';

type Options<T extends AnyEntity<T>> = {
where?: FilterQuery<T>;
Expand Down Expand Up @@ -102,8 +101,12 @@ export class EntityLoader {

// set populate flag
entities.forEach(entity => {
if (Utils.isEntity(entity[field], true) || entity[field] as unknown instanceof Collection) {
wrap(entity[field], true).populated();
const value = entity[field];

if (Utils.isEntity(value, true)) {
(value as AnyEntity).__helper!.populated();
} else if (Utils.isCollection(value)) {
value.populated();
}
});

Expand Down Expand Up @@ -171,7 +174,7 @@ export class EntityLoader {
return [];
}

const ids = Utils.unique(children.map(e => Utils.getPrimaryKeyValues(e, wrap(e, true).__meta.primaryKeys, true)));
const ids = Utils.unique(children.map(e => Utils.getPrimaryKeyValues(e, e.__helper!.__meta.primaryKeys, true)));
const where = { [fk]: { $in: ids }, ...(options.where as Dictionary) };

return this.em.find<T>(prop.type, where, {
Expand Down Expand Up @@ -213,11 +216,11 @@ export class EntityLoader {
}

private async findChildrenFromPivotTable<T extends AnyEntity<T>>(filtered: T[], prop: EntityProperty, field: keyof T, refresh: boolean, where?: FilterQuery<T>, orderBy?: QueryOrderMap): Promise<AnyEntity[]> {
const map = await this.driver.loadFromPivotTable(prop, filtered.map(e => wrap(e, true).__primaryKeys), where, orderBy, this.em.getTransactionContext());
const map = await this.driver.loadFromPivotTable(prop, filtered.map(e => e.__helper!.__primaryKeys), where, orderBy, this.em.getTransactionContext());
const children: AnyEntity[] = [];

for (const entity of filtered) {
const items = map[wrap(entity, true).__serializedPrimaryKey as string].map(item => this.em.merge(prop.type, item, refresh));
const items = map[entity.__helper!.__serializedPrimaryKey as string].map(item => this.em.merge(prop.type, item, refresh));
(entity[field] as unknown as Collection<AnyEntity>).hydrate(items);
children.push(...items);
}
Expand Down Expand Up @@ -257,7 +260,7 @@ export class EntityLoader {
return children.map(e => Reference.unwrapReference(e[field]));
}

return children.filter(e => !wrap(e[field], true).isInitialized()).map(e => Reference.unwrapReference(e[field]));
return children.filter(e => !(e[field] as AnyEntity).__helper!.isInitialized()).map(e => Reference.unwrapReference(e[field]));
}

private lookupAllRelationships<T>(entityName: string, prefix = '', visited: string[] = []): PopulateOptions<T>[] {
Expand Down

0 comments on commit 66ffc3b

Please sign in to comment.