const FindOperation = require('../queryBuilder/operations/FindOperation'); const { uniqBy } = require('../utils/objectUtils'); class RelationFindOperation extends FindOperation { constructor(name, opt) { super(name, opt); this.relation = opt.relation; this.owners = opt.owners; this.alwaysReturnArray = false; this.assignResultToOwner = true; this.relationProperty = opt.relationProperty || this.relation.name; this.omitProps = []; } onBuild(builder) { let ids = new Array(this.owners.length); for (let i = 0, l = this.owners.length; i < l; ++i) { ids[i] = this.relation.ownerProp.getProps(this.owners[i]); } this.relation.findQuery(builder, { ownerIds: uniqBy(ids, join) }); this.selectMissingJoinColumns(builder); } onAfter2(builder, related) { const isOneToOne = this.relation.isOneToOne(); if (this.assignResultToOwner) { const owners = this.owners; 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.fullCol(builder, c); 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); } } 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; } } function join(arr) { return arr.join(); } module.exports = RelationFindOperation;