datagraph/node.go
2025-07-03 21:53:09 +02:00

188 lines
3.4 KiB
Go

package main
import (
// External
"github.com/jmoiron/sqlx"
// Standard
"encoding/json"
"time"
)
type Node struct {
ID int
Name string
ParentID int `db:"parent_id"`
TypeID int `db:"type_id"`
TypeName string `db:"type_name"`
TypeSchema any `db:"type_schema"`
TypeSchemaRaw []byte `db:"type_schema_raw" json:"-"`
TypeIcon string `db:"type_icon"`
Updated time.Time
Data any
DataRaw []byte `db:"data_raw" json:"-"`
NumChildren int `db:"num_children"`
Children []*Node
}
func GetNode(nodeID int) (node Node, err error) {
row := db.QueryRowx(`
SELECT
n.id,
n.name,
n.updated,
n.data AS data_raw,
t.id AS type_id,
t.name AS type_name,
t.schema AS type_schema_raw,
t.schema->>'icon' AS type_icon
FROM public.node n
INNER JOIN public.type t ON n.type_id = t.id
WHERE
n.id = $1
`, nodeID)
err = row.StructScan(&node)
if err != nil {
return
}
err = json.Unmarshal(node.TypeSchemaRaw, &node.TypeSchema)
if err != nil {
return
}
err = json.Unmarshal(node.DataRaw, &node.Data)
if err != nil {
return
}
return
}
func GetNodeTree(startNodeID, maxDepth int) (topNode *Node, err error) {
nodes := make(map[int]*Node)
var rows *sqlx.Rows
rows, err = GetNodeRows(startNodeID, maxDepth)
if err != nil {
return
}
defer rows.Close()
first := true
for rows.Next() {
var node Node
err = rows.StructScan(&node)
if err != nil {
return
}
if first {
topNode = &node
first = false
}
ComposeTree(nodes, &node)
}
return
}
func GetNodeRows(startNodeID, maxDepth int) (rows *sqlx.Rows, err error) {
rows, err = db.Queryx(`
WITH RECURSIVE nodes AS (
SELECT
COALESCE(
(SELECT parent FROM connection WHERE child = $1),
0
) AS parent_id,
$1::int AS id,
0 AS depth
UNION
SELECT
c.parent,
c.child,
ns.depth+1 AS depth
FROM connection c
INNER JOIN nodes ns ON ns.depth < $2 AND c.parent = ns.id
)
SEARCH DEPTH FIRST BY id SET ordercol
SELECT
ns.parent_id,
n.id,
n.name,
n.type_id,
t.name AS type_name,
COALESCE(t.schema->>'icon', '') AS type_icon,
n.updated,
n.data AS data_raw,
COUNT(c.child) AS num_children
FROM nodes ns
INNER JOIN public.node n ON ns.id = n.id
INNER JOIN public.type t ON n.type_id = t.id
LEFT JOIN public.connection c ON c.parent = n.id
GROUP BY
ns.depth,
ns.parent_id,
n.id,
t.name,
t.schema,
ns.ordercol
ORDER BY ordercol
`,
startNodeID,
maxDepth,
)
return
}
func ComposeTree(nodes map[int]*Node, node *Node) {
if node.Children == nil {
node.Children = []*Node{}
}
parentNode, found := nodes[node.ParentID]
if found {
if parentNode.Children == nil {
parentNode.Children = []*Node{node}
} else {
parentNode.Children = append(parentNode.Children, node)
}
}
nodes[node.ID] = node
}
func UpdateNode(nodeID int, data []byte) (err error) {
_, err = db.Exec(`UPDATE public.node SET data=$2 WHERE id=$1`, nodeID, data)
return
}
func CreateNode(parentNodeID, typeID int, name string) (err error) {
j, _ := json.Marshal(struct { Name string }{name})
row := db.QueryRow(`
INSERT INTO node(type_id, name, data)
VALUES($1, $2, $3::jsonb)
RETURNING id
`,
typeID, name, j)
var id int
err = row.Scan(&id)
if err != nil {
return
}
_, err = db.Exec(`INSERT INTO connection("parent", "child") VALUES($1, $2)`, parentNodeID, id)
return
}