package neo4j import ( "context" "fmt" "next.orly.dev/pkg/database" "next.orly.dev/pkg/database/indexes/types" "git.mleku.dev/mleku/nostr/encoders/event" "git.mleku.dev/mleku/nostr/encoders/hex" "git.mleku.dev/mleku/nostr/encoders/tag" "next.orly.dev/pkg/interfaces/store" ) // FetchEventBySerial retrieves an event by its serial number func (n *N) FetchEventBySerial(ser *types.Uint40) (ev *event.E, err error) { serial := ser.Get() cypher := ` MATCH (e:Event {serial: $serial}) RETURN e.id AS id, e.kind AS kind, e.created_at AS created_at, e.content AS content, e.sig AS sig, e.pubkey AS pubkey, e.tags AS tags` params := map[string]any{"serial": int64(serial)} result, err := n.ExecuteRead(context.Background(), cypher, params) if err != nil { return nil, fmt.Errorf("failed to fetch event by serial: %w", err) } evs, err := n.parseEventsFromResult(result) if err != nil { return nil, err } if len(evs) == 0 { return nil, fmt.Errorf("event not found") } return evs[0], nil } // FetchEventsBySerials retrieves multiple events by their serial numbers func (n *N) FetchEventsBySerials(serials []*types.Uint40) ( events map[uint64]*event.E, err error, ) { if len(serials) == 0 { return make(map[uint64]*event.E), nil } // Build list of serial numbers serialNums := make([]int64, len(serials)) for i, ser := range serials { serialNums[i] = int64(ser.Get()) } cypher := ` MATCH (e:Event) WHERE e.serial IN $serials RETURN e.id AS id, e.kind AS kind, e.created_at AS created_at, e.content AS content, e.sig AS sig, e.pubkey AS pubkey, e.tags AS tags, e.serial AS serial` params := map[string]any{"serials": serialNums} result, err := n.ExecuteRead(context.Background(), cypher, params) if err != nil { return nil, fmt.Errorf("failed to fetch events by serials: %w", err) } // Parse events and map by serial events = make(map[uint64]*event.E) ctx := context.Background() for result.Next(ctx) { record := result.Record() if record == nil { continue } // Parse event idRaw, _ := record.Get("id") kindRaw, _ := record.Get("kind") createdAtRaw, _ := record.Get("created_at") contentRaw, _ := record.Get("content") sigRaw, _ := record.Get("sig") pubkeyRaw, _ := record.Get("pubkey") tagsRaw, _ := record.Get("tags") serialRaw, _ := record.Get("serial") idStr, _ := idRaw.(string) kind, _ := kindRaw.(int64) createdAt, _ := createdAtRaw.(int64) content, _ := contentRaw.(string) sigStr, _ := sigRaw.(string) pubkeyStr, _ := pubkeyRaw.(string) tagsStr, _ := tagsRaw.(string) serialVal, _ := serialRaw.(int64) id, err := hex.Dec(idStr) if err != nil { continue } sig, err := hex.Dec(sigStr) if err != nil { continue } pubkey, err := hex.Dec(pubkeyStr) if err != nil { continue } tags := tag.NewS() if tagsStr != "" { _ = tags.UnmarshalJSON([]byte(tagsStr)) } e := &event.E{ Kind: uint16(kind), CreatedAt: createdAt, Content: []byte(content), Tags: tags, } copy(e.ID[:], id) copy(e.Sig[:], sig) copy(e.Pubkey[:], pubkey) events[uint64(serialVal)] = e } return events, nil } // GetSerialById retrieves the serial number for an event ID func (n *N) GetSerialById(id []byte) (ser *types.Uint40, err error) { idStr := hex.Enc(id) cypher := "MATCH (e:Event {id: $id}) RETURN e.serial AS serial" params := map[string]any{"id": idStr} result, err := n.ExecuteRead(context.Background(), cypher, params) if err != nil { return nil, fmt.Errorf("failed to get serial by ID: %w", err) } ctx := context.Background() if result.Next(ctx) { record := result.Record() if record != nil { serialRaw, found := record.Get("serial") if found { if serialVal, ok := serialRaw.(int64); ok { ser = &types.Uint40{} ser.Set(uint64(serialVal)) return ser, nil } } } } return nil, fmt.Errorf("event not found") } // GetSerialsByIds retrieves serial numbers for multiple event IDs func (n *N) GetSerialsByIds(ids *tag.T) ( serials map[string]*types.Uint40, err error, ) { serials = make(map[string]*types.Uint40) if len(ids.T) == 0 { return serials, nil } // Extract ID strings idStrs := make([]string, 0, len(ids.T)) for _, idTag := range ids.T { if len(idTag) >= 2 { idStrs = append(idStrs, string(idTag[1])) } } if len(idStrs) == 0 { return serials, nil } cypher := ` MATCH (e:Event) WHERE e.id IN $ids RETURN e.id AS id, e.serial AS serial` params := map[string]any{"ids": idStrs} result, err := n.ExecuteRead(context.Background(), cypher, params) if err != nil { return nil, fmt.Errorf("failed to get serials by IDs: %w", err) } ctx := context.Background() for result.Next(ctx) { record := result.Record() if record == nil { continue } idRaw, found := record.Get("id") if !found { continue } serialRaw, found := record.Get("serial") if !found { continue } idStr, _ := idRaw.(string) serialVal, _ := serialRaw.(int64) serial := &types.Uint40{} serial.Set(uint64(serialVal)) serials[idStr] = serial } return serials, nil } // GetSerialsByIdsWithFilter retrieves serials with a filter function func (n *N) GetSerialsByIdsWithFilter( ids *tag.T, fn func(ev *event.E, ser *types.Uint40) bool, ) (serials map[string]*types.Uint40, err error) { serials = make(map[string]*types.Uint40) if fn == nil { // No filter, just return all return n.GetSerialsByIds(ids) } // With filter, need to fetch events for _, idTag := range ids.T { if len(idTag) < 2 { continue } idBytes, err := hex.Dec(string(idTag[1])) if err != nil { continue } serial, err := n.GetSerialById(idBytes) if err != nil { continue } ev, err := n.FetchEventBySerial(serial) if err != nil { continue } if fn(ev, serial) { serials[string(idTag[1])] = serial } } return serials, nil } // GetSerialsByRange retrieves serials within a range func (n *N) GetSerialsByRange(idx database.Range) ( serials types.Uint40s, err error, ) { // This would need to be implemented based on how ranges are defined // For now, returning not implemented err = fmt.Errorf("not implemented") return } // GetFullIdPubkeyBySerial retrieves ID and pubkey for a serial number func (n *N) GetFullIdPubkeyBySerial(ser *types.Uint40) ( fidpk *store.IdPkTs, err error, ) { serial := ser.Get() cypher := ` MATCH (e:Event {serial: $serial}) RETURN e.id AS id, e.pubkey AS pubkey, e.created_at AS created_at` params := map[string]any{"serial": int64(serial)} result, err := n.ExecuteRead(context.Background(), cypher, params) if err != nil { return nil, fmt.Errorf("failed to get ID and pubkey by serial: %w", err) } ctx := context.Background() if result.Next(ctx) { record := result.Record() if record != nil { idRaw, found := record.Get("id") if !found { return nil, fmt.Errorf("event not found") } pubkeyRaw, found := record.Get("pubkey") if !found { return nil, fmt.Errorf("event not found") } createdAtRaw, found := record.Get("created_at") if !found { return nil, fmt.Errorf("event not found") } idStr, _ := idRaw.(string) pubkeyStr, _ := pubkeyRaw.(string) createdAt, _ := createdAtRaw.(int64) id, err := hex.Dec(idStr) if err != nil { return nil, err } pubkey, err := hex.Dec(pubkeyStr) if err != nil { return nil, err } fidpk = &store.IdPkTs{ Id: id, Pub: pubkey, Ts: createdAt, Ser: serial, } return fidpk, nil } } return nil, fmt.Errorf("event not found") } // GetFullIdPubkeyBySerials retrieves IDs and pubkeys for multiple serials func (n *N) GetFullIdPubkeyBySerials(sers []*types.Uint40) ( fidpks []*store.IdPkTs, err error, ) { fidpks = make([]*store.IdPkTs, 0, len(sers)) if len(sers) == 0 { return fidpks, nil } // Build list of serial numbers serialNums := make([]int64, len(sers)) for i, ser := range sers { serialNums[i] = int64(ser.Get()) } cypher := ` MATCH (e:Event) WHERE e.serial IN $serials RETURN e.id AS id, e.pubkey AS pubkey, e.created_at AS created_at, e.serial AS serial` params := map[string]any{"serials": serialNums} result, err := n.ExecuteRead(context.Background(), cypher, params) if err != nil { return nil, fmt.Errorf("failed to get IDs and pubkeys by serials: %w", err) } ctx := context.Background() for result.Next(ctx) { record := result.Record() if record == nil { continue } idRaw, found := record.Get("id") if !found { continue } pubkeyRaw, found := record.Get("pubkey") if !found { continue } createdAtRaw, found := record.Get("created_at") if !found { continue } serialRaw, found := record.Get("serial") if !found { continue } idStr, _ := idRaw.(string) pubkeyStr, _ := pubkeyRaw.(string) createdAt, _ := createdAtRaw.(int64) serialVal, _ := serialRaw.(int64) id, err := hex.Dec(idStr) if err != nil { continue } pubkey, err := hex.Dec(pubkeyStr) if err != nil { continue } fidpks = append(fidpks, &store.IdPkTs{ Id: id, Pub: pubkey, Ts: createdAt, Ser: uint64(serialVal), }) } return fidpks, nil }