package virtualfs import ( "context" "fmt" "github.com/get-drexa/drexa/internal/blob" "github.com/google/uuid" "github.com/uptrace/bun" ) type HierarchicalKeyResolver struct { db *bun.DB } var _ BlobKeyResolver = &HierarchicalKeyResolver{} func NewHierarchicalKeyResolver(db *bun.DB) *HierarchicalKeyResolver { return &HierarchicalKeyResolver{db: db} } func (r *HierarchicalKeyResolver) ShouldPersistKey() bool { return false } func (r *HierarchicalKeyResolver) Resolve(ctx context.Context, db bun.IDB, node *Node) (blob.Key, error) { path, err := buildNodeAbsolutePathString(ctx, db, node) if err != nil { return "", err } return blob.Key(fmt.Sprintf("%s/%s", node.AccountID, path)), nil } func (r *HierarchicalKeyResolver) ResolveDeletionKeys(ctx context.Context, node *Node, allKeys []blob.Key) (*DeletionPlan, error) { path, err := buildNodeAbsolutePathString(ctx, r.db, node) if err != nil { return nil, err } return &DeletionPlan{Prefix: blob.Key(path)}, nil } // ResolveBulkMoveOps computes blob move operations for nodes being moved to a new parent. // This implementation optimizes by computing parent paths only once (2 queries total), // rather than computing the full path for each node individually (N queries). func (r *HierarchicalKeyResolver) ResolveBulkMoveOps(ctx context.Context, db bun.IDB, nodes []*Node, newParentID uuid.UUID) ([]BlobMoveOp, error) { if len(nodes) == 0 { return nil, nil } accountID := nodes[0].AccountID oldParentID := nodes[0].ParentID for _, node := range nodes[1:] { if node.ParentID != oldParentID { return nil, ErrUnsupportedOperation } } oldParentPath, err := buildPathFromNodeID(ctx, db, oldParentID) if err != nil { return nil, err } newParentPath, err := buildPathFromNodeID(ctx, db, newParentID) if err != nil { return nil, err } // For each node, construct old and new keys using the precomputed parent paths ops := make([]BlobMoveOp, len(nodes)) for i, node := range nodes { oldKey := blob.Key(fmt.Sprintf("%s/%s/%s", accountID, oldParentPath, node.Name)) newKey := blob.Key(fmt.Sprintf("%s/%s/%s", accountID, newParentPath, node.Name)) ops[i] = BlobMoveOp{Node: node, OldKey: oldKey, NewKey: newKey} } return ops, nil }