|
1 | 1 | import { edgeColors } from '../../theme/colors' |
2 | 2 |
|
| 3 | +/** |
| 4 | + * Aspects that indicate identity equivalence. |
| 5 | + * These are used for visual merging of nodes that represent the same entity. |
| 6 | + * |
| 7 | + * Supports both: |
| 8 | + * - Legacy: relation="same_as" (claim.claim="SAME_AS") |
| 9 | + * - New: relation="related_to" with aspect="relationship:same-as" |
| 10 | + */ |
| 11 | +const IDENTITY_EQUIVALENT_ASPECTS = ['relationship:same-as'] as const |
| 12 | + |
| 13 | +/** |
| 14 | + * Check if an edge represents an identity-equivalent relationship. |
| 15 | + * This is the single source of truth for what edges should cause node merging. |
| 16 | + * |
| 17 | + * @param edge - Cytoscape edge object with data.relation and optionally data.raw.aspect or data.aspect |
| 18 | + */ |
| 19 | +const isIdentityEquivalentEdge = (edge: any): boolean => { |
| 20 | + const relation = edge.data?.relation?.toLowerCase() |
| 21 | + |
| 22 | + // Legacy: direct SAME_AS claim type |
| 23 | + if (relation === 'same_as') { |
| 24 | + return true |
| 25 | + } |
| 26 | + |
| 27 | + // New: RELATED_TO with identity-equivalent aspect |
| 28 | + if (relation === 'related_to') { |
| 29 | + // Check for aspect in edge data (may be in raw claim data or directly on edge) |
| 30 | + const aspect = edge.data?.raw?.aspect || edge.data?.aspect |
| 31 | + if (aspect && IDENTITY_EQUIVALENT_ASPECTS.includes(aspect as any)) { |
| 32 | + return true |
| 33 | + } |
| 34 | + } |
| 35 | + |
| 36 | + return false |
| 37 | +} |
| 38 | + |
3 | 39 | // Edge styles configuration using theme colors |
4 | 40 | const edgeStylesByClaimType: any = { |
5 | 41 | is_vouched_for: { color: edgeColors.is_vouched_for, style: 'solid', width: 4, arrow: 'triangle' }, |
@@ -84,7 +120,8 @@ const parseClaims = (claims: any) => { |
84 | 120 | id: claim.id, |
85 | 121 | source: claim.subject, |
86 | 122 | target: claim.object, |
87 | | - relation: claim.claim |
| 123 | + relation: claim.claim, |
| 124 | + aspect: claim.aspect // Include aspect for identity-equivalent detection |
88 | 125 | } |
89 | 126 | }) |
90 | 127 | }) |
@@ -304,10 +341,8 @@ const mergeSameAsNodes = ( |
304 | 341 | return { nodes, edges } |
305 | 342 | } |
306 | 343 |
|
307 | | - // Find SAME_AS edges |
308 | | - const sameAsEdges = edges.filter( |
309 | | - e => e.data.relation?.toLowerCase() === 'same_as' |
310 | | - ) |
| 344 | + // Find identity-equivalent edges (SAME_AS or RELATED_TO with same-as aspect) |
| 345 | + const sameAsEdges = edges.filter(isIdentityEquivalentEdge) |
311 | 346 |
|
312 | 347 | if (sameAsEdges.length === 0) { |
313 | 348 | return { nodes, edges } |
@@ -399,10 +434,10 @@ const mergeSameAsNodes = ( |
399 | 434 | idToCanonical.set(node.data.id, find(node.data.id)) |
400 | 435 | }) |
401 | 436 |
|
402 | | - // Rewrite edges and filter out SAME_AS |
| 437 | + // Rewrite edges and filter out identity-equivalent edges |
403 | 438 | const seenEdges = new Set<string>() |
404 | 439 | const mergedEdges = edges |
405 | | - .filter(e => e.data.relation?.toLowerCase() !== 'same_as') |
| 440 | + .filter(e => !isIdentityEquivalentEdge(e)) |
406 | 441 | .map(edge => { |
407 | 442 | const newSource = idToCanonical.get(edge.data.source) || edge.data.source |
408 | 443 | const newTarget = idToCanonical.get(edge.data.target) || edge.data.target |
|
0 commit comments