Skip to content

Commit

Permalink
refactor: finalize support for joined strategy (#600)
Browse files Browse the repository at this point in the history
Closes #440
  • Loading branch information
B4nan committed Aug 9, 2020
1 parent c09a5b6 commit e38e062
Show file tree
Hide file tree
Showing 7 changed files with 285 additions and 138 deletions.
55 changes: 28 additions & 27 deletions packages/core/src/EntityManager.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { v4 as uuid } from 'uuid';

import { Configuration, RequestContext, SmartQueryHelper, Utils, ValidationError } from './utils';
import { EntityAssigner, EntityFactory, EntityLoader, EntityRepository, EntityValidator, IdentifiedReference, Reference, ReferenceType, wrap } from './entity';
import { EntityAssigner, EntityFactory, EntityLoader, EntityRepository, EntityValidator, IdentifiedReference, LoadStrategy, Reference, ReferenceType, wrap } from './entity';
import { LockMode, UnitOfWork } from './unit-of-work';
import { EntityManagerType, FindOneOptions, FindOptions, IDatabaseDriver, Populate, PopulateMap, PopulateOptions } from './drivers';
import { AnyEntity, Constructor, Dictionary, EntityData, EntityMetadata, EntityName, FilterQuery, IPrimaryKey, Primary } from './typings';
Expand Down Expand Up @@ -83,9 +83,8 @@ export class EntityManager<D extends IDatabaseDriver = IDatabaseDriver> {
this.validator.validateParams(where);
const options = Utils.isObject<FindOptions<T>>(populate) ? populate : { populate, orderBy, limit, offset };
options.orderBy = options.orderBy || {};
const preparedPopulate = this.preparePopulate<T>(entityName, options.populate);
const opts = { ...options, populate: preparedPopulate };
const results = await this.driver.find<T>(entityName, where, opts, this.transactionContext);
options.populate = this.preparePopulate<T>(entityName, options.populate, options.strategy);
const results = await this.driver.find<T>(entityName, where, options, this.transactionContext);

if (results.length === 0) {
return [];
Expand All @@ -99,7 +98,7 @@ export class EntityManager<D extends IDatabaseDriver = IDatabaseDriver> {
}

const unique = Utils.unique(ret);
await this.entityLoader.populate<T>(entityName, unique, preparedPopulate, where, options.orderBy, options.refresh);
await this.entityLoader.populate<T>(entityName, unique, options.populate as PopulateOptions<T>[], where, options.orderBy, options.refresh);

return unique;
}
Expand Down Expand Up @@ -157,10 +156,8 @@ export class EntityManager<D extends IDatabaseDriver = IDatabaseDriver> {
}

this.validator.validateParams(where);
const preparedPopulate = this.preparePopulate<T>(entityName, options.populate);
options.populate = preparedPopulate;

const data = await this.driver.findOne(entityName, where, { ...options, populate: preparedPopulate }, this.transactionContext);
options.populate = this.preparePopulate<T>(entityName, options.populate, options.strategy);
const data = await this.driver.findOne(entityName, where, options, this.transactionContext);

if (!data) {
return null;
Expand Down Expand Up @@ -589,57 +586,61 @@ export class EntityManager<D extends IDatabaseDriver = IDatabaseDriver> {
await this.lock(entity, options.lockMode, options.lockVersion);
}

const preparedPopulate = this.preparePopulate(entityName, options.populate);
const preparedPopulate = this.preparePopulate(entityName, options.populate, options.strategy);
await this.entityLoader.populate(entityName, [entity], preparedPopulate, where, options.orderBy || {}, options.refresh);

return entity;
}

private preparePopulate<T>(entityName: string, populate?: Populate<T>): PopulateOptions<T>[] {
private preparePopulate<T>(entityName: string, populate?: Populate<T>, strategy?: LoadStrategy): PopulateOptions<T>[] {
if (!populate) {
return [];
}

if (populate === true) {
return [{ field: '*', all: true }];
}

const meta = this.metadata.get(entityName);

if (Utils.isPlainObject(populate)) {
return this.preparePopulateObject(meta, populate as PopulateMap<T>);
return this.preparePopulateObject(meta, populate as PopulateMap<T>, strategy);
}

return (populate as string[]).map(field => {
if (Utils.isString(field)) {
const strategy = meta.properties[field]?.strategy;
return { field, strategy };
}
if (Array.isArray(populate)) {
populate = (populate as string[]).map(field => {
if (Utils.isString(field)) {
return { field };
}

return field;
});
}

const ret: PopulateOptions<T>[] = this.entityLoader.normalizePopulate<T>(entityName, populate as true);

return ret.map(field => {
field.strategy = strategy ?? field.strategy;
return field;
});
}

private preparePopulateObject<T>(meta: EntityMetadata<T>, populate: PopulateMap<T>): PopulateOptions<T>[] {
private preparePopulateObject<T>(meta: EntityMetadata<T>, populate: PopulateMap<T>, strategy?: LoadStrategy): PopulateOptions<T>[] {
return Object.keys(populate).map(field => {
const prop = meta.properties[field];
const strategy = Utils.isString(populate[field]) ? populate[field] : prop!.strategy;
const fieldStrategy = strategy ?? (Utils.isString(populate[field]) ? populate[field] : prop.strategy);

if (populate[field] === true) {
return { field, strategy };
return { field, strategy: fieldStrategy };
}

if (Array.isArray(populate[field])) {
const meta2 = this.metadata.get(prop!.type);
return { field, strategy: populate[field][0], children: this.preparePopulateObject(meta2, populate[field][1]) };
return { field, strategy: populate[field][0], children: this.preparePopulateObject(meta2, populate[field][1], strategy) };
}

if (Utils.isPlainObject(populate[field])) {
const meta2 = this.metadata.get(prop!.type);
return { field, strategy, children: this.preparePopulateObject(meta2, populate[field]) };
return { field, strategy: fieldStrategy, children: this.preparePopulateObject(meta2, populate[field], strategy) };
}

return { field, strategy };
return { field, strategy: fieldStrategy };
});
}

Expand Down
11 changes: 2 additions & 9 deletions packages/core/src/drivers/IDatabaseDriver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,19 +85,12 @@ export interface FindOptions<T> {
flags?: QueryFlag[];
groupBy?: string | string[];
having?: QBFilterQuery<T>;
strategy?: LoadStrategy;
}

export interface FindOneOptions<T> {
populate?: Populate<T>;
orderBy?: QueryOrderMap;
groupBy?: string | string[];
having?: QBFilterQuery<T>;
export interface FindOneOptions<T> extends Omit<FindOptions<T>, 'limit' | 'offset'> {
lockMode?: LockMode;
lockVersion?: number | Date;
refresh?: boolean;
fields?: string[];
schema?: string;
flags?: QueryFlag[];
}

export type PopulateChildren<T> = { [K in keyof T]?: PopulateMap<ReferencedEntity<T[K]> | CollectionItem<T[K]>> };
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/entity/EntityLoader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export class EntityLoader {
}
}

private normalizePopulate<T>(entityName: string, populate: PopulateOptions<T>[] | true, lookup: boolean): PopulateOptions<T>[] {
normalizePopulate<T>(entityName: string, populate: PopulateOptions<T>[] | true, lookup = true): PopulateOptions<T>[] {
if (populate === true || populate.some(p => p.all)) {
populate = this.lookupAllRelationships(entityName);
} else {
Expand Down

0 comments on commit e38e062

Please sign in to comment.