mirror of
https://github.com/tiennm99/goclaw.git
synced 2026-10-13 04:14:56 +00:00
- Sigma.js graph: restore doc_type coloring (revert Louvain community detection) - Fix animation flash after FA2 layout finishes by removing post-processing camera reset and redundant noverlap/compactOrphans in stopLayout - Fix vault tree "Load more" state bug when filtering by doc_type: add treeVersion counter to force re-mount and reset auto-expand state - Fix meta map loss: loadRoot now merges instead of replacing, preserving subtree entries from previous loadSubtree calls - Add compact graph DTO endpoints and hooks for KG and vault graphs - Add semantic zoom tiers, adaptive FA2 settings, and node sizing
113 lines
2.9 KiB
Go
113 lines
2.9 KiB
Go
package pg
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"fmt"
|
|
|
|
"github.com/nextlevelbuilder/goclaw/internal/store"
|
|
)
|
|
|
|
// PGKGGraphStore implements store.KGGraphStore backed by PostgreSQL.
|
|
type PGKGGraphStore struct {
|
|
db *sql.DB
|
|
}
|
|
|
|
// NewPGKGGraphStore creates a new PG-backed KG graph store.
|
|
func NewPGKGGraphStore(db *sql.DB) *PGKGGraphStore {
|
|
return &PGKGGraphStore{db: db}
|
|
}
|
|
|
|
// ListKGGraphNodes returns lightweight KG entities for graph visualization.
|
|
func (s *PGKGGraphStore) ListKGGraphNodes(ctx context.Context, agentID, userID string, limit int) ([]store.KGGraphNode, int, error) {
|
|
aid := parseUUIDOrNil(agentID)
|
|
if limit <= 0 {
|
|
limit = 2000
|
|
}
|
|
if limit > 10000 {
|
|
limit = 10000
|
|
}
|
|
|
|
tid := store.TenantIDFromContext(ctx)
|
|
|
|
q := `SELECT id, name, entity_type, confidence
|
|
FROM kg_entities WHERE agent_id = $1 AND tenant_id = $2 AND valid_until IS NULL`
|
|
args := []any{aid, tid}
|
|
p := 3
|
|
|
|
if !store.IsSharedKG(ctx) && userID != "" {
|
|
q += fmt.Sprintf(" AND user_id = $%d", p)
|
|
args = append(args, userID)
|
|
p++
|
|
}
|
|
|
|
q += " ORDER BY updated_at DESC"
|
|
q += fmt.Sprintf(" LIMIT $%d", p)
|
|
args = append(args, limit)
|
|
|
|
rows, err := s.db.QueryContext(ctx, q, args...)
|
|
if err != nil {
|
|
return nil, 0, fmt.Errorf("kg graph nodes: %w", err)
|
|
}
|
|
defer rows.Close()
|
|
|
|
var nodes []store.KGGraphNode
|
|
for rows.Next() {
|
|
var n store.KGGraphNode
|
|
if err := rows.Scan(&n.ID, &n.Name, &n.EntityType, &n.Confidence); err != nil {
|
|
return nil, 0, fmt.Errorf("kg graph nodes scan: %w", err)
|
|
}
|
|
nodes = append(nodes, n)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, 0, fmt.Errorf("kg graph nodes rows: %w", err)
|
|
}
|
|
return nodes, len(nodes), nil
|
|
}
|
|
|
|
// ListKGGraphEdges returns lightweight KG relations for graph visualization.
|
|
func (s *PGKGGraphStore) ListKGGraphEdges(ctx context.Context, agentID, userID string, limit int) ([]store.KGGraphEdge, int, error) {
|
|
aid := parseUUIDOrNil(agentID)
|
|
if limit <= 0 {
|
|
limit = 6000
|
|
}
|
|
if limit > 30000 {
|
|
limit = 30000
|
|
}
|
|
|
|
tid := store.TenantIDFromContext(ctx)
|
|
|
|
q := `SELECT id, source_entity_id, target_entity_id, relation_type
|
|
FROM kg_relations WHERE agent_id = $1 AND tenant_id = $2 AND valid_until IS NULL`
|
|
args := []any{aid, tid}
|
|
p := 3
|
|
|
|
if !store.IsSharedKG(ctx) && userID != "" {
|
|
q += fmt.Sprintf(" AND user_id = $%d", p)
|
|
args = append(args, userID)
|
|
p++
|
|
}
|
|
|
|
q += fmt.Sprintf(" LIMIT $%d", p)
|
|
args = append(args, limit)
|
|
|
|
rows, err := s.db.QueryContext(ctx, q, args...)
|
|
if err != nil {
|
|
return nil, 0, fmt.Errorf("kg graph edges: %w", err)
|
|
}
|
|
defer rows.Close()
|
|
|
|
var edges []store.KGGraphEdge
|
|
for rows.Next() {
|
|
var e store.KGGraphEdge
|
|
if err := rows.Scan(&e.ID, &e.SourceID, &e.TargetID, &e.RelationType); err != nil {
|
|
return nil, 0, fmt.Errorf("kg graph edges scan: %w", err)
|
|
}
|
|
edges = append(edges, e)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, 0, fmt.Errorf("kg graph edges rows: %w", err)
|
|
}
|
|
return edges, len(edges), nil
|
|
}
|