This commit is contained in:
Daniel Ponte 2024-08-06 19:46:01 -04:00
parent 7879715ce8
commit d4dc80d116
6 changed files with 78 additions and 77 deletions

View file

@ -1,9 +1,9 @@
package calls package calls
import ( import (
"cmp" "context"
"encoding/json"
"slices" "dynatron.me/x/stillbox/pkg/gordio/database"
) )
type FilterQuery struct { type FilterQuery struct {
@ -29,23 +29,6 @@ type Filter struct {
TalkgroupTagsNot []string `json:"talkgroupTagsNot,omitempty"` TalkgroupTagsNot []string `json:"talkgroupTagsNot,omitempty"`
talkgroups map[Talkgroup]bool talkgroups map[Talkgroup]bool
talkgroupTagsAll map[string]bool
talkgroupTagsAny map[string]bool
talkgroupTagsNot map[string]bool
query *FilterQuery
}
func queryParams(q string, p ...any) FilterQuery {
return FilterQuery{Query: q, Params: p}
}
func (f *Filter) filterQuery() *FilterQuery {
fq := queryParams(
`((talkgroups.id = ANY(?) OR talkgroups.tags @> ARRAY[?]) OR (talkgroups.tags && ARRAY[?])) AND (talkgroups.id != ANY(?) AND NOT talkgroups.tags @> ARRAY[?])`,
f.Talkgroups, f.TalkgroupTagsAny, f.TalkgroupTagsAll, f.TalkgroupsNot, f.TalkgroupTagsNot)
return &fq
} }
func PackedTGs(tg []Talkgroup) []int64 { func PackedTGs(tg []Talkgroup) []int64 {
@ -58,71 +41,50 @@ func PackedTGs(tg []Talkgroup) []int64 {
return s return s
} }
func (f *Filter) compile() *Filter { func (f *Filter) hasTags() bool {
return len(f.TalkgroupTagsAny) > 0 || len(f.TalkgroupTagsAll) > 0 || len(f.TalkgroupTagsNot) > 0
}
func (f *Filter) GetFinalTalkgroups() map[Talkgroup]bool {
return f.talkgroups
}
func (f *Filter) compile(ctx context.Context) error {
f.talkgroups = make(map[Talkgroup]bool) f.talkgroups = make(map[Talkgroup]bool)
for _, tg := range f.Talkgroups { for _, tg := range f.Talkgroups {
f.talkgroups[tg] = true f.talkgroups[tg] = true
} }
if f.hasTags() { // don't bother with DB if no tags
db := database.FromCtx(ctx)
tagTGs, err := db.GetTalkgroupIDsByTags(ctx, f.TalkgroupTagsAny, f.TalkgroupTagsAll, f.TalkgroupTagsNot)
if err != nil {
return nil, err
}
for _, tg := range tagTGs {
f.talkgroups[Talkgroup{System: uint32(tg.SystemID), Talkgroup: uint32(tg.Tgid)}] = true
}
}
for _, tg := range f.TalkgroupsNot { for _, tg := range f.TalkgroupsNot {
f.talkgroups[tg] = false f.talkgroups[tg] = false
} }
f.talkgroupTagsAll = make(map[string]bool) return nil
for _, tag := range f.TalkgroupTagsAll {
f.talkgroupTagsAll[tag] = true
} }
f.talkgroupTagsAny = make(map[string]bool) func (f *Filter) Test(ctx context.Context, call *Call) bool {
for _, tag := range f.TalkgroupTagsAny { if f == nil { // no filter means all calls
f.talkgroupTagsAny[tag] = true return true
} }
for _, tag := range f.TalkgroupTagsNot { if f.talkgroups == nil {
f.talkgroupTagsNot[tag] = true err := f.compile(ctx)
}
f.query = f.filterQuery()
return f
}
func (f *Filter) normalize() {
tgSort := func(a, b Talkgroup) int {
if n := cmp.Compare(a.System, b.System); n != 0 {
return n
}
return cmp.Compare(a.Talkgroup, b.Talkgroup)
}
slices.SortFunc(f.Talkgroups, tgSort)
slices.SortFunc(f.TalkgroupsNot, tgSort)
slices.SortFunc(f.TalkgroupTagsAll, cmp.Compare)
slices.SortFunc(f.TalkgroupTagsAny, cmp.Compare)
slices.SortFunc(f.TalkgroupTagsNot, cmp.Compare)
}
func (f *Filter) cacheKey() string {
f.normalize()
buf, err := json.Marshal(f)
if err != nil { if err != nil {
panic(err) panic(err)
} }
return string(buf)
} }
func (f *Filter) Test(call *Call) bool {
return false return false
} }
type FilterCache struct {
cache map[string]Filter
}
func NewFilterCache() *FilterCache {
return &FilterCache{
cache: make(map[string]Filter),
}
}

View file

@ -69,8 +69,8 @@ type System struct {
type Talkgroup struct { type Talkgroup struct {
ID int64 `json:"id"` ID int64 `json:"id"`
SystemID int `json:"system_id"` SystemID int32 `json:"system_id"`
Tgid *int32 `json:"tgid"` Tgid int32 `json:"tgid"`
Name *string `json:"name"` Name *string `json:"name"`
TgGroup *string `json:"tg_group"` TgGroup *string `json:"tg_group"`
Frequency *int32 `json:"frequency"` Frequency *int32 `json:"frequency"`

View file

@ -19,6 +19,7 @@ type Querier interface {
DeleteAPIKey(ctx context.Context, apiKey string) error DeleteAPIKey(ctx context.Context, apiKey string) error
DeleteUser(ctx context.Context, username string) error DeleteUser(ctx context.Context, username string) error
GetAPIKey(ctx context.Context, apiKey string) (ApiKey, error) GetAPIKey(ctx context.Context, apiKey string) (ApiKey, error)
GetTalkgroupIDsByTags(ctx context.Context, anytags []string, alltags []string, nottags []string) ([]GetTalkgroupIDsByTagsRow, error)
GetTalkgroupTags(ctx context.Context, sys int, tg int) ([]string, error) GetTalkgroupTags(ctx context.Context, sys int, tg int) ([]string, error)
GetTalkgroupsWithAllTags(ctx context.Context, tags []string) ([]Talkgroup, error) GetTalkgroupsWithAllTags(ctx context.Context, tags []string) ([]Talkgroup, error)
GetTalkgroupsWithAnyTags(ctx context.Context, tags []string) ([]Talkgroup, error) GetTalkgroupsWithAnyTags(ctx context.Context, tags []string) ([]Talkgroup, error)

View file

@ -19,6 +19,38 @@ func (q *Queries) BulkSetTalkgroupTags(ctx context.Context, iD int64, tags []str
return err return err
} }
const getTalkgroupIDsByTags = `-- name: GetTalkgroupIDsByTags :many
SELECT system_id, tgid FROM talkgroups
WHERE (tags @> ARRAY[$1])
AND (tags && ARRAY[$2])
AND NOT (tags @> ARRAY[$3])
`
type GetTalkgroupIDsByTagsRow struct {
SystemID int32 `json:"system_id"`
Tgid int32 `json:"tgid"`
}
func (q *Queries) GetTalkgroupIDsByTags(ctx context.Context, anytags []string, alltags []string, nottags []string) ([]GetTalkgroupIDsByTagsRow, error) {
rows, err := q.db.Query(ctx, getTalkgroupIDsByTags, anytags, alltags, nottags)
if err != nil {
return nil, err
}
defer rows.Close()
var items []GetTalkgroupIDsByTagsRow
for rows.Next() {
var i GetTalkgroupIDsByTagsRow
if err := rows.Scan(&i.SystemID, &i.Tgid); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getTalkgroupTags = `-- name: GetTalkgroupTags :one const getTalkgroupTags = `-- name: GetTalkgroupTags :one
SELECT tags FROM talkgroups SELECT tags FROM talkgroups
WHERE id = systg2id($1, $2) WHERE id = systg2id($1, $2)

View file

@ -46,8 +46,8 @@ $$;
CREATE TABLE IF NOT EXISTS talkgroups( CREATE TABLE IF NOT EXISTS talkgroups(
id INT8 PRIMARY KEY, id INT8 PRIMARY KEY,
system_id INTEGER REFERENCES systems(id) NOT NULL GENERATED ALWAYS AS (id >> 32) STORED, system_id INT4 REFERENCES systems(id) NOT NULL GENERATED ALWAYS AS (id >> 32) STORED,
tgid INTEGER GENERATED ALWAYS AS (id & x'ffffffff'::BIGINT) STORED, tgid INT4 NOT NULL GENERATED ALWAYS AS (id & x'ffffffff'::BIGINT) STORED,
name TEXT, name TEXT,
tg_group TEXT, tg_group TEXT,
frequency INTEGER, frequency INTEGER,

View file

@ -6,6 +6,12 @@ WHERE tags @> ARRAY[$1];
SELECT * FROM talkgroups SELECT * FROM talkgroups
WHERE tags && ARRAY[$1]; WHERE tags && ARRAY[$1];
-- name: GetTalkgroupIDsByTags :many
SELECT system_id, tgid FROM talkgroups
WHERE (tags @> ARRAY[sqlc.arg(anyTags)])
AND (tags && ARRAY[sqlc.arg(allTags)])
AND NOT (tags @> ARRAY[sqlc.arg(notTags)]);
-- name: GetTalkgroupTags :one -- name: GetTalkgroupTags :one
SELECT tags FROM talkgroups SELECT tags FROM talkgroups
WHERE id = systg2id($1, $2); WHERE id = systg2id($1, $2);