@mikro-orm/core
Version:
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 JavaScript.
104 lines (103 loc) • 3.56 kB
JavaScript
import { LoadStrategy, PopulatePath, ReferenceKind } from '../enums.js';
import { Utils } from '../utils/Utils.js';
/**
* Expands `books.perex` like populate to use `children` array instead of the dot syntax
*/
function expandNestedPopulate(parentProp, parts, strategy, all) {
const meta = parentProp.targetMeta;
const field = parts.shift();
const prop = meta.properties[field];
const ret = { field, strategy, all };
if (parts.length > 0) {
ret.children = [expandNestedPopulate(prop, parts, strategy)];
}
return ret;
}
/**
* @internal
*/
export function expandDotPaths(meta, populate, normalized = false) {
const ret = normalized
? populate
: Utils.asArray(populate).map(field => {
if (typeof field === 'string') {
return { field };
}
/* v8 ignore next */
return typeof field === 'boolean' || field.field === PopulatePath.ALL
? { all: !!field, field: meta.primaryKeys[0] }
: field;
});
for (const p of ret) {
if (!p.field.includes('.')) {
continue;
}
const [f, ...parts] = p.field.split('.');
p.field = f;
p.children ??= [];
const prop = meta.properties[p.field];
if (parts[0] === PopulatePath.ALL) {
prop
.targetMeta.props.filter(prop => prop.lazy || prop.kind !== ReferenceKind.SCALAR)
.forEach(prop => p.children.push({ field: prop.name, strategy: p.strategy }));
}
else if (prop.kind === ReferenceKind.EMBEDDED) {
const embeddedProp = Object.values(prop.embeddedProps).find(c => c.embedded[1] === parts[0]);
ret.push({
...p,
field: embeddedProp.name,
children: parts.length > 1 ? [expandNestedPopulate(embeddedProp, parts.slice(1), p.strategy, p.all)] : [],
});
p.children.push(expandNestedPopulate(prop, parts, p.strategy, p.all));
}
else {
p.children.push(expandNestedPopulate(prop, parts, p.strategy, p.all));
}
}
return ret;
}
/**
* Returns the loading strategy based on the provided hint.
* If `BALANCED` strategy is used, it will return JOINED if the property is a to-one relation.
* @internal
*/
export function getLoadingStrategy(strategy, kind) {
if (strategy === LoadStrategy.BALANCED) {
return [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(kind)
? LoadStrategy.JOINED
: LoadStrategy.SELECT_IN;
}
return strategy;
}
function findPopulateEntry(populate, parts) {
let current = populate;
for (let i = 0; i < parts.length; i++) {
const entry = current.find(p => p.field.split(':')[0] === parts[i]);
if (!entry) {
return undefined;
}
if (i === parts.length - 1) {
return entry;
}
current = (entry.children ?? []);
}
/* v8 ignore next */
return undefined;
}
/**
* Applies per-relation overrides from `populateHints` to the normalized populate tree.
* @internal
*/
export function applyPopulateHints(populate, hints) {
for (const [path, hint] of Object.entries(hints)) {
const entry = findPopulateEntry(populate, path.split('.'));
if (!entry) {
continue;
}
for (const key of Object.keys(hint)) {
if (hint[key] != null) {
entry[key] = hint[key];
}
}
}
}