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
import (
"cmp"
"encoding/json"
"slices"
"context"
"dynatron.me/x/stillbox/pkg/gordio/database"
)
type FilterQuery struct {
@ -28,24 +28,7 @@ type Filter struct {
TalkgroupTagsAny []string `json:"talkgroupTagsAny,omitempty"`
TalkgroupTagsNot []string `json:"talkgroupTagsNot,omitempty"`
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
talkgroups map[Talkgroup]bool
}
func PackedTGs(tg []Talkgroup) []int64 {
@ -58,71 +41,50 @@ func PackedTGs(tg []Talkgroup) []int64 {
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)
for _, tg := range f.Talkgroups {
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 {
f.talkgroups[tg] = false
}
f.talkgroupTagsAll = make(map[string]bool)
for _, tag := range f.TalkgroupTagsAll {
f.talkgroupTagsAll[tag] = true
}
f.talkgroupTagsAny = make(map[string]bool)
for _, tag := range f.TalkgroupTagsAny {
f.talkgroupTagsAny[tag] = true
}
for _, tag := range f.TalkgroupTagsNot {
f.talkgroupTagsNot[tag] = true
}
f.query = f.filterQuery()
return f
return nil
}
func (f *Filter) normalize() {
tgSort := func(a, b Talkgroup) int {
if n := cmp.Compare(a.System, b.System); n != 0 {
return n
func (f *Filter) Test(ctx context.Context, call *Call) bool {
if f == nil { // no filter means all calls
return true
}
if f.talkgroups == nil {
err := f.compile(ctx)
if err != nil {
panic(err)
}
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 {
panic(err)
}
return string(buf)
}
func (f *Filter) Test(call *Call) bool {
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 {
ID int64 `json:"id"`
SystemID int `json:"system_id"`
Tgid *int32 `json:"tgid"`
SystemID int32 `json:"system_id"`
Tgid int32 `json:"tgid"`
Name *string `json:"name"`
TgGroup *string `json:"tg_group"`
Frequency *int32 `json:"frequency"`

View file

@ -19,6 +19,7 @@ type Querier interface {
DeleteAPIKey(ctx context.Context, apiKey string) error
DeleteUser(ctx context.Context, username string) 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)
GetTalkgroupsWithAllTags(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
}
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
SELECT tags FROM talkgroups
WHERE id = systg2id($1, $2)

View file

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

View file

@ -6,6 +6,12 @@ WHERE tags @> ARRAY[$1];
SELECT * FROM talkgroups
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
SELECT tags FROM talkgroups
WHERE id = systg2id($1, $2);