EntityAPI: Save nested summary info in the SQL database (#61732)
This commit is contained in:
@@ -11,15 +11,23 @@ import (
|
||||
|
||||
type folderInfo struct {
|
||||
UID string `json:"uid"`
|
||||
Name string `json:"name"`
|
||||
Slug string `json:"slug"`
|
||||
Name string `json:"name"` // original display name
|
||||
Slug string `json:"slug"` // full slug
|
||||
|
||||
// original slug
|
||||
originalSlug string
|
||||
|
||||
depth int32
|
||||
left int32
|
||||
right int32
|
||||
|
||||
// Build the tree
|
||||
ParentUID string `json:"-"`
|
||||
parentUID string
|
||||
|
||||
// Added after query
|
||||
children []*folderInfo
|
||||
// Calculated after query
|
||||
parent *folderInfo
|
||||
children []*folderInfo
|
||||
stack []*folderInfo
|
||||
}
|
||||
|
||||
// This will replace all entries in `entity_folder`
|
||||
@@ -32,7 +40,6 @@ func updateFolderTree(ctx context.Context, tx *session.SessionTx, tenant int64)
|
||||
}
|
||||
|
||||
all := []*folderInfo{}
|
||||
lookup := make(map[string]*folderInfo)
|
||||
rows, err := tx.Query(ctx, "SELECT uid,folder,name,slug FROM entity WHERE kind=? AND tenant_id=? ORDER BY slug asc;",
|
||||
models.StandardKindFolder, tenant)
|
||||
if err != nil {
|
||||
@@ -42,11 +49,10 @@ func updateFolderTree(ctx context.Context, tx *session.SessionTx, tenant int64)
|
||||
folder := folderInfo{
|
||||
children: []*folderInfo{},
|
||||
}
|
||||
err = rows.Scan(&folder.UID, &folder.ParentUID, &folder.Name, &folder.Slug)
|
||||
err = rows.Scan(&folder.UID, &folder.parentUID, &folder.Name, &folder.originalSlug)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
lookup[folder.UID] = &folder
|
||||
all = append(all, &folder)
|
||||
}
|
||||
err = rows.Close()
|
||||
@@ -54,16 +60,43 @@ func updateFolderTree(ctx context.Context, tx *session.SessionTx, tenant int64)
|
||||
return err
|
||||
}
|
||||
|
||||
root, lost, err := buildFolderTree(all)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = insertFolderInfo(ctx, tx, tenant, root, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, folder := range lost {
|
||||
err = insertFolderInfo(ctx, tx, tenant, folder, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func buildFolderTree(all []*folderInfo) (*folderInfo, []*folderInfo, error) {
|
||||
lost := []*folderInfo{}
|
||||
lookup := make(map[string]*folderInfo)
|
||||
for _, folder := range all {
|
||||
lookup[folder.UID] = folder
|
||||
}
|
||||
|
||||
root := &folderInfo{
|
||||
Name: "Root",
|
||||
UID: "",
|
||||
children: []*folderInfo{},
|
||||
left: 1,
|
||||
}
|
||||
lookup[""] = root
|
||||
lost := []*folderInfo{}
|
||||
|
||||
// already sorted by slug
|
||||
for _, folder := range all {
|
||||
parent, ok := lookup[folder.ParentUID]
|
||||
parent, ok := lookup[folder.parentUID]
|
||||
if ok {
|
||||
folder.parent = parent
|
||||
parent.children = append(parent.children, folder)
|
||||
@@ -72,40 +105,49 @@ func updateFolderTree(ctx context.Context, tx *session.SessionTx, tenant int64)
|
||||
}
|
||||
}
|
||||
|
||||
for _, folder := range root.children {
|
||||
err = addFolderInfo(ctx, tx, tenant, []*folderInfo{folder}, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
for _, folder := range lost {
|
||||
err = addFolderInfo(ctx, tx, tenant, []*folderInfo{folder}, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return err
|
||||
_, err := setMPTTOrder(root, []*folderInfo{}, int32(1))
|
||||
return root, lost, err
|
||||
}
|
||||
|
||||
func addFolderInfo(ctx context.Context, tx *session.SessionTx, tenant int64, tree []*folderInfo, isDetached bool) error {
|
||||
folder := tree[len(tree)-1] // last item in the tree
|
||||
// https://imrannazar.com/Modified-Preorder-Tree-Traversal
|
||||
func setMPTTOrder(folder *folderInfo, stack []*folderInfo, idx int32) (int32, error) {
|
||||
var err error
|
||||
folder.depth = int32(len(stack))
|
||||
folder.left = idx
|
||||
folder.stack = stack
|
||||
|
||||
js, _ := json.Marshal(tree)
|
||||
slugPath := "/"
|
||||
for _, f := range tree {
|
||||
slugPath += f.Slug + "/"
|
||||
if folder.depth > 0 {
|
||||
folder.Slug = "/"
|
||||
for _, f := range stack {
|
||||
folder.Slug += f.originalSlug + "/"
|
||||
}
|
||||
}
|
||||
|
||||
for _, child := range folder.children {
|
||||
idx, err = setMPTTOrder(child, append(stack, child), idx+1)
|
||||
if err != nil {
|
||||
return idx, err
|
||||
}
|
||||
}
|
||||
folder.right = idx + 1
|
||||
return folder.right, nil
|
||||
}
|
||||
|
||||
func insertFolderInfo(ctx context.Context, tx *session.SessionTx, tenant int64, folder *folderInfo, isDetached bool) error {
|
||||
js, _ := json.Marshal(folder.stack)
|
||||
grn := entity.GRN{TenantId: tenant, Kind: models.StandardKindFolder, UID: folder.UID}
|
||||
_, err := tx.Exec(ctx,
|
||||
`INSERT INTO entity_folder `+
|
||||
"(grn, tenant_id, uid, slug_path, tree, depth, detached) "+
|
||||
`VALUES (?, ?, ?, ?, ?, ?, ?)`,
|
||||
"(grn, tenant_id, uid, slug_path, tree, depth, left, right, detached) "+
|
||||
`VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
grn.ToGRNString(),
|
||||
tenant,
|
||||
folder.UID,
|
||||
slugPath,
|
||||
folder.Slug,
|
||||
string(js),
|
||||
len(tree),
|
||||
folder.depth,
|
||||
folder.left,
|
||||
folder.right,
|
||||
isDetached,
|
||||
)
|
||||
if err != nil {
|
||||
@@ -113,7 +155,7 @@ func addFolderInfo(ctx context.Context, tx *session.SessionTx, tenant int64, tre
|
||||
}
|
||||
|
||||
for _, sub := range folder.children {
|
||||
err := addFolderInfo(ctx, tx, tenant, append(tree, sub), isDetached)
|
||||
err := insertFolderInfo(ctx, tx, tenant, sub, isDetached)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user