init: fuzzygraph app from fn_registry
This commit is contained in:
@@ -0,0 +1,106 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"math/rand/v2"
|
||||
|
||||
ops "fn-registry/fn_operations"
|
||||
)
|
||||
|
||||
// searchEntitiesFTS searches entities using FTS and returns matching entities.
|
||||
func searchEntitiesFTS(db *ops.DB, query string) ([]ops.Entity, error) {
|
||||
if query == "" {
|
||||
return db.ListEntities("", "")
|
||||
}
|
||||
return db.SearchEntities(query, "")
|
||||
}
|
||||
|
||||
// searchGraph returns a GraphData subgraph containing matching entities and their direct relations.
|
||||
func searchGraph(db *ops.DB, query string) (GraphData, error) {
|
||||
matches, err := searchEntitiesFTS(db, query)
|
||||
if err != nil {
|
||||
return GraphData{}, fmt.Errorf("searching entities: %w", err)
|
||||
}
|
||||
|
||||
if len(matches) == 0 {
|
||||
return GraphData{}, nil
|
||||
}
|
||||
|
||||
matchIDs := map[string]bool{}
|
||||
for _, e := range matches {
|
||||
matchIDs[e.ID] = true
|
||||
}
|
||||
|
||||
// Get all relations and filter to those touching matched entities
|
||||
allRelations, err := db.ListRelations("")
|
||||
if err != nil {
|
||||
return GraphData{}, err
|
||||
}
|
||||
|
||||
// Collect neighbor IDs
|
||||
neighborIDs := map[string]bool{}
|
||||
var subRels []ops.Relation
|
||||
for _, r := range allRelations {
|
||||
if matchIDs[r.FromEntity] || matchIDs[r.ToEntity] {
|
||||
subRels = append(subRels, r)
|
||||
neighborIDs[r.FromEntity] = true
|
||||
neighborIDs[r.ToEntity] = true
|
||||
}
|
||||
}
|
||||
|
||||
// Merge match IDs and neighbor IDs
|
||||
for id := range matchIDs {
|
||||
neighborIDs[id] = true
|
||||
}
|
||||
|
||||
// Get all entities in the subgraph
|
||||
allEntities, err := db.ListEntities("", "")
|
||||
if err != nil {
|
||||
return GraphData{}, err
|
||||
}
|
||||
|
||||
degree := map[string]int{}
|
||||
for _, r := range subRels {
|
||||
degree[r.FromEntity]++
|
||||
degree[r.ToEntity]++
|
||||
}
|
||||
|
||||
nodes := make([]GraphNode, 0)
|
||||
for _, e := range allEntities {
|
||||
if !neighborIDs[e.ID] {
|
||||
continue
|
||||
}
|
||||
color, ok := entityTypeColors[e.TypeRef]
|
||||
if !ok {
|
||||
color = "#95a5a6"
|
||||
}
|
||||
size := 8.0 + math.Min(float64(degree[e.ID])*2.0, 20.0)
|
||||
if rs, ok := e.Metadata["risk_score"]; ok {
|
||||
if v, ok := toFloat64(rs); ok {
|
||||
size += v * 0.1
|
||||
}
|
||||
}
|
||||
nodes = append(nodes, GraphNode{
|
||||
ID: e.ID, Label: e.Name, Type: e.TypeRef,
|
||||
Color: color, Size: size,
|
||||
X: rand.Float64()*100 - 50, Y: rand.Float64()*100 - 50,
|
||||
Extra: e.Metadata,
|
||||
})
|
||||
}
|
||||
|
||||
edges := make([]GraphEdge, 0)
|
||||
for _, r := range subRels {
|
||||
w := 1.0
|
||||
if r.Weight != nil {
|
||||
w = *r.Weight
|
||||
}
|
||||
edges = append(edges, GraphEdge{
|
||||
ID: r.ID, Source: r.FromEntity, Target: r.ToEntity,
|
||||
Label: r.Name, Color: "#ffffff30",
|
||||
Size: math.Max(w*3, 0.5), Type: "arrow",
|
||||
})
|
||||
}
|
||||
|
||||
return GraphData{Nodes: nodes, Edges: edges}, nil
|
||||
}
|
||||
Reference in New Issue
Block a user