mirror of
https://github.com/trustgraph-ai/trustgraph.git
synced 2026-07-03 06:51:00 +02:00
Merge commit 'a8390532f7' as 'ai-context/workbench-ui'
This commit is contained in:
commit
1a72bfdec0
310 changed files with 56430 additions and 0 deletions
167
ai-context/workbench-ui/src/utils/knowledge-graph-viz.ts
Normal file
167
ai-context/workbench-ui/src/utils/knowledge-graph-viz.ts
Normal file
|
|
@ -0,0 +1,167 @@
|
|||
// Functionality here helps construct subgraphs for react-force-graph
|
||||
// visualisation
|
||||
|
||||
import { Triple, BaseApi } from "@trustgraph/client";
|
||||
import {
|
||||
query,
|
||||
labelS,
|
||||
labelP,
|
||||
labelO,
|
||||
filterInternals,
|
||||
} from "./knowledge-graph";
|
||||
|
||||
interface Node {
|
||||
id: string;
|
||||
label: string;
|
||||
group: number;
|
||||
}
|
||||
|
||||
interface Link {
|
||||
id: string;
|
||||
source: string;
|
||||
target: string;
|
||||
label: string;
|
||||
value: number;
|
||||
}
|
||||
|
||||
export interface Subgraph {
|
||||
nodes: Node[];
|
||||
links: Link[];
|
||||
}
|
||||
|
||||
export const createSubgraph = (): Subgraph => {
|
||||
return {
|
||||
nodes: [],
|
||||
links: [],
|
||||
};
|
||||
};
|
||||
|
||||
export const updateSubgraphTriples = (sg: Subgraph, triples: Triple[]) => {
|
||||
const groupId = 1;
|
||||
|
||||
const nodeIds = new Set<string>(sg.nodes.map((n) => n.id));
|
||||
const linkIds = new Set<string>(sg.links.map((n) => n.id));
|
||||
|
||||
for (const t of triples) {
|
||||
// Skip triples where the object is a literal (property edges)
|
||||
// These are now shown in the node details drawer instead
|
||||
if (!t.o.e) {
|
||||
continue;
|
||||
}
|
||||
// Source has a URI, that can be its unique ID
|
||||
const sourceId = t.s.v;
|
||||
|
||||
// Target is always an entity now (we filtered out literals above)
|
||||
const targetId = t.o.v;
|
||||
|
||||
// Links have an ID so that this edge is unique
|
||||
const linkId = t.s.v + "@@" + t.p.v + "@@" + t.o.v;
|
||||
|
||||
if (!nodeIds.has(sourceId)) {
|
||||
const n: Node = {
|
||||
id: sourceId,
|
||||
label: t.s.label ? t.s.label : "unknown",
|
||||
group: groupId,
|
||||
};
|
||||
nodeIds.add(sourceId);
|
||||
sg = {
|
||||
...sg,
|
||||
nodes: [...sg.nodes, n],
|
||||
};
|
||||
}
|
||||
|
||||
if (!nodeIds.has(targetId)) {
|
||||
const n: Node = {
|
||||
id: targetId,
|
||||
label: t.o.label ? t.o.label : "unknown",
|
||||
group: groupId,
|
||||
};
|
||||
nodeIds.add(targetId);
|
||||
sg = {
|
||||
...sg,
|
||||
nodes: [...sg.nodes, n],
|
||||
};
|
||||
}
|
||||
|
||||
if (!linkIds.has(linkId)) {
|
||||
const l: Link = {
|
||||
source: sourceId,
|
||||
target: targetId,
|
||||
id: linkId,
|
||||
label: t.p.label ? t.p.label : "unknown",
|
||||
value: 1,
|
||||
};
|
||||
linkIds.add(linkId);
|
||||
sg = {
|
||||
...sg,
|
||||
links: [...sg.links, l],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return sg;
|
||||
};
|
||||
|
||||
export const updateSubgraph = (
|
||||
socket: BaseApi,
|
||||
flowId: string,
|
||||
uri: string,
|
||||
sg: Subgraph,
|
||||
add: (s: string) => void,
|
||||
remove: (s: string) => void,
|
||||
collection?: string,
|
||||
) => {
|
||||
const api = socket.flow(flowId);
|
||||
return query(api, uri, add, remove, undefined, collection)
|
||||
.then((d) => labelS(api, d, add, remove, collection))
|
||||
.then((d) => labelP(api, d, add, remove, collection))
|
||||
.then((d) => labelO(api, d, add, remove, collection))
|
||||
.then((d) => filterInternals(d))
|
||||
.then((d) => updateSubgraphTriples(sg, d));
|
||||
};
|
||||
|
||||
export const updateSubgraphByRelationship = (
|
||||
socket: BaseApi,
|
||||
flowId: string,
|
||||
selectedNodeId: string,
|
||||
relationshipUri: string,
|
||||
direction: "incoming" | "outgoing",
|
||||
sg: Subgraph,
|
||||
add: (s: string) => void,
|
||||
remove: (s: string) => void,
|
||||
collection?: string,
|
||||
) => {
|
||||
const api = socket.flow(flowId);
|
||||
const activityName = `Following ${direction} relationship: ${relationshipUri}`;
|
||||
|
||||
add(activityName);
|
||||
|
||||
// Build the query based on direction
|
||||
const queryPromise =
|
||||
direction === "outgoing"
|
||||
? api.triplesQuery(
|
||||
{ v: selectedNodeId, e: true }, // s = selectedNode
|
||||
{ v: relationshipUri, e: true }, // p = relationship
|
||||
undefined, // o = ??? (what we want to find)
|
||||
20, // Limit results
|
||||
collection,
|
||||
)
|
||||
: api.triplesQuery(
|
||||
undefined, // s = ??? (what we want to find)
|
||||
{ v: relationshipUri, e: true }, // p = relationship
|
||||
{ v: selectedNodeId, e: true }, // o = selectedNode
|
||||
20, // Limit results
|
||||
collection,
|
||||
);
|
||||
|
||||
return queryPromise
|
||||
.then((triples) => {
|
||||
remove(activityName);
|
||||
return triples;
|
||||
})
|
||||
.then((d) => labelS(api, d, add, remove, collection))
|
||||
.then((d) => labelP(api, d, add, remove, collection))
|
||||
.then((d) => labelO(api, d, add, remove, collection))
|
||||
.then((d) => filterInternals(d))
|
||||
.then((d) => updateSubgraphTriples(sg, d));
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue