iagate-querykit
Version:
QueryKit: lightweight TypeScript query toolkit with models, views, triggers, events, scheduler and adapters (better-sqlite3).
68 lines (67 loc) • 2.73 kB
JavaScript
import { QueryBuilder } from './query-builder';
export async function attachRelations(table, rows, selector) {
if (!rows || rows.length === 0)
return rows;
let registry = {};
try {
registry = (await import(process.env.QK_RELATIONS_PATH || '')).RELATIONS || {};
}
catch { }
const defs = registry[table] || [];
if (defs.length === 0)
return rows;
let wanted = 'ALL';
if (selector) {
const tmp = {};
selector((name, select) => { tmp[name] = select; });
wanted = tmp;
}
const byId = new Map();
for (const r of rows)
byId.set(r.id, r);
for (const def of defs) {
if (wanted !== 'ALL' && !(def.name in wanted))
continue;
if (def.kind === 'hasMany') {
const ids = rows.map(r => r[def.localKey || 'id']);
const children = await new QueryBuilder(def.table).whereIn(def.foreignKey, ids).all();
for (const c of children) {
const parent = byId.get(c[def.foreignKey]);
if (!parent)
continue;
parent[def.name] = parent[def.name] || [];
parent[def.name].push(c);
}
}
if (def.kind === 'belongsTo') {
const fks = rows.map(r => r[def.foreignKey]).filter((v) => v !== undefined && v !== null);
if (fks.length === 0)
continue;
const parents = await new QueryBuilder(def.table).whereIn(def.ownerKey || 'id', fks).all();
const parentByKey = new Map(parents.map((p) => [p[def.ownerKey || 'id'], p]));
for (const r of rows)
r[def.name] = parentByKey.get(r[def.foreignKey]) || null;
}
if (def.kind === 'manyToMany') {
const ids = rows.map(r => r.id);
const join = def.through.table;
const leftKey = def.through.leftKey;
const rightKey = def.through.rightKey;
const links = await new QueryBuilder(join).whereIn(leftKey, ids).all();
const rightIds = links.map((l) => l[rightKey]);
const rights = rightIds.length ? await new QueryBuilder(def.table).whereIn('id', rightIds).all() : [];
const rightById = new Map(rights.map((x) => [x.id, x]));
const bucket = new Map();
for (const l of links) {
const arr = bucket.get(l[leftKey]) || [];
const item = rightById.get(l[rightKey]);
if (item)
arr.push(item);
bucket.set(l[leftKey], arr);
}
for (const r of rows)
r[def.name] = bucket.get(r.id) || [];
}
}
return rows;
}