167 lines
2.9 KiB
Go
167 lines
2.9 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
|
|
}
|