UNPKG

staticql

Version:

Type-safe query engine for static content including Markdown, YAML, JSON, and more.

111 lines (110 loc) 3.76 kB
import { resolveField } from "./field.js"; /** * Builds a map from foreign key values to parent objects. * * Useful for indexing related records by a specified field. * * @param data - Array of objects to index. * @param foreignKeyPath - Dot-notated path to the key field. * @returns A map of key to array of matching objects. */ export function buildForeignKeyMap(data, foreignKeyPath) { const map = new Map(); for (const obj of data) { const values = resolveField(obj, foreignKeyPath); for (const v of values) { if (!map.has(v)) { map.set(v, []); } map.get(v).push(obj); } } return map; } /** * Finds map entries whose keys partially match a keyword. * * @param map - A map to search. * @param keyword - Keyword to match. * @param options - Optional case-insensitive matching. * @returns Matching values. */ export function findEntriesByPartialKey(map, keyword, options) { const matchFn = options?.caseInsensitive ? (k) => k.toLowerCase().includes(keyword.toLowerCase()) : (k) => k.includes(keyword); return Array.from(map.entries()) .filter(([key]) => key && matchFn(key)) .map(([, value]) => value); } /** * Resolves a direct relation (e.g., hasOne, hasMany) for a given row. * * @param row - The source object. * @param rel - Relation metadata (including localKey and foreignKey). * @param foreignData - The target dataset to match against. * @param foreignMapOpt - (optional) Pre-built foreign key map for performance. * @returns Related object(s) or null. */ export function resolveDirectRelation(row, rel, foreignData, foreignMapOpt) { const foreignMap = foreignMapOpt ?? buildForeignKeyMap(foreignData, rel.foreignKey); const relType = rel.type; const localKeys = resolveField(row, rel.localKey).flat(); let matches = []; if (localKeys.length === 1) { for (const k of localKeys) { const arr = foreignMap.get(k); if (arr) matches.push(...arr); } } else { matches = localKeys .map((k) => findEntriesByPartialKey(foreignMap, k)) .flat() .flat(); } if (relType === "hasOne") { return matches.length > 0 ? matches[0] : null; } else { return matches; } } /** * Resolves a through-relation (e.g., hasOneThrough, hasManyThrough). * * @param row - The source object. * @param rel - Relation metadata including through and target keys. * @param throughData - The intermediate dataset. * @param targetData - The final related dataset. * @param targetMapOpt - (optional) Pre-built target key map for performance. * @returns Related object(s) or null. */ export function resolveThroughRelation(row, rel, throughData, targetData, targetMapOpt, throughMapOpt) { const sourceKeys = resolveField(row, rel.sourceLocalKey).flat(); const throughMap = throughMapOpt ?? buildForeignKeyMap(throughData, rel.throughForeignKey); const targetMap = targetMapOpt ?? buildForeignKeyMap(targetData, rel.targetForeignKey); const throughRecords = sourceKeys .map((k) => throughMap.get(k)) .filter((v) => !!v) .filter(Boolean) .flat(); const targets = throughRecords .map((t) => { const throughLocalKeys = resolveField(t, rel.throughLocalKey) .filter((v) => !!v) .flat(); return throughLocalKeys .map((k) => targetMap.get(k)) .filter((v) => !!v) .flat(); }) .flat(); if (rel.type === "hasOneThrough") { return targets.length > 0 ? targets[0] : null; } else { return targets; } }