Files
next.orly.dev/pkg/database/graph-refs.go
mleku 6b98c23606
Some checks failed
Go / build-and-release (push) Has been cancelled
add first draft graph query implementation
2025-12-04 09:28:13 +00:00

207 lines
5.5 KiB
Go

//go:build !(js && wasm)
package database
import (
"lol.mleku.dev/log"
"next.orly.dev/pkg/database/indexes/types"
)
// AddInboundRefsToResult collects inbound references (events that reference discovered items)
// for events at a specific depth in the result.
//
// For example, if you have a follows graph result and want to find all kind-7 reactions
// to posts by users at depth 1, this collects those reactions and adds them to result.InboundRefs.
//
// Parameters:
// - result: The graph result to augment with ref data
// - depth: The depth at which to collect refs (0 = all depths)
// - kinds: Event kinds to collect (e.g., [7] for reactions, [6] for reposts)
func (d *D) AddInboundRefsToResult(result *GraphResult, depth int, kinds []uint16) error {
// Determine which events to find refs for
var targetEventIDs []string
if depth == 0 {
// Collect for all depths
targetEventIDs = result.GetAllEvents()
} else {
targetEventIDs = result.GetEventsAtDepth(depth)
}
// Also collect refs for events authored by pubkeys in the result
// This is common for "find reactions to posts by my follows" queries
pubkeys := result.GetAllPubkeys()
for _, pubkeyHex := range pubkeys {
pubkeySerial, err := d.PubkeyHexToSerial(pubkeyHex)
if err != nil {
continue
}
// Get events authored by this pubkey
// For efficiency, limit to relevant event kinds that might have reactions
authoredEvents, err := d.GetEventsByAuthor(pubkeySerial, []uint16{1, 30023}) // notes and articles
if err != nil {
continue
}
for _, eventSerial := range authoredEvents {
eventIDHex, err := d.GetEventIDFromSerial(eventSerial)
if err != nil {
continue
}
// Add to target list if not already tracking
if !result.HasEvent(eventIDHex) {
targetEventIDs = append(targetEventIDs, eventIDHex)
}
}
}
// For each target event, find referencing events
for _, eventIDHex := range targetEventIDs {
eventSerial, err := d.EventIDHexToSerial(eventIDHex)
if err != nil {
continue
}
refSerials, err := d.GetReferencingEvents(eventSerial, kinds)
if err != nil {
continue
}
for _, refSerial := range refSerials {
refEventIDHex, err := d.GetEventIDFromSerial(refSerial)
if err != nil {
continue
}
// Get the kind of the referencing event
// For now, use the first kind in the filter (assumes single kind queries)
// TODO: Look up actual event kind from index if needed
if len(kinds) > 0 {
result.AddInboundRef(kinds[0], eventIDHex, refEventIDHex)
}
}
}
log.D.F("AddInboundRefsToResult: collected refs for %d target events", len(targetEventIDs))
return nil
}
// AddOutboundRefsToResult collects outbound references (events referenced by discovered items).
//
// For example, find all events that posts by users at depth 1 reference (quoted posts, replied-to posts).
func (d *D) AddOutboundRefsToResult(result *GraphResult, depth int, kinds []uint16) error {
// Determine source events
var sourceEventIDs []string
if depth == 0 {
sourceEventIDs = result.GetAllEvents()
} else {
sourceEventIDs = result.GetEventsAtDepth(depth)
}
// Also include events authored by pubkeys in result
pubkeys := result.GetAllPubkeys()
for _, pubkeyHex := range pubkeys {
pubkeySerial, err := d.PubkeyHexToSerial(pubkeyHex)
if err != nil {
continue
}
authoredEvents, err := d.GetEventsByAuthor(pubkeySerial, kinds)
if err != nil {
continue
}
for _, eventSerial := range authoredEvents {
eventIDHex, err := d.GetEventIDFromSerial(eventSerial)
if err != nil {
continue
}
if !result.HasEvent(eventIDHex) {
sourceEventIDs = append(sourceEventIDs, eventIDHex)
}
}
}
// For each source event, find referenced events
for _, eventIDHex := range sourceEventIDs {
eventSerial, err := d.EventIDHexToSerial(eventIDHex)
if err != nil {
continue
}
refSerials, err := d.GetETagsFromEventSerial(eventSerial)
if err != nil {
continue
}
for _, refSerial := range refSerials {
refEventIDHex, err := d.GetEventIDFromSerial(refSerial)
if err != nil {
continue
}
// Use first kind for categorization
if len(kinds) > 0 {
result.AddOutboundRef(kinds[0], eventIDHex, refEventIDHex)
}
}
}
log.D.F("AddOutboundRefsToResult: collected refs from %d source events", len(sourceEventIDs))
return nil
}
// CollectRefsForPubkeys collects inbound references to events by specific pubkeys.
// This is useful for "find all reactions to posts by these users" queries.
//
// Parameters:
// - pubkeySerials: The pubkeys whose events should be checked for refs
// - refKinds: Event kinds to collect (e.g., [7] for reactions)
// - eventKinds: Event kinds to check for refs (e.g., [1] for notes)
func (d *D) CollectRefsForPubkeys(
pubkeySerials []*types.Uint40,
refKinds []uint16,
eventKinds []uint16,
) (*GraphResult, error) {
result := NewGraphResult()
for _, pubkeySerial := range pubkeySerials {
// Get events by this author
authoredEvents, err := d.GetEventsByAuthor(pubkeySerial, eventKinds)
if err != nil {
continue
}
for _, eventSerial := range authoredEvents {
eventIDHex, err := d.GetEventIDFromSerial(eventSerial)
if err != nil {
continue
}
// Find refs to this event
refSerials, err := d.GetReferencingEvents(eventSerial, refKinds)
if err != nil {
continue
}
for _, refSerial := range refSerials {
refEventIDHex, err := d.GetEventIDFromSerial(refSerial)
if err != nil {
continue
}
// Add to result
if len(refKinds) > 0 {
result.AddInboundRef(refKinds[0], eventIDHex, refEventIDHex)
}
}
}
}
return result, nil
}