Spaces:
Running
Running
File size: 4,506 Bytes
eb60b6b | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 | export interface GraphEntity {
uuid: string;
name: string;
labels: string[];
description?: string;
properties?: Record<string, unknown>;
}
export interface GraphRelationship {
subject: GraphEntity;
object: GraphEntity;
predicate: { name: string; uuid?: string };
}
function asEntity(value: unknown): GraphEntity | null {
if (!value || typeof value !== "object") return null;
const v = value as Record<string, unknown>;
const uuid = typeof v.uuid === "string" ? v.uuid : "";
if (!uuid) return null;
return {
uuid,
name: typeof v.name === "string" ? v.name : uuid,
labels: Array.isArray(v.labels) ? (v.labels as string[]) : [],
description: typeof v.description === "string" ? v.description : undefined,
properties:
v.properties && typeof v.properties === "object"
? (v.properties as Record<string, unknown>)
: undefined,
};
}
export function normalizeTriple(raw: unknown): GraphRelationship | null {
if (!raw || typeof raw !== "object") return null;
const row = raw as Record<string, unknown>;
const subject = asEntity(row.subject);
const object = asEntity(row.object);
if (!subject || !object) return null;
const predicateRaw = row.predicate;
let predName = "RELATED";
let predUuid: string | undefined;
if (predicateRaw && typeof predicateRaw === "object") {
const p = predicateRaw as Record<string, unknown>;
if (typeof p.name === "string" && p.name.trim()) predName = p.name;
if (typeof p.uuid === "string") predUuid = p.uuid;
}
return {
subject,
object,
predicate: { name: predName, uuid: predUuid },
};
}
export function normalizeTriples(raw: unknown[]): GraphRelationship[] {
return raw.map(normalizeTriple).filter((r): r is GraphRelationship => r !== null);
}
export function mergeGraphData(
entities: GraphEntity[],
relationships: GraphRelationship[],
): { entities: GraphEntity[]; relationships: GraphRelationship[] } {
const entityMap = new Map<string, GraphEntity>();
for (const entity of entities) {
entityMap.set(entity.uuid, entity);
}
for (const rel of relationships) {
entityMap.set(rel.subject.uuid, rel.subject);
entityMap.set(rel.object.uuid, rel.object);
}
return {
entities: [...entityMap.values()],
relationships,
};
}
export interface GraphEdge {
id: string;
from: string;
to: string;
label: string;
}
export function normalizeLabel(label: string): string {
return label
.replace(/ /g, "_")
.toUpperCase()
.replace(/-/g, "_")
.replace(/\./g, "_")
.replace(/,/g, "_")
.replace(/:/g, "_")
.replace(/;/g, "_")
.replace(/\(/g, "_")
.replace(/\)/g, "_")
.replace(/\[/g, "_")
.replace(/\]/g, "_")
.replace(/\{/g, "_")
.replace(/\}/g, "_")
.replace(/'/g, "_");
}
function entityMatchesLabel(entity: GraphEntity, label: string): boolean {
const wanted = normalizeLabel(label);
return entity.labels.some((value) => normalizeLabel(value) === wanted);
}
export function filterRelationshipsByEntities(
rels: GraphRelationship[],
entities: GraphEntity[],
): GraphRelationship[] {
if (entities.length === 0) return rels;
const ids = new Set(entities.map((e) => e.uuid));
return rels.filter(
(r) => ids.has(r.subject.uuid) || ids.has(r.object.uuid),
);
}
export function filterRelationshipsByLabel(
rels: GraphRelationship[],
label: string,
): GraphRelationship[] {
return rels.filter(
(r) =>
entityMatchesLabel(r.subject, label) || entityMatchesLabel(r.object, label),
);
}
export function filterRelationshipsByQuery(
rels: GraphRelationship[],
query: string,
): GraphRelationship[] {
const q = query.trim().toLowerCase();
if (!q) return rels;
return rels.filter(
(r) =>
r.subject.name.toLowerCase().includes(q) ||
r.object.name.toLowerCase().includes(q) ||
r.predicate.name.toLowerCase().includes(q),
);
}
export function relationshipsToEdges(
rels: GraphRelationship[],
): GraphEdge[] {
const seen = new Map<string, number>();
const edges: GraphEdge[] = [];
for (const rel of rels) {
const from = rel.subject.uuid;
const to = rel.object.uuid;
if (!from || !to) continue;
const label = rel.predicate?.name?.trim() || "RELATED";
const key = `${from}|${to}|${label}`;
const count = seen.get(key) ?? 0;
seen.set(key, count + 1);
edges.push({
id: `${from}-${to}-${label}-${count}`,
from,
to,
label,
});
}
return edges;
}
|