A Visual DAG Runner for Multi-Source Data Joins in the Browser
TLDR
Relying on fragile VLOOKUP formulas to merge datasets in the browser breaks whenever columns shift. Replacing cell formulas with a Visual Directed Acyclic Graph (DAG) pipeline resolves dependencies deterministically using topological sorting and isolated node handler dispatch.| Data Joining Model | Dependency Management | Resilience to Column Shifts | Execution Location |
|---|---|---|---|
| Cell VLOOKUP Formulas | Fragile cell-coordinate ranges | Fails when columns move | In-cell formula engine |
| Visual DAG Pipeline Engine | Topological Sort DAG Execution | 100% Column Shift Proof | Client-Side In-Memory Runner |
Structural column shifts break positional cell formulas in multi-sheet workflows
Joining data across multiple sheets in traditional web spreadsheets usually involves VLOOKUP or XLOOKUP formulas. These formulas break easily when users insert or reorder columns because they rely on positional column index arguments rather than named data attributes.
To establish reproducible data processing pipelines without backend servers, we designed a client-side Visual Directed Acyclic Graph (DAG) executor.
Topological dependency sorting ensures deterministic order-of-operation execution
Each pipeline node represents an isolated data step (Source, Filter, Formula, Join), while wires define directed dependencies. The execution engine performs a topological sort before processing nodes:
VISUAL NODE GRAPH:
┌───────────────┐
│ CSV Source A │ ──┐
└───────────────┘ │
├──> ┌───────────────┐ ┌───────────────┐
│ │ Inner Join │ ─────> │ Clean CSV │
├──> └───────────────┘ └───────────────┘
┌───────────────┐ │
│ CSV Source B │ ──┘
└───────────────┘
TOPOLOGICAL SORTED EXECUTION PIPELINE: [Step 1: Load Source A] ───► Dataset A ──┐ ├──► [Step 3: Inner Join] ───► [Step 4: Clean Output] [Step 2: Load Source B] ───► Dataset B ──┘
Here is the implementation of topological sorted pipeline execution:
// Execute visual DAG nodes in topological dependency order
export function executeDagPipeline(nodesMap, edgesArray) {
const sortedNodeIds = topologicalSort(nodesMap, edgesArray);
const nodeResults = new Map(); // nodeId -> rowObjects[]
for (const nodeId of sortedNodeIds) { const node = nodesMap.get(nodeId); // Resolve upstream inputs for current node const inputDataSets = getUpstreamNodeIds(nodeId, edgesArray) .map(id => nodeResults.get(id) || []);
// Dispatch handler and store isolated dataset output const outputRows = runNodeHandler(node, inputDataSets); nodeResults.set(nodeId, outputRows); }
return nodeResults; }
By passing immutable datasets between isolated node handlers, execution remains completely deterministic and independent of UI presentation choices.
Rule of thumb: Process multi-source data dependencies using topologically sorted pipeline nodes instead of relying on position-based cell formulas.