Skip to content

Commit 99d0857

Browse files
committed
feat: enhance hypergraph viewer to support normal edges for 2-node entries and assign clusters based on hyperedges
1 parent 5f2d3cb commit 99d0857

File tree

1 file changed

+47
-14
lines changed

1 file changed

+47
-14
lines changed

hyperdb/templates/hypergraph_viewer.html

Lines changed: 47 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
<script src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
1313
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
1414
<script src="https://unpkg.com/@tailwindcss/browser@4"></script>
15-
<script src="./data.js"></script>
15+
<!-- <script src="./data.js"></script> -->
1616
<style type="text/tailwindcss">
1717
@theme {
1818
--color-primary-50: #faf5ff;
@@ -32,11 +32,6 @@
3232
// API base path
3333
const API_BASE = window.location.origin;
3434

35-
// Local data testing helper
36-
// To validate with local data:
37-
// 2) Set USE_LOCAL_DATA to true below
38-
const USE_LOCAL_DATA = true; // default off
39-
4035
// Configuration constants
4136
const COLORS = [
4237
"#F6BD16",
@@ -113,7 +108,7 @@
113108

114109
// API call functions
115110
const fetchDatabaseInfo = async () => {
116-
if (USE_LOCAL_DATA && typeof datas !== "undefined") {
111+
if (typeof datas !== "undefined") {
117112
return Promise.resolve(datas.database);
118113
}
119114
const response = await fetch(`${API_BASE}/api/database/info`);
@@ -127,7 +122,7 @@
127122
sortBy,
128123
sortOrder
129124
) => {
130-
if (USE_LOCAL_DATA && typeof datas !== "undefined") {
125+
if (typeof datas !== "undefined") {
131126
const toLower = (v) => (v ?? "").toString().toLowerCase();
132127
let list = Array.isArray(datas.vertices) ? datas.vertices : [];
133128

@@ -176,7 +171,7 @@
176171
};
177172

178173
const fetchGraphData = async (vertexId) => {
179-
if (USE_LOCAL_DATA && typeof datas !== "undefined") {
174+
if (typeof datas !== "undefined") {
180175
const entry = datas.graphs[vertexId];
181176
if (entry && entry.vertices && entry.edges) {
182177
return Promise.resolve({
@@ -360,10 +355,26 @@
360355
connectedNodes.has(node.id)
361356
);
362357
} else {
363-
// Hyper mode: use bubble-sets plugin
358+
// Hyper mode: render 2-node entries as normal edges, others as bubble-sets
359+
const edgeSet = new Set();
364360
edgeEntries.forEach(([key, edge], i) => {
365361
const nodes = key.split(EDGE_SEPARATOR);
366362

363+
if (nodes.length === 2) {
364+
const [a, b] = nodes;
365+
const edgeId = a < b ? `${a}-${b}` : `${b}-${a}`;
366+
if (!edgeSet.has(edgeId)) {
367+
edgeSet.add(edgeId);
368+
hyperData.edges.push({
369+
id: edgeId,
370+
source: a,
371+
target: b,
372+
...edge,
373+
});
374+
}
375+
return;
376+
}
377+
367378
plugins.push({
368379
key: `bubble-sets-${key}`,
369380
type: "bubble-sets",
@@ -380,6 +391,28 @@
380391
members: nodes,
381392
});
382393
});
394+
395+
// Assign cluster by hyperEdges for layout grouping
396+
// Pick the heaviest hyperedge containing the node as its primary cluster
397+
const nodeIdToCandidateClusters = new Map();
398+
hyperData.hyperEdges.forEach((he) => {
399+
const weight =
400+
he.weight ||
401+
(Array.isArray(he.members) ? he.members.length : 1);
402+
(he.members || []).forEach((m) => {
403+
const list = nodeIdToCandidateClusters.get(m) || [];
404+
list.push({ id: he.id, weight });
405+
nodeIdToCandidateClusters.set(m, list);
406+
});
407+
});
408+
hyperData.nodes = hyperData.nodes.map((n) => {
409+
const candidates = nodeIdToCandidateClusters.get(n.id) || [];
410+
if (candidates.length === 0) return n;
411+
const primary = candidates.reduce((best, cur) =>
412+
cur.weight > best.weight ? cur : best
413+
);
414+
return { ...n, cluster: primary.id };
415+
});
383416
}
384417

385418
// Add tooltip plugin
@@ -427,16 +460,16 @@
427460
node: {
428461
palette: { field: "cluster" },
429462
style: {
430-
size: isGraph ? 20 : 25,
463+
size: hyperData.nodes.length > LAYOUT_THRESHOLD ? 15 : 20,
431464
labelText: (d) => d.id,
432465
fill: (d) => getNodeColor(d, selectedVertex, entityTypeColors),
433466
},
434467
},
435468
edge: {
436469
style: {
437470
size: isGraph ? 3 : 2,
438-
stroke: isGraph ? "#a68fff" : undefined,
439-
lineWidth: isGraph ? 2 : undefined,
471+
stroke: "#a68fff",
472+
lineWidth: 1,
440473
},
441474
},
442475
layout: {
@@ -446,7 +479,7 @@
446479
: "force",
447480
clustering: !isGraph,
448481
preventOverlap: true,
449-
nodeClusterBy: isGraph ? undefined : "entity_type",
482+
nodeClusterBy: isGraph ? undefined : "cluster",
450483
gravity: 20,
451484
linkDistance: isGraph ? 100 : 150,
452485
},

0 commit comments

Comments
 (0)