graph-refs.go raw
1 //go:build !(js && wasm)
2
3 package database
4
5 import (
6 "next.orly.dev/pkg/lol/log"
7 "next.orly.dev/pkg/database/indexes/types"
8 )
9
10 // AddInboundRefsToResult collects inbound references (events that reference discovered items)
11 // for events at a specific depth in the result.
12 //
13 // For example, if you have a follows graph result and want to find all kind-7 reactions
14 // to posts by users at depth 1, this collects those reactions and adds them to result.InboundRefs.
15 //
16 // Parameters:
17 // - result: The graph result to augment with ref data
18 // - depth: The depth at which to collect refs (0 = all depths)
19 // - kinds: Event kinds to collect (e.g., [7] for reactions, [6] for reposts)
20 func (d *D) AddInboundRefsToResult(result *GraphResult, depth int, kinds []uint16) error {
21 // Determine which events to find refs for
22 var targetEventIDs []string
23
24 if depth == 0 {
25 // Collect for all depths
26 targetEventIDs = result.GetAllEvents()
27 } else {
28 targetEventIDs = result.GetEventsAtDepth(depth)
29 }
30
31 // Also collect refs for events authored by pubkeys in the result
32 // This is common for "find reactions to posts by my follows" queries
33 pubkeys := result.GetAllPubkeys()
34 for _, pubkeyHex := range pubkeys {
35 pubkeySerial, err := d.PubkeyHexToSerial(pubkeyHex)
36 if err != nil {
37 continue
38 }
39
40 // Get events authored by this pubkey
41 // For efficiency, limit to relevant event kinds that might have reactions
42 authoredEvents, err := d.GetEventsByAuthor(pubkeySerial, []uint16{1, 30023}) // notes and articles
43 if err != nil {
44 continue
45 }
46
47 for _, eventSerial := range authoredEvents {
48 eventIDHex, err := d.GetEventIDFromSerial(eventSerial)
49 if err != nil {
50 continue
51 }
52 // Add to target list if not already tracking
53 if !result.HasEvent(eventIDHex) {
54 targetEventIDs = append(targetEventIDs, eventIDHex)
55 }
56 }
57 }
58
59 // For each target event, find referencing events
60 for _, eventIDHex := range targetEventIDs {
61 eventSerial, err := d.EventIDHexToSerial(eventIDHex)
62 if err != nil {
63 continue
64 }
65
66 refSerials, err := d.GetReferencingEvents(eventSerial, kinds)
67 if err != nil {
68 continue
69 }
70
71 for _, refSerial := range refSerials {
72 refEventIDHex, err := d.GetEventIDFromSerial(refSerial)
73 if err != nil {
74 continue
75 }
76
77 // Get the kind of the referencing event
78 // For now, use the first kind in the filter (assumes single kind queries)
79 // TODO: Look up actual event kind from index if needed
80 if len(kinds) > 0 {
81 result.AddInboundRef(kinds[0], eventIDHex, refEventIDHex)
82 }
83 }
84 }
85
86 log.D.F("AddInboundRefsToResult: collected refs for %d target events", len(targetEventIDs))
87
88 return nil
89 }
90
91 // AddOutboundRefsToResult collects outbound references (events referenced by discovered items).
92 //
93 // For example, find all events that posts by users at depth 1 reference (quoted posts, replied-to posts).
94 func (d *D) AddOutboundRefsToResult(result *GraphResult, depth int, kinds []uint16) error {
95 // Determine source events
96 var sourceEventIDs []string
97
98 if depth == 0 {
99 sourceEventIDs = result.GetAllEvents()
100 } else {
101 sourceEventIDs = result.GetEventsAtDepth(depth)
102 }
103
104 // Also include events authored by pubkeys in result
105 pubkeys := result.GetAllPubkeys()
106 for _, pubkeyHex := range pubkeys {
107 pubkeySerial, err := d.PubkeyHexToSerial(pubkeyHex)
108 if err != nil {
109 continue
110 }
111
112 authoredEvents, err := d.GetEventsByAuthor(pubkeySerial, kinds)
113 if err != nil {
114 continue
115 }
116
117 for _, eventSerial := range authoredEvents {
118 eventIDHex, err := d.GetEventIDFromSerial(eventSerial)
119 if err != nil {
120 continue
121 }
122 if !result.HasEvent(eventIDHex) {
123 sourceEventIDs = append(sourceEventIDs, eventIDHex)
124 }
125 }
126 }
127
128 // For each source event, find referenced events
129 for _, eventIDHex := range sourceEventIDs {
130 eventSerial, err := d.EventIDHexToSerial(eventIDHex)
131 if err != nil {
132 continue
133 }
134
135 refSerials, err := d.GetETagsFromEventSerial(eventSerial)
136 if err != nil {
137 continue
138 }
139
140 for _, refSerial := range refSerials {
141 refEventIDHex, err := d.GetEventIDFromSerial(refSerial)
142 if err != nil {
143 continue
144 }
145
146 // Use first kind for categorization
147 if len(kinds) > 0 {
148 result.AddOutboundRef(kinds[0], eventIDHex, refEventIDHex)
149 }
150 }
151 }
152
153 log.D.F("AddOutboundRefsToResult: collected refs from %d source events", len(sourceEventIDs))
154
155 return nil
156 }
157
158 // CollectRefsForPubkeys collects inbound references to events by specific pubkeys.
159 // This is useful for "find all reactions to posts by these users" queries.
160 //
161 // Parameters:
162 // - pubkeySerials: The pubkeys whose events should be checked for refs
163 // - refKinds: Event kinds to collect (e.g., [7] for reactions)
164 // - eventKinds: Event kinds to check for refs (e.g., [1] for notes)
165 func (d *D) CollectRefsForPubkeys(
166 pubkeySerials []*types.Uint40,
167 refKinds []uint16,
168 eventKinds []uint16,
169 ) (*GraphResult, error) {
170 result := NewGraphResult()
171
172 for _, pubkeySerial := range pubkeySerials {
173 // Get events by this author
174 authoredEvents, err := d.GetEventsByAuthor(pubkeySerial, eventKinds)
175 if err != nil {
176 continue
177 }
178
179 for _, eventSerial := range authoredEvents {
180 eventIDHex, err := d.GetEventIDFromSerial(eventSerial)
181 if err != nil {
182 continue
183 }
184
185 // Find refs to this event
186 refSerials, err := d.GetReferencingEvents(eventSerial, refKinds)
187 if err != nil {
188 continue
189 }
190
191 for _, refSerial := range refSerials {
192 refEventIDHex, err := d.GetEventIDFromSerial(refSerial)
193 if err != nil {
194 continue
195 }
196
197 // Add to result
198 if len(refKinds) > 0 {
199 result.AddInboundRef(refKinds[0], eventIDHex, refEventIDHex)
200 }
201 }
202 }
203 }
204
205 return result, nil
206 }
207