OG
JS
Docs
Demos
Examples
Edge filter pipeline
Compose relationship predicates without rebuilding the graph or overwriting edge styles.
Relationship kind
All
Payment
Device
Minimum risk
0
5
8
Clear filters
Payment
Device
7 / 7 visible
0 predicates · complete data retained
// Each filter is a relationship predicate over public edge data. OGJS composes predicates // against the retained graph and updates visibility without deleting topology or replacing // style rules, so clearing filters restores the original graph exactly. import { Ogjs } from '../../src/index'; const og = new Ogjs({ container: '#viz', theme: 'light', accessibility: { label: 'Edge filter pipeline' }, labels: { minZoom: 0 }, }); og.setGraph({ nodes: [ { id: 'case', x: -180, y: -80, size: 15, color: '#0f172a', label: 'Case' }, { id: 'account', x: -60, y: 100, size: 12, color: '#2563eb', label: 'Account' }, { id: 'device', x: -20, y: -120, size: 12, color: '#7c3aed', label: 'Device' }, { id: 'merchant', x: 80, y: 70, size: 13, color: '#ea580c', label: 'Merchant' }, { id: 'beneficiary', x: 180, y: -40, size: 12, color: '#059669', label: 'Beneficiary' }, { id: 'identity', x: 160, y: 130, size: 11, color: '#0891b2', label: 'Identity' }, ], edges: [ { source: 'case', target: 'account', width: 3, color: '#f59e0b', arrow: true, data: { kind: 'payment', risk: 9 } }, { source: 'case', target: 'device', width: 3, color: '#7c3aed', arrow: true, data: { kind: 'device', risk: 8 } }, { source: 'account', target: 'merchant', width: 3, color: '#f59e0b', arrow: true, data: { kind: 'payment', risk: 7 } }, { source: 'device', target: 'merchant', width: 3, color: '#7c3aed', arrow: true, data: { kind: 'device', risk: 6 } }, { source: 'merchant', target: 'beneficiary', width: 3, color: '#f59e0b', arrow: true, data: { kind: 'payment', risk: 4 } }, { source: 'beneficiary', target: 'identity', width: 3, color: '#7c3aed', arrow: true, data: { kind: 'device', risk: 3 } }, { source: 'account', target: 'identity', width: 3, color: '#f59e0b', arrow: true, data: { kind: 'payment', risk: 2 } }, ], }); og.camera.fit(); const kind = document.querySelector('#kind'); const risk = document.querySelector('#risk'); const count = document.querySelector('[data-testid="edge-count"]'); const predicates = document.querySelector('#predicates'); function apply() { // Rebuild the predicate set from the current controls. Clearing first makes repeated Apply // actions deterministic while the retained edge records remain untouched. og.transformations.clearEdgeFilters(); let active = 0; if (kind.value !== 'all') { const selected = kind.value; // Predicates receive public relationship data, so filtering never depends on renderer // indices or requires the application to copy the graph into another collection. og.transformations.addEdgeFilter(({ data }) => data.kind === selected); active++; } const minimum = Number(risk.value); if (minimum > 0) { og.transformations.addEdgeFilter(({ data }) => data.risk >= minimum); active++; } // Stable statistics report the composed result without exposing dense renderer flags. const { visibleEdges: visible, edges: total } = og.stats(); count.textContent = `${visible} / ${total} visible`; predicates.textContent = `${active} predicate${active === 1 ? '' : 's'} · complete data retained`; } kind.addEventListener('change', apply); risk.addEventListener('change', apply); document.querySelector('#clear').addEventListener('click', () => { kind.value = 'all'; risk.value = '0'; apply(); }); apply();
<!doctype html> <html lang="en"><head><meta charset="utf-8"/> <meta name="viewport" content="width=device-width, initial-scale=1"/> <title>Ogjs example — Edge filter pipeline</title> <link rel="stylesheet" href="../site.css"/><link rel="stylesheet" href="./data-lifecycle.css"/> <style> #viz { left: 452px; } .legend { display: flex; gap: 14px; margin-top: 12px; color: #526174; font: 600 11px var(--mono); } .legend span::before { content: ''; display: inline-block; width: 9px; height: 9px; margin-right: 6px; border-radius: 50%; background: var(--dot); } @media (max-width: 760px) { #viz { left: 0; } } </style> </head><body> <header class="rail"><a class="wordmark" href="../index.html">OG<b>JS</b></a><nav> <a href="../docs.html">Docs</a><a href="../demos.html">Demos</a> <a href="./index.html" aria-current="page">Examples</a></nav></header> <div id="viz"></div> <section class="workflow-panel"><h1>Edge filter pipeline</h1> <p>Compose relationship predicates without rebuilding the graph or overwriting edge styles.</p> <div class="controls"> <label>Relationship kind <select id="kind" aria-label="Relationship kind"> <option value="all" selected>All</option><option value="payment">Payment</option><option value="device">Device</option> </select></label> <label>Minimum risk <select id="risk" aria-label="Minimum risk"> <option value="0" selected>0</option><option value="5">5</option><option value="8">8</option> </select></label> <button id="clear">Clear filters</button> </div> <div class="legend"><span style="--dot:#f59e0b">Payment</span><span style="--dot:#7c3aed">Device</span></div> <div class="status" data-testid="edge-count" role="status" aria-live="polite">7 / 7 visible</div> <div class="status" id="predicates">0 predicates · complete data retained</div> </section> <script type="module"> // Each filter is a relationship predicate over public edge data. OGJS composes predicates // against the retained graph and updates visibility without deleting topology or replacing // style rules, so clearing filters restores the original graph exactly. import { Ogjs } from '../../src/index'; const og = new Ogjs({ container: '#viz', theme: 'light', accessibility: { label: 'Edge filter pipeline' }, labels: { minZoom: 0 }, }); og.setGraph({ nodes: [ { id: 'case', x: -180, y: -80, size: 15, color: '#0f172a', label: 'Case' }, { id: 'account', x: -60, y: 100, size: 12, color: '#2563eb', label: 'Account' }, { id: 'device', x: -20, y: -120, size: 12, color: '#7c3aed', label: 'Device' }, { id: 'merchant', x: 80, y: 70, size: 13, color: '#ea580c', label: 'Merchant' }, { id: 'beneficiary', x: 180, y: -40, size: 12, color: '#059669', label: 'Beneficiary' }, { id: 'identity', x: 160, y: 130, size: 11, color: '#0891b2', label: 'Identity' }, ], edges: [ { source: 'case', target: 'account', width: 3, color: '#f59e0b', arrow: true, data: { kind: 'payment', risk: 9 } }, { source: 'case', target: 'device', width: 3, color: '#7c3aed', arrow: true, data: { kind: 'device', risk: 8 } }, { source: 'account', target: 'merchant', width: 3, color: '#f59e0b', arrow: true, data: { kind: 'payment', risk: 7 } }, { source: 'device', target: 'merchant', width: 3, color: '#7c3aed', arrow: true, data: { kind: 'device', risk: 6 } }, { source: 'merchant', target: 'beneficiary', width: 3, color: '#f59e0b', arrow: true, data: { kind: 'payment', risk: 4 } }, { source: 'beneficiary', target: 'identity', width: 3, color: '#7c3aed', arrow: true, data: { kind: 'device', risk: 3 } }, { source: 'account', target: 'identity', width: 3, color: '#f59e0b', arrow: true, data: { kind: 'payment', risk: 2 } }, ], }); og.camera.fit(); const kind = document.querySelector('#kind'); const risk = document.querySelector('#risk'); const count = document.querySelector('[data-testid="edge-count"]'); const predicates = document.querySelector('#predicates'); function apply() { // Rebuild the predicate set from the current controls. Clearing first makes repeated Apply // actions deterministic while the retained edge records remain untouched. og.transformations.clearEdgeFilters(); let active = 0; if (kind.value !== 'all') { const selected = kind.value; // Predicates receive public relationship data, so filtering never depends on renderer // indices or requires the application to copy the graph into another collection. og.transformations.addEdgeFilter(({ data }) => data.kind === selected); active++; } const minimum = Number(risk.value); if (minimum > 0) { og.transformations.addEdgeFilter(({ data }) => data.risk >= minimum); active++; } // Stable statistics report the composed result without exposing dense renderer flags. const { visibleEdges: visible, edges: total } = og.stats(); count.textContent = `${visible} / ${total} visible`; predicates.textContent = `${active} predicate${active === 1 ? '' : 's'} · complete data retained`; } kind.addEventListener('change', apply); risk.addEventListener('change', apply); document.querySelector('#clear').addEventListener('click', () => { kind.value = 'all'; risk.value = '0'; apply(); }); apply(); </script></body></html>