[DEPRECATED] Go implementation of plcbundle
1package didindex
2
3import (
4 "context"
5 "fmt"
6 "sort"
7 "sync"
8
9 "tangled.org/atscan.net/plcbundle/internal/plcclient"
10)
11
12// GetDIDOperations retrieves all operations for a DID WITH location metadata
13// Returns operations with bundle/position info (includes nullified operations)
14func (dim *Manager) GetDIDOperations(ctx context.Context, did string, provider BundleProvider) ([]OpLocationWithOperation, error) {
15 if err := plcclient.ValidateDIDFormat(did); err != nil {
16 return nil, err
17 }
18
19 if !dim.Exists() {
20 return nil, fmt.Errorf("DID index not available - run 'plcbundle index build' to enable DID lookups")
21 }
22
23 locations, err := dim.GetDIDLocations(did)
24 if err != nil {
25 return nil, err
26 }
27
28 if len(locations) == 0 {
29 return []OpLocationWithOperation{}, nil
30 }
31
32 // Group by bundle
33 bundleMap := make(map[uint16][]OpLocation)
34 for _, loc := range locations {
35 bundleMap[loc.Bundle()] = append(bundleMap[loc.Bundle()], loc)
36 }
37
38 var results []OpLocationWithOperation
39 for bundleNum, locs := range bundleMap {
40 positions := make([]int, len(locs))
41 for i, l := range locs {
42 positions[i] = l.PositionInt()
43 }
44 opsMap, err := provider.LoadOperations(ctx, int(bundleNum), positions)
45 if err != nil {
46 dim.logger.Printf("Warning: failed to load bundle %d: %v", bundleNum, err)
47 continue
48 }
49 for i, l := range locs {
50 if op, ok := opsMap[positions[i]]; ok {
51 results = append(results, OpLocationWithOperation{
52 Operation: *op,
53 Bundle: l.BundleInt(),
54 Position: l.PositionInt(),
55 })
56 }
57 }
58 }
59
60 // Sort by time
61 sort.Slice(results, func(i, j int) bool {
62 return results[i].Operation.CreatedAt.Before(results[j].Operation.CreatedAt)
63 })
64
65 return results, nil
66}
67
68// GetLatestDIDOperation returns the most recent non-nullified operation
69func (dim *Manager) GetLatestDIDOperation(ctx context.Context, did string, provider BundleProvider) (*plcclient.PLCOperation, error) {
70 if err := plcclient.ValidateDIDFormat(did); err != nil {
71 return nil, err
72 }
73
74 if !dim.Exists() {
75 return nil, fmt.Errorf("DID index not available - run 'plcbundle index build' to enable DID lookups")
76 }
77
78 locations, err := dim.GetDIDLocations(did)
79 if err != nil {
80 return nil, err
81 }
82
83 if len(locations) == 0 {
84 return nil, fmt.Errorf("DID not found")
85 }
86
87 // Find latest non-nullified location
88 var latestLoc *OpLocation
89 for i := range locations {
90 if locations[i].Nullified() {
91 continue
92 }
93
94 if latestLoc == nil {
95 latestLoc = &locations[i]
96 } else {
97 if locations[i].Bundle() > latestLoc.Bundle() ||
98 (locations[i].Bundle() == latestLoc.Bundle() && locations[i].Position() > latestLoc.Position()) {
99 latestLoc = &locations[i]
100 }
101 }
102 }
103
104 if latestLoc == nil {
105 return nil, fmt.Errorf("no valid operations found (all nullified)")
106 }
107
108 // Load ONLY the specific operation (efficient!)
109 return provider.LoadOperation(ctx, latestLoc.BundleInt(), latestLoc.PositionInt())
110}
111
112// BatchGetDIDLocations retrieves locations for multiple DIDs efficiently
113// Returns map[did][]OpLocation - only locations, no operation loading
114func (dim *Manager) BatchGetDIDLocations(dids []string) (map[string][]OpLocation, error) {
115 if !dim.Exists() {
116 return nil, fmt.Errorf("DID index not available")
117 }
118
119 // Group DIDs by shard to minimize shard loads
120 type shardQuery struct {
121 shardNum uint8
122 identifiers []string
123 didMap map[string]string // identifier -> original DID
124 }
125
126 shardQueries := make(map[uint8]*shardQuery)
127
128 for _, did := range dids {
129 identifier, err := extractDIDIdentifier(did)
130 if err != nil {
131 continue
132 }
133
134 shardNum := dim.calculateShard(identifier)
135
136 if shardQueries[shardNum] == nil {
137 shardQueries[shardNum] = &shardQuery{
138 shardNum: shardNum,
139 identifiers: make([]string, 0),
140 didMap: make(map[string]string),
141 }
142 }
143
144 sq := shardQueries[shardNum]
145 sq.identifiers = append(sq.identifiers, identifier)
146 sq.didMap[identifier] = did
147 }
148
149 if dim.verbose {
150 dim.logger.Printf("DEBUG: Batch lookup: %d DIDs across %d shards", len(dids), len(shardQueries))
151 }
152
153 // Process each shard (load once, search multiple times)
154 results := make(map[string][]OpLocation)
155 var mu sync.Mutex
156
157 var wg sync.WaitGroup
158 for _, sq := range shardQueries {
159 wg.Add(1)
160 go func(query *shardQuery) {
161 defer wg.Done()
162
163 // Load shard once
164 shard, err := dim.loadShard(query.shardNum)
165 if err != nil {
166 if dim.verbose {
167 dim.logger.Printf("DEBUG: Failed to load shard %02x: %v", query.shardNum, err)
168 }
169 return
170 }
171 defer dim.releaseShard(shard)
172
173 if shard.data == nil {
174 return
175 }
176
177 // Search for all identifiers in this shard
178 for _, identifier := range query.identifiers {
179 locations := dim.searchShard(shard, identifier)
180 if len(locations) > 0 {
181 originalDID := query.didMap[identifier]
182 mu.Lock()
183 results[originalDID] = locations
184 mu.Unlock()
185 }
186 }
187 }(sq)
188 }
189
190 wg.Wait()
191
192 return results, nil
193}