Data Model: SPARQL CONSTRUCT Graph Visualization
December 6, 2025 · View on GitHub
Feature: 001-construct-graph-viz
Date: 2025-12-05
Purpose: Define core data structures and their relationships
Entity Definitions
GraphNode
Represents a unique subject or object from RDF triples in the visualization.
Attributes:
id(Number): Unique numeric identifier for vis-network (auto-incremented)uri(String | null): Full URI for URI nodes, null for literalslabel(String): Display text (prefixed URI or literal value, truncated if needed)color(String): Hex color code -#a6c8a6ff(light green, literals),#e15b13ff(orange, rdf:type objects),#97C2FC(light blue, other URIs),#c5c5c5ff(light grey, blank nodes)type(Enum):'uri'or'literal'fullValue(String): Un-truncated value for tooltip display
Relationships:
- Connected to other GraphNodes via GraphEdges (many-to-many through edges)
- Multiple edges can connect same pair of nodes (different predicates)
Lifecycle:
- Created during triple parsing
- Deduplicated by URI/literal value (Map-based construction)
- Persists in vis-network DataSet until new query results arrive
Example:
{
id: 1,
uri: 'http://example.org/Person',
label: 'ex:Person',
color: '#e15b13ff', // Orange if object of rdf:type
type: 'uri',
fullValue: 'http://example.org/Person'
}
{
id: 2,
uri: null,
label: 'John',
color: '#a6c8a6ff', // Light green for literals
type: 'literal',
fullValue: '"John"^^xsd:string'
}
GraphEdge
Represents an RDF predicate connecting two nodes.
Attributes:
id(String): Optional unique identifier (can be auto-generated by vis-network)from(Number): Source node ID (subject)to(Number): Target node ID (object)label(String): Predicate label (prefixed form, truncated if needed)predicate(String): Full predicate URIarrows(String): Arrow direction ('to'for directed edges)
Relationships:
- Links exactly one source GraphNode (from) to one target GraphNode (to)
- One-to-one with RDF predicate (after deduplication)
Lifecycle:
- Created for each unique triple (subject-predicate-object combination)
- Duplicate triples deduplicated (same from/to/predicate = single edge)
- Automatically repositions when connected nodes are dragged
Example:
{
id: 'edge_1_2',
from: 1,
to: 2,
label: 'ex:name',
predicate: 'http://example.org/name',
arrows: 'to'
}
RDFTriple (Intermediate)
Raw triple data extracted from SPARQL CONSTRUCT results. Transformed into GraphNodes and GraphEdges.
Attributes:
subject(String): Subject URIpredicate(String): Predicate URIobject(Object): Object with properties:value(String): URI or literal valuetype(String):'uri'or'literal'datatype(String | undefined): Datatype URI for literals (e.g.,http://www.w3.org/2001/XMLSchema#string)
Lifecycle:
- Parsed from YASR results (bindings or JSON-LD format)
- Exists transiently during transformation
- Not persisted after graph data created
Example:
{
subject: 'http://example.org/Person',
predicate: 'http://example.org/name',
object: {
value: 'John',
type: 'literal',
datatype: 'http://www.w3.org/2001/XMLSchema#string'
}
}
PrefixMap
Mapping of namespace prefixes to URIs for label abbreviation.
Attributes:
- Key (String): Prefix abbreviation (e.g.,
'ex','rdf','xsd') - Value (String): Full namespace URI (e.g.,
'http://example.org/')
Lifecycle:
- Extracted from YASR query metadata or result prefixes
- Merged with common default prefixes (rdf, rdfs, xsd, owl)
- Used during node/edge label generation
- Persists for duration of visualization
Example:
{
'rdf': 'http://www.w3.org/1999/02/22-rdf-syntax-ns#',
'rdfs': 'http://www.w3.org/2000/01/rdf-schema#',
'xsd': 'http://www.w3.org/2001/XMLSchema#',
'ex': 'http://example.org/'
}
NetworkOptions
Configuration object passed to vis-network instance.
Attributes:
physics(Object): Physics engine settings (force-directed layout)enabled(Boolean): Physics simulation on/offstabilization(Object): Initial layout stabilization configiterations(Number): Max iterations (200 default)
interaction(Object): User interaction settingsdragNodes(Boolean): Enable node draggingzoomView(Boolean): Enable zoom via mouse wheeldragView(Boolean): Enable pan via draghover(Boolean): Enable hover events for tooltips
layout(Object): Layout algorithm settingsimprovedLayout(Boolean): Use improved force-directed layout
autoResize(Boolean): Auto-detect container size changeswidth(String): Canvas width ('100%')height(String): Canvas height ('100%')
Lifecycle:
- Defined once during plugin initialization
- Can be updated via
network.setOptions()(e.g., disable physics after stabilization)
Example:
{
physics: {
enabled: true,
stabilization: {iterations: 200}
},
interaction: {
dragNodes: true,
zoomView: true,
dragView: true,
hover: true
},
layout: {improvedLayout: true},
autoResize: true,
width: '100%',
height: '100%'
}
Data Flow
SPARQL CONSTRUCT Results (YASR)
↓
Parse to RDFTriples[]
↓
Extract PrefixMap
↓
Transform to {nodes: GraphNode[], edges: GraphEdge[]}
↓
Create vis-network DataSets
↓
Render Network instance
↓
User Interactions → Update DataSets → Re-render
State Management
Plugin State
this.yasr: Reference to YASR instance (access to results, resultsEl)this.network: vis-network Network instance (null until first draw)this.container: DOM element containing visualizationthis.prefixes: Current PrefixMapthis.nodesDataSet: vis-network DataSet for nodesthis.edgesDataSet: vis-network DataSet for edges
Lifecycle
- Plugin Creation: Constructor called, state initialized
- Results Arrival:
canHandleResults()checks format - First Render:
draw()creates container, Network instance, parses triples, renders - Subsequent Renders: Clear DataSets, parse new results, update visualization
- Interactions: vis-network handles internally, state preserved
- Tab Switch: Container hidden/shown by YASR, Network instance persists
Validation Rules
GraphNode
idmust be unique within visualizationcolormust be valid hex code (#RRGGBB)typemust be 'uri' or 'literal'labelshould not exceed 50 characters (truncate with ellipsis)
GraphEdge
fromandtomust reference existing node IDspredicatemust be valid URI- No duplicate edges (same from + to + predicate)
RDFTriple
subjectmust be valid URIpredicatemust be valid URIobject.typemust be 'uri' or 'literal'object.datatyperequired for literals (can be inferred as xsd:string if missing)
Edge Cases Handling
- Empty triples array: No nodes or edges → Empty state message
- Self-referencing triple:
from === to→ vis-network renders loop edge - Disconnected components: Multiple unconnected subgraphs → All rendered with auto-spacing
- Very long labels: Truncate to 50 chars, preserve full value in
fullValueattribute - Missing prefixes: Fall back to URI truncation with ellipsis
- Duplicate triples: Deduplicate during edge creation (Set/Map-based tracking)
- No datatype on literal: Default to
xsd:string