package main import ( // External "github.com/jmoiron/sqlx" // Standard "time" ) type Node struct { ID int Name string ParentID int `db:"parent_id"` TypeID int `db:"type_id"` TypeName string `db:"type_name"` TypeIcon string `db:"type_icon"` Updated time.Time Data any RawData []byte `db:"raw_data"` NumChildren int `db:"num_children"` Children []*Node } func GetNode(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 raw_data, 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 }