JavaScript is required

Shortest Path Query

This example loads a people relationship graph, derives searchable endpoint options from the live relation-graph instance, and lets users query the shortest connection between two selected nodes. It then maps the result back onto the rendered canvas by dimming unrelated nodes and lines while thickening the active route inside a draggable utility window.

Shortest Path Query on a Live Relationship Graph

What This Example Builds

This example builds a relationship-path explorer on top of an already rendered people graph. Users see a force-directed canvas of avatar nodes and labeled relationship lines, plus a floating white query window where they can choose a start person and an end person.

Once both endpoints are selected, the example computes the shortest connection path from the live relation-graph instance and applies the result back to the current canvas. The active route stays emphasized, unrelated nodes and lines fade to low opacity, and the same floating window also exposes shared canvas settings and image export.

How the Data Is Organized

The loaded graph follows RGJsonData with a rootId, a nodes array, and a lines array. Each node carries an id, display text, border and fill colors, and an avatar URL in data.icon. Each line stores its endpoints, a relationship label such as teacher-student or relative, display colors, and a semantic type in data.type.

There is almost no preprocessing before setJsonData(): fetchJsonData() only returns the local dataset after a short timeout. The first meaningful transformation happens after the graph is mounted, when initializeGraph() reads graphInstance.getNodes() and turns the live node set into selector options for the query form. During each query, a temporary helper graph is built from getLinks() so the route search works against the current rendered graph state rather than against a separate parallel model.

In a business application, the same structure could represent employee relationships, account-to-account investigation chains, customer influence networks, or service dependency links. The path-query panel would still work as long as node ids stay stable and each rendered node exposes a user-facing label.

How relation-graph Is Used

The page is wrapped in RGProvider, and the main component retrieves the live instance through RGHooks.useGraphInstance(). The graph itself uses a force layout with layoutName: 'force', force_node_repulsion: 0.5, and maxLayoutTimes: 50, plus straight lines, circular nodes, line text rendered on the path, a multiLineDistance of 20, and a bottom-right vertical toolbar.

The example treats the graph as a viewer with runtime analysis, not as an editor. After loading, it calls loading(), setJsonData(), clearLoading(), moveToCenter(), and zoomToFit() on the instance. During path queries, it reads getNodes(), getLinks(), and getLines(), then uses updateNode() and updateLine() to clear previous styling, dim non-path items, and mark the active route.

Custom rendering is handled with RGSlotOnNode, which replaces the default node body with a circular avatar and a label below it. Styling is extended through my-relation-graph.scss, especially for line-label chips, checked-state emphasis, and thicker highlighted route lines. The floating DraggableWindow brings in a secondary tool layer: it can be dragged, minimized, switched to a settings panel, and used to export an image by calling prepareForImageGeneration(), getOptions(), setOptions(), and restoreAfterImageGeneration() from the same live graph context.

Key Interactions

  • Users type into the start and end fields to filter node names, then pick concrete nodes from the dropdown lists.
  • The query button remains disabled until both endpoints are selected, and partially typed but unselected input is tinted red to signal an incomplete choice.
  • Submitting the form recomputes the route on the current graph state and redraws emphasis without rebuilding the dataset.
  • Non-path nodes and lines are dimmed instead of hidden, so the selected route remains readable inside its original graph context.
  • Clicking the empty canvas clears relation-graph checked-state emphasis, while the next shortest-path query resets path classes and opacity before applying a new result.
  • The floating utility window supports dragging, minimizing, opening a settings overlay, and exporting the current canvas as an image.

Key Code Fragments

This initialization fragment shows that the graph loads first and then reuses the live node list to populate the query form.

const initializeGraph = async () => {
    const myJsonData: RGJsonData = await fetchJsonData();

    graphInstance.loading();
    await graphInstance.setJsonData(myJsonData);
    graphInstance.clearLoading();
    graphInstance.moveToCenter();
    graphInstance.zoomToFit();
    setNodeItems(graphInstance.getNodes().map(n => ({text: n.text || '', value: n.id})));
};

This helper proves that shortest-path computation is derived from the current relation-graph instance rather than from a second render-specific dataset.

export const calcShortestPath = (fromNodeId: string, toNodeId: string, graphInstance: RelationGraphInstance, itemsOnPathClassName: string) => {
    clearItemsOnPathActiveStyles(graphInstance, itemsOnPathClassName);
    const graphDb = new ShortestPathGraph();
    graphDb.loadDataFromRelationGraph(graphInstance);
    const nodeIdsOnPath = graphDb.findPath(fromNodeId, toNodeId);
    const lineIdsOnPath = getAllLineIdsOnPath(nodeIdsOnPath, graphInstance);
    drawMinPath(nodeIdsOnPath, lineIdsOnPath, graphInstance, itemsOnPathClassName);
    return { lineIdsOnPath, nodeIdsOnPath };
};

This form logic shows how typing clears an old selection and turns the node list into a searchable endpoint picker.

const filtered = data.filter((item) =>
    item.text.toLowerCase().includes(keyword.toLowerCase())
);

<input
    value={value ? value.text : keyword}
    onChange={(e) => {
        if (value) {
            onChange(null);
        }
        setKeyword(e.target.value);
        setOpen(true);
    }}

This slot and style combination explains the visual identity of the demo: avatar nodes plus thicker route lines on the matched path.

<RGSlotOnNode>
    {({ node }: RGNodeSlotProps) => (
        <div className="w-12 h-12 flex place-items-center justify-center">
            <div className="my-node-avatar" style={{ backgroundImage: `url(${node.data?.icon})` }} />
            <div className="my-node-name absolute transform translate-y-[35px]">{node.text}</div>
        </div>
    )}
</RGSlotOnNode>
.rg-line-peel.my-hightlighted-path {
    .rg-line {
        stroke-width: 4px;
    }
}

What Makes This Example Distinct

Compared with search-and-focus, this example goes beyond single-node lookup and camera focus by turning live graph data into a two-endpoint relationship query. Compared with line-style-pro, it keeps the emphasis on a human-driven form workflow with searchable endpoint selection rather than on click-driven path demos, autoplay, or styling experiments. Compared with line-shape-and-label, it does not rewrite all line presentation rules at once; it selectively updates only the nodes and lines that matter for the computed route.

The strongest distinctive combination, according to the prepared comparison data, is an avatar-based people graph, a draggable white query window, selector options derived from graphInstance.getNodes(), shortest-path computation from live nodes and links, and focus-plus-context highlighting that dims everything outside the result path. That makes it a stronger starting point for analytical route inspection than for editing or global style control.

Where Else This Pattern Applies

This pattern transfers well to compliance and investigation tools where users need to explain how two accounts, devices, or people are connected inside an already loaded graph. It also fits organizational analysis, such as tracing reporting or influence paths between two employees, and infrastructure analysis, such as showing the shortest dependency route between two services in a rendered topology.

The reusable idea is to keep the visual graph as the source of truth for interaction, derive query options from the mounted instance, and apply path emphasis back onto current nodes and lines with runtime update APIs. That approach is useful when a team needs graph analysis behavior without introducing a separate editor or a second rendering pipeline.