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 }