You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
179 lines
4.4 KiB
179 lines
4.4 KiB
//go:build !(js && wasm) |
|
|
|
package bbolt |
|
|
|
import ( |
|
"bytes" |
|
"errors" |
|
|
|
bolt "go.etcd.io/bbolt" |
|
"next.orly.dev/pkg/database" |
|
"next.orly.dev/pkg/database/indexes/types" |
|
"next.orly.dev/pkg/interfaces/store" |
|
"git.mleku.dev/mleku/nostr/encoders/event" |
|
"git.mleku.dev/mleku/nostr/encoders/tag" |
|
) |
|
|
|
// GetSerialById gets the serial for an event ID. |
|
func (b *B) GetSerialById(id []byte) (ser *types.Uint40, err error) { |
|
if len(id) < 8 { |
|
return nil, errors.New("bbolt: invalid event ID length") |
|
} |
|
|
|
idHash := hashEventId(id) |
|
|
|
err = b.db.View(func(tx *bolt.Tx) error { |
|
bucket := tx.Bucket(bucketEid) |
|
if bucket == nil { |
|
return errors.New("id not found in database") |
|
} |
|
|
|
// Scan for matching ID hash prefix |
|
c := bucket.Cursor() |
|
for k, _ := c.Seek(idHash); k != nil && bytes.HasPrefix(k, idHash); k, _ = c.Next() { |
|
// Key format: id_hash(8) | serial(5) |
|
if len(k) >= 13 { |
|
ser = new(types.Uint40) |
|
ser.Set(decodeUint40(k[8:13])) |
|
return nil |
|
} |
|
} |
|
return errors.New("id not found in database") |
|
}) |
|
|
|
return |
|
} |
|
|
|
// GetSerialsByIds gets serials for multiple event IDs. |
|
func (b *B) GetSerialsByIds(ids *tag.T) (serials map[string]*types.Uint40, err error) { |
|
serials = make(map[string]*types.Uint40, ids.Len()) |
|
|
|
if ids == nil || ids.Len() == 0 { |
|
return |
|
} |
|
|
|
err = b.db.View(func(tx *bolt.Tx) error { |
|
bucket := tx.Bucket(bucketEid) |
|
if bucket == nil { |
|
return nil |
|
} |
|
|
|
// Iterate over the tag entries using the .T field |
|
for _, id := range ids.T { |
|
if len(id) < 8 { |
|
continue |
|
} |
|
|
|
idHash := hashEventId(id) |
|
c := bucket.Cursor() |
|
for k, _ := c.Seek(idHash); k != nil && bytes.HasPrefix(k, idHash); k, _ = c.Next() { |
|
if len(k) >= 13 { |
|
ser := new(types.Uint40) |
|
ser.Set(decodeUint40(k[8:13])) |
|
serials[string(id)] = ser |
|
break |
|
} |
|
} |
|
} |
|
return nil |
|
}) |
|
|
|
return |
|
} |
|
|
|
// GetSerialsByIdsWithFilter gets serials with a filter function. |
|
func (b *B) GetSerialsByIdsWithFilter(ids *tag.T, fn func(ev *event.E, ser *types.Uint40) bool) (serials map[string]*types.Uint40, err error) { |
|
// For now, just call GetSerialsByIds - full implementation would apply filter |
|
return b.GetSerialsByIds(ids) |
|
} |
|
|
|
// GetSerialsByRange gets serials within a key range. |
|
func (b *B) GetSerialsByRange(idx database.Range) (serials types.Uint40s, err error) { |
|
if len(idx.Start) < 3 { |
|
return nil, errors.New("bbolt: invalid range start") |
|
} |
|
|
|
// Extract bucket name from prefix |
|
bucketName := idx.Start[:3] |
|
startKey := idx.Start[3:] |
|
endKey := idx.End[3:] |
|
|
|
err = b.db.View(func(tx *bolt.Tx) error { |
|
bucket := tx.Bucket(bucketName) |
|
if bucket == nil { |
|
return nil |
|
} |
|
|
|
c := bucket.Cursor() |
|
for k, _ := c.Seek(startKey); k != nil; k, _ = c.Next() { |
|
// Check if we've passed the end |
|
if len(endKey) > 0 && bytes.Compare(k, endKey) >= 0 { |
|
break |
|
} |
|
|
|
// Extract serial from end of key (last 5 bytes) |
|
if len(k) >= 5 { |
|
ser := new(types.Uint40) |
|
ser.Set(decodeUint40(k[len(k)-5:])) |
|
serials = append(serials, ser) |
|
} |
|
} |
|
return nil |
|
}) |
|
|
|
return |
|
} |
|
|
|
// GetFullIdPubkeyBySerial gets full event ID and pubkey by serial. |
|
func (b *B) GetFullIdPubkeyBySerial(ser *types.Uint40) (fidpk *store.IdPkTs, err error) { |
|
if ser == nil { |
|
return nil, errors.New("bbolt: nil serial") |
|
} |
|
|
|
serial := ser.Get() |
|
key := makeSerialKey(serial) |
|
|
|
err = b.db.View(func(tx *bolt.Tx) error { |
|
// Get full ID/pubkey from fpc bucket |
|
fpcBucket := tx.Bucket(bucketFpc) |
|
if fpcBucket == nil { |
|
return errors.New("bbolt: fpc bucket not found") |
|
} |
|
|
|
// Scan for matching serial prefix |
|
c := fpcBucket.Cursor() |
|
for k, _ := c.Seek(key); k != nil && bytes.HasPrefix(k, key); k, _ = c.Next() { |
|
// Key format: serial(5) | id(32) | pubkey_hash(8) | created_at(8) |
|
if len(k) >= 53 { |
|
fidpk = &store.IdPkTs{ |
|
Ser: serial, |
|
} |
|
fidpk.Id = make([]byte, 32) |
|
copy(fidpk.Id, k[5:37]) |
|
// Pubkey is only hash here, need to look up full pubkey |
|
// For now return what we have |
|
fidpk.Pub = make([]byte, 8) |
|
copy(fidpk.Pub, k[37:45]) |
|
fidpk.Ts = int64(decodeUint64(k[45:53])) |
|
return nil |
|
} |
|
} |
|
return errors.New("bbolt: serial not found in fpc index") |
|
}) |
|
|
|
return |
|
} |
|
|
|
// GetFullIdPubkeyBySerials gets full event IDs and pubkeys for multiple serials. |
|
func (b *B) GetFullIdPubkeyBySerials(sers []*types.Uint40) (fidpks []*store.IdPkTs, err error) { |
|
fidpks = make([]*store.IdPkTs, 0, len(sers)) |
|
|
|
for _, ser := range sers { |
|
fidpk, e := b.GetFullIdPubkeyBySerial(ser) |
|
if e == nil && fidpk != nil { |
|
fidpks = append(fidpks, fidpk) |
|
} |
|
} |
|
|
|
return |
|
}
|
|
|