Files
eventstore/bluge/query.go
2024-05-27 18:19:35 -03:00

118 lines
2.5 KiB
Go

package bluge
import (
"context"
"encoding/hex"
"fmt"
"strconv"
"github.com/blugelabs/bluge"
"github.com/blugelabs/bluge/search"
"github.com/nbd-wtf/go-nostr"
)
func (b *BlugeBackend) QueryEvents(ctx context.Context, filter nostr.Filter) (chan *nostr.Event, error) {
ch := make(chan *nostr.Event)
if len(filter.Search) < 2 {
close(ch)
return ch, nil
}
reader, err := b.writer.Reader()
if err != nil {
close(ch)
return nil, fmt.Errorf("unable to open reader: %w", err)
}
searchQ := bluge.NewMatchQuery(filter.Search)
searchQ.SetField(contentField)
var q bluge.Query = searchQ
complicatedQuery := bluge.NewBooleanQuery().AddMust(searchQ)
if len(filter.Kinds) > 0 {
eitherKind := bluge.NewBooleanQuery()
eitherKind.SetMinShould(1)
for _, kind := range filter.Kinds {
kindQ := bluge.NewTermQuery(strconv.Itoa(kind))
kindQ.SetField(kindField)
eitherKind.AddShould(kindQ)
}
complicatedQuery.AddMust(eitherKind)
q = complicatedQuery
}
if len(filter.Authors) > 0 {
eitherPubkey := bluge.NewBooleanQuery()
eitherPubkey.SetMinShould(1)
for _, pubkey := range filter.Authors {
if len(pubkey) != 64 {
continue
}
pubkeyQ := bluge.NewTermQuery(pubkey[56:])
pubkeyQ.SetField(pubkeyField)
eitherPubkey.AddShould(pubkeyQ)
}
complicatedQuery.AddMust(eitherPubkey)
q = complicatedQuery
}
if filter.Since != nil || filter.Until != nil {
min := 0.0
if filter.Since != nil {
min = float64(*filter.Since)
}
max := float64(nostr.Now())
if filter.Until != nil {
max = float64(*filter.Until)
}
dateRangeQ := bluge.NewNumericRangeInclusiveQuery(min, max, true, true)
dateRangeQ.SetField(createdAtField)
complicatedQuery.AddMust(dateRangeQ)
q = complicatedQuery
}
limit := 40
if filter.Limit != 0 {
limit = filter.Limit
if filter.Limit > 150 {
limit = 150
}
}
req := bluge.NewTopNSearch(limit, q)
dmi, err := reader.Search(context.Background(), req)
if err != nil {
close(ch)
reader.Close()
return ch, fmt.Errorf("error executing search: %w", err)
}
go func() {
defer reader.Close()
defer close(ch)
var next *search.DocumentMatch
for next, err = dmi.Next(); next != nil; next, err = dmi.Next() {
next.VisitStoredFields(func(field string, value []byte) bool {
id := hex.EncodeToString(value)
rawch, err := b.RawEventStore.QueryEvents(ctx, nostr.Filter{IDs: []string{id}})
if err != nil {
return false
}
for evt := range rawch {
ch <- evt
}
return false
})
}
if err != nil {
return
}
}()
return ch, nil
}