diff --git a/sdk/hints/badgerh/db.go b/sdk/hints/badgerh/db.go index bedb429..6936101 100644 --- a/sdk/hints/badgerh/db.go +++ b/sdk/hints/badgerh/db.go @@ -112,6 +112,59 @@ func (bh *BadgerHints) TopN(pubkey string, n int) []string { return result } +func (bh *BadgerHints) GetDetailedScores(pubkey string, n int) []hints.RelayScores { + type relayScore struct { + relay string + tss timestamps + score int64 + } + + scores := make([]relayScore, 0, n) + err := bh.db.View(func(txn *badger.Txn) error { + prefix, _ := hex.DecodeString(pubkey) + it := txn.NewIterator(badger.DefaultIteratorOptions) + defer it.Close() + + for it.Seek(prefix); it.ValidForPrefix(prefix); it.Next() { + item := it.Item() + k := item.Key() + relay := string(k[32:]) + + var tss timestamps + err := item.Value(func(v []byte) error { + tss = parseValue(v) + return nil + }) + if err != nil { + return err + } + + scores = append(scores, relayScore{relay, tss, tss.sum()}) + } + return nil + }) + if err != nil { + return nil + } + + slices.SortFunc(scores, func(a, b relayScore) int { + return int(b.score - a.score) + }) + + result := make([]hints.RelayScores, 0, n) + for i, rs := range scores { + if i >= n { + break + } + result = append(result, hints.RelayScores{ + Relay: rs.relay, + Scores: rs.tss, + Sum: rs.score, + }) + } + return result +} + func (bh *BadgerHints) PrintScores() { fmt.Println("= print scores") diff --git a/sdk/hints/interface.go b/sdk/hints/interface.go index 048897c..bb5c523 100644 --- a/sdk/hints/interface.go +++ b/sdk/hints/interface.go @@ -2,8 +2,15 @@ package hints import "github.com/nbd-wtf/go-nostr" +type RelayScores struct { + Relay string + Scores [4]nostr.Timestamp + Sum int64 +} + type HintsDB interface { TopN(pubkey string, n int) []string Save(pubkey string, relay string, key HintKey, score nostr.Timestamp) PrintScores() + GetDetailedScores(pubkey string, n int) []RelayScores } diff --git a/sdk/hints/lmdbh/db.go b/sdk/hints/lmdbh/db.go index 4163b37..15fcaa9 100644 --- a/sdk/hints/lmdbh/db.go +++ b/sdk/hints/lmdbh/db.go @@ -182,6 +182,62 @@ func (lh *LMDBHints) PrintScores() { } } +func (lh *LMDBHints) GetDetailedScores(pubkey string, n int) []hints.RelayScores { + type relayScore struct { + relay string + tss timestamps + score int64 + } + + scores := make([]relayScore, 0, n) + err := lh.env.View(func(txn *lmdb.Txn) error { + txn.RawRead = true + + cursor, err := txn.OpenCursor(lh.dbi) + if err != nil { + return err + } + defer cursor.Close() + + prefix, _ := hex.DecodeString(pubkey) + k, v, err := cursor.Get(prefix, nil, lmdb.SetRange) + for ; err == nil; k, v, err = cursor.Get(nil, nil, lmdb.Next) { + // check if we're still in the prefix range + if len(k) < 32 || !bytes.Equal(k[:32], prefix) { + break + } + + relay := string(k[32:]) + tss := parseValue(v) + scores = append(scores, relayScore{relay, tss, tss.sum()}) + } + if err != nil && !lmdb.IsNotFound(err) { + return err + } + return nil + }) + if err != nil { + return nil + } + + slices.SortFunc(scores, func(a, b relayScore) int { + return int(b.score - a.score) + }) + + result := make([]hints.RelayScores, 0, n) + for i, rs := range scores { + if i >= n { + break + } + result = append(result, hints.RelayScores{ + Relay: rs.relay, + Scores: rs.tss, + Sum: rs.score, + }) + } + return result +} + type timestamps [4]nostr.Timestamp func (tss timestamps) sum() int64 { diff --git a/sdk/hints/memoryh/db.go b/sdk/hints/memoryh/db.go index 496d1e2..c840e5e 100644 --- a/sdk/hints/memoryh/db.go +++ b/sdk/hints/memoryh/db.go @@ -108,6 +108,31 @@ func (db *HintDB) PrintScores() { } } +func (db *HintDB) GetDetailedScores(pubkey string, n int) []hints.RelayScores { + db.Lock() + defer db.Unlock() + + result := make([]hints.RelayScores, 0, n) + if rfpk, ok := db.OrderedRelaysByPubKey[pubkey]; ok { + // sort everything from scratch + slices.SortFunc(rfpk.Entries, func(a, b RelayEntry) int { + return int(b.Sum() - a.Sum()) + }) + + for i, re := range rfpk.Entries { + if i >= n { + break + } + result = append(result, hints.RelayScores{ + Relay: db.RelayBySerial[re.Relay], + Scores: re.Timestamps, + Sum: re.Sum(), + }) + } + } + return result +} + type RelaysForPubKey struct { Entries []RelayEntry } diff --git a/sdk/hints/sqlh/db.go b/sdk/hints/sqlh/db.go index ee619cf..491ae18 100644 --- a/sdk/hints/sqlh/db.go +++ b/sdk/hints/sqlh/db.go @@ -209,6 +209,40 @@ func (sh SQLHints) PrintScores() { } } +func (sh SQLHints) GetDetailedScores(pubkey string, n int) []hints.RelayScores { + result := make([]hints.RelayScores, 0, n) + + rows, err := sh.Queryx( + `SELECT relay, last_fetch_attempt, most_recent_event_fetched, last_in_relay_list, last_in_hint, + coalesce(`+sh.scorePartialQuery()+`, 0) AS score + FROM nostr_sdk_pubkey_relays + WHERE pubkey = `+sh.interop.generateBindingSpots(0, 1)+` + ORDER BY score DESC + LIMIT `+sh.interop.generateBindingSpots(1, 1), + pubkey, n) + if err != nil { + return nil + } + defer rows.Close() + + for rows.Next() { + var rs hints.RelayScores + var scores [4]sql.NullInt64 + err := rows.Scan(&rs.Relay, &scores[0], &scores[1], &scores[2], &scores[3], &rs.Sum) + if err != nil { + continue + } + for i, s := range scores { + if s.Valid { + rs.Scores[i] = nostr.Timestamp(s.Int64) + } + } + result = append(result, rs) + } + + return result +} + func (sh SQLHints) scorePartialQuery() string { calc := strings.Builder{} calc.Grow(len(hints.KeyBasePoints) * (11 + 25 + 32 + 4 + 4 + 9 + 12 + 25 + 12 + 25 + 19 + 3))