tabel
Version:
A simple orm for PostgreSQL which works with simple javascript objects and arrays
160 lines (133 loc) • 4.85 kB
JavaScript
const {assign, isArray} = require('lodash');
const Relation = require('./Relation');
class HasManyThrough extends Relation {
constructor(ownerTable, toTable, throughTable, firstKey, secondKey, joiner=(() => {})) {
super(ownerTable);
assign(this, {fromTable: ownerTable.fork(), toTable, throughTable, firstKey, secondKey});
this.throughFields = [throughTable.key(), firstKey];
this.constrain((t) => {
t.scope((q) => {
q.join(
this.throughTable.tableName(), (j) => {
j.on(this.throughTable.keyCol(), '=', this.toTable.c(secondKey));
joiner(j);
}
);
});
});
}
withThrough(...throughFields) {
this.throughFields = throughFields.concat([this.throughTable.key(), this.firstKey]);
return this;
}
initRelation(fromModels) {
return fromModels.map((m) => assign(m, {[this.relationName]: []}));
}
getRelated(...args) {
if (args.length === 0) {
if (this.activeModel !== null) {
return this.getRelated([this.activeModel]);
} else {
return Promise.resolve([]);
}
}
const [fromModels] = args;
const {fromTable, throughTable, firstKey} = this;
const toTable = this.constraints.apply(this.toTable.fork());
const fromKeys = fromModels.map((m) => m[fromTable.key()]);
const cols = ['*'].concat(this.throughFields.map((field) => {
return `${throughTable.c(field)} as ${throughTable.tableName()}__${field}`;
}));
return toTable.whereIn(throughTable.c(firstKey), fromKeys)
.select(...cols).all().then((relatedModels) => {
return relatedModels.map((model) => {
const through = Object.keys(model)
.filter((field) => field.indexOf(`${throughTable.tableName()}__`) > -1)
.reduce((throughModel, field) => {
const strippedField = field.slice(`${throughTable.tableName()}__`.length);
return assign({}, throughModel, {[strippedField]: model[field]});
}, {})
;
return assign(
Object.keys(model)
.filter((field) => field.indexOf(`${throughTable.tableName()}__`) === -1)
.reduce((modelWithoutThroughs, field) => {
return assign({}, modelWithoutThroughs, {[field]: model[field]});
}, {}),
{through}
);
});
});
}
matchModels(fromModels=[], relatedModels=[]) {
const {relationName, firstKey, fromTable} = this;
const keyDict = relatedModels.reduce((dict, m) => {
const key = m.through[firstKey];
if (!isArray(dict[key])) {
return assign(dict, {[key]: [m]});
} else {
return assign(dict, {[key]: dict[key].concat([m])});
}
}, {});
return fromModels.map((m) => assign(m, {
[relationName]: isArray(keyDict[m[fromTable.key()]]) ? keyDict[m[fromTable.key()]] : []
}));
}
join(joiner=(() => {}), label=null) {
label = this.jointLabel(label, {});
const {throughTable, toTable, secondKey} = this;
if (this.ownerTable.scopeTrack.hasJoint(label)) {
return this.ownerTable;
} else {
return this.joinThrough().joint((q) => {
q.join(toTable.tableName(), (j) => {
j.on(throughTable.keyCol(), '=', toTable.c(secondKey));
joiner(j);
});
}, label);
}
}
joinThrough(joiner=(() => {}), label=null) {
label = this.throughJointLabel(label, {});
const {fromTable, throughTable, firstKey} = this;
if (this.ownerTable.scopeTrack.hasJoint(label)) {
return this.ownerTable;
} else {
return this.ownerTable.joint((q) => {
q.join(throughTable.tableName(), (j) => {
j.on(fromTable.keyCol(), '=', throughTable.c(firstKey));
joiner(j);
});
}, label);
}
}
leftJoin(joiner=(() => {}), label=null) {
label = this.jointLabel(label, {isLeftJoin: true});
const {throughTable, toTable, secondKey} = this;
if (this.ownerTable.scopeTrack.hasJoint(label)) {
return this.ownerTable;
} else {
return this.leftJoinThrough().joint((q) => {
q.leftJoin(toTable.tableName(), (j) => {
j.on(throughTable.keyCol(), '=', toTable.c(secondKey));
joiner(j);
});
}, label);
}
}
leftJoinThrough(joiner=(() => {}), label=null) {
label = this.throughJointLabel(label, {isLeftJoin: true});
const {fromTable, throughTable, firstKey} = this;
if (this.ownerTable.scopeTrack.hasJoint(label)) {
return this.ownerTable;
} else {
return this.ownerTable.joint((q) => {
q.leftJoin(throughTable.tableName(), (j) => {
j.on(fromTable.keyCol(), '=', throughTable.c(firstKey));
joiner(j);
});
}, label);
}
}
}
module.exports = HasManyThrough;