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