objection
Version:
An SQL-friendly ORM for Node.js
150 lines (115 loc) • 3.8 kB
JavaScript
'use strict';
const { FindOperation } = require('../queryBuilder/operations/FindOperation');
class RelationFindOperation extends FindOperation {
constructor(name, opt) {
super(name, opt);
this.relation = opt.relation;
this.owner = opt.owner;
this.alwaysReturnArray = false;
this.assignResultToOwner = true;
this.relationProperty = opt.relationProperty || this.relation.name;
this.omitProps = [];
this.alias = null;
}
onBuild(builder) {
this.maybeApplyAlias(builder);
this.relation.findQuery(builder, this.owner);
if (this.assignResultToOwner && this.owner.isModels) {
this.selectMissingJoinColumns(builder);
}
}
onAfter2(_, related) {
const isOneToOne = this.relation.isOneToOne();
if (this.assignResultToOwner && this.owner.isModels) {
const owners = this.owner.modelArray;
const relatedByOwnerId = new Map();
for (let i = 0, l = related.length; i < l; ++i) {
const rel = related[i];
const key = this.relation.relatedProp.propKey(rel);
let arr = relatedByOwnerId.get(key);
if (!arr) {
arr = [];
relatedByOwnerId.set(key, arr);
}
arr.push(rel);
}
for (let i = 0, l = owners.length; i < l; ++i) {
const own = owners[i];
const key = this.relation.ownerProp.propKey(own);
const related = relatedByOwnerId.get(key);
if (isOneToOne) {
own[this.relationProperty] = (related && related[0]) || null;
} else {
own[this.relationProperty] = related || [];
}
}
}
return related;
}
onAfter3(builder, related) {
const isOneToOne = this.relation.isOneToOne();
const intOpt = builder.internalOptions();
if (!intOpt.keepImplicitJoinProps) {
this.omitImplicitJoinProps(related);
}
if (!this.alwaysReturnArray && isOneToOne && related.length <= 1) {
related = related[0] || undefined;
}
return super.onAfter3(builder, related);
}
selectMissingJoinColumns(builder) {
const relatedProp = this.relation.relatedProp;
const addedSelects = [];
for (let c = 0, lc = relatedProp.size; c < lc; ++c) {
const fullCol = relatedProp.ref(builder, c).fullColumn(builder);
const prop = relatedProp.props[c];
const col = relatedProp.cols[c];
if (!builder.hasSelectionAs(fullCol, col) && addedSelects.indexOf(fullCol) === -1) {
this.omitProps.push(prop);
addedSelects.push(fullCol);
}
}
if (addedSelects.length) {
builder.select(addedSelects);
}
}
maybeApplyAlias(builder) {
if (!builder.alias() && this.alias) {
builder.alias(this.alias);
}
}
omitImplicitJoinProps(related) {
const relatedModelClass = this.relation.relatedModelClass;
if (!this.omitProps.length || !related) {
return related;
}
if (!Array.isArray(related)) {
return this.omitImplicitJoinPropsFromOne(relatedModelClass, related);
}
if (!related.length) {
return related;
}
for (let i = 0, l = related.length; i < l; ++i) {
this.omitImplicitJoinPropsFromOne(relatedModelClass, related[i]);
}
return related;
}
omitImplicitJoinPropsFromOne(relatedModelClass, model) {
for (let c = 0, lc = this.omitProps.length; c < lc; ++c) {
relatedModelClass.omitImpl(model, this.omitProps[c]);
}
return model;
}
clone() {
const clone = super.clone();
clone.alwaysReturnArray = this.alwaysReturnArray;
clone.assignResultToOwner = this.assignResultToOwner;
clone.relationProperty = this.relationProperty;
clone.omitProps = this.omitProps.slice();
clone.alias = this.alias;
return clone;
}
}
module.exports = {
RelationFindOperation,
};