From d4dc80d1165a832d07ce71e0cdfb7013b07759af Mon Sep 17 00:00:00 2001 From: Daniel Ponte Date: Tue, 6 Aug 2024 19:46:01 -0400 Subject: [PATCH] wip --- pkg/calls/filter.go | 108 +++++++-------------- pkg/gordio/database/models.go | 4 +- pkg/gordio/database/querier.go | 1 + pkg/gordio/database/talkgroups.sql.go | 32 ++++++ sql/postgres/migrations/001_initial.up.sql | 4 +- sql/postgres/queries/talkgroups.sql | 6 ++ 6 files changed, 78 insertions(+), 77 deletions(-) diff --git a/pkg/calls/filter.go b/pkg/calls/filter.go index 8610861..ac050ef 100644 --- a/pkg/calls/filter.go +++ b/pkg/calls/filter.go @@ -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), - } -} diff --git a/pkg/gordio/database/models.go b/pkg/gordio/database/models.go index da5b3e2..e0d4509 100644 --- a/pkg/gordio/database/models.go +++ b/pkg/gordio/database/models.go @@ -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"` diff --git a/pkg/gordio/database/querier.go b/pkg/gordio/database/querier.go index 8f35eb3..45d38d8 100644 --- a/pkg/gordio/database/querier.go +++ b/pkg/gordio/database/querier.go @@ -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) diff --git a/pkg/gordio/database/talkgroups.sql.go b/pkg/gordio/database/talkgroups.sql.go index ce66dcf..dab0f72 100644 --- a/pkg/gordio/database/talkgroups.sql.go +++ b/pkg/gordio/database/talkgroups.sql.go @@ -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) diff --git a/sql/postgres/migrations/001_initial.up.sql b/sql/postgres/migrations/001_initial.up.sql index 3aca0a8..e390f9f 100644 --- a/sql/postgres/migrations/001_initial.up.sql +++ b/sql/postgres/migrations/001_initial.up.sql @@ -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, diff --git a/sql/postgres/queries/talkgroups.sql b/sql/postgres/queries/talkgroups.sql index 213d074..41909ae 100644 --- a/sql/postgres/queries/talkgroups.sql +++ b/sql/postgres/queries/talkgroups.sql @@ -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);