wip
This commit is contained in:
parent
a7acacda15
commit
93c742895f
8 changed files with 155 additions and 22 deletions
90
pkg/calls/filter.go
Normal file
90
pkg/calls/filter.go
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
package calls
|
||||||
|
|
||||||
|
type filterQuery struct {
|
||||||
|
Query string
|
||||||
|
Params []interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Talkgroup struct {
|
||||||
|
System uint32
|
||||||
|
Talkgroup uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t Talkgroup) Pack() int64 {
|
||||||
|
// P25 system IDs are 12 bits, so we can fit them in a signed 8 byte int (int64, pg INT8)
|
||||||
|
return int64((int64(t.System) << 32) | int64(t.Talkgroup))
|
||||||
|
}
|
||||||
|
|
||||||
|
type Filter struct {
|
||||||
|
Talkgroups []Talkgroup `json:"talkgroups"`
|
||||||
|
TalkgroupsNot []Talkgroup `json:"talkgroupsNot"`
|
||||||
|
TalkgroupTagsAll []string `json:"talkgroupTagsAll"`
|
||||||
|
TalkgroupTagsAny []string `json:"talkgroupTagsAny"`
|
||||||
|
TalkgroupTagsNot []string `json:"talkgroupTagsNot"`
|
||||||
|
|
||||||
|
talkgroups map[Talkgroup]bool
|
||||||
|
talkgroupTagsAll map[string]bool
|
||||||
|
talkgroupTagsAny map[string]bool
|
||||||
|
talkgroupTagsNot map[string]bool
|
||||||
|
|
||||||
|
query *filterQuery
|
||||||
|
}
|
||||||
|
|
||||||
|
func queryParams(s string, p ...any) (string, []any) {
|
||||||
|
return s, p
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Filter) filterQuery() filterQuery {
|
||||||
|
var q string
|
||||||
|
var args []interface{}
|
||||||
|
|
||||||
|
q, args = 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 filterQuery{Query: q, Params: args}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Filter) Packed(tg []Talkgroup) []int64 {
|
||||||
|
s := make([]int64, len(f.Talkgroups))
|
||||||
|
|
||||||
|
for i, v := range tg {
|
||||||
|
s[i] = v.Pack()
|
||||||
|
}
|
||||||
|
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Filter) Compile() *Filter {
|
||||||
|
f.talkgroups = make(map[Talkgroup]bool)
|
||||||
|
for _, tg := range f.Talkgroups {
|
||||||
|
f.talkgroups[tg] = 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
|
||||||
|
}
|
||||||
|
|
||||||
|
q := f.filterQuery()
|
||||||
|
f.query = &q
|
||||||
|
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
|
||||||
|
type FilterCache struct {
|
||||||
|
cache map[string]Filter
|
||||||
|
}
|
|
@ -13,6 +13,8 @@ import (
|
||||||
"github.com/jackc/pgx/v5/pgxpool"
|
"github.com/jackc/pgx/v5/pgxpool"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// This file will eventually turn into a postgres driver.
|
||||||
|
|
||||||
// DB is a database handle.
|
// DB is a database handle.
|
||||||
type DB struct {
|
type DB struct {
|
||||||
*pgxpool.Pool
|
*pgxpool.Pool
|
||||||
|
|
|
@ -68,8 +68,9 @@ type System struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type Talkgroup struct {
|
type Talkgroup struct {
|
||||||
|
ID int64 `json:"id"`
|
||||||
SystemID int `json:"system_id"`
|
SystemID int `json:"system_id"`
|
||||||
Tgid int `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"`
|
||||||
|
|
|
@ -13,12 +13,13 @@ import (
|
||||||
|
|
||||||
type Querier interface {
|
type Querier interface {
|
||||||
AddCall(ctx context.Context, arg AddCallParams) (uuid.UUID, error)
|
AddCall(ctx context.Context, arg AddCallParams) (uuid.UUID, error)
|
||||||
|
BulkSetTalkgroupTags(ctx context.Context, iD int64, tags []string) error
|
||||||
CreateAPIKey(ctx context.Context, owner int, expires pgtype.Timestamp, disabled *bool) (ApiKey, error)
|
CreateAPIKey(ctx context.Context, owner int, expires pgtype.Timestamp, disabled *bool) (ApiKey, error)
|
||||||
CreateUser(ctx context.Context, arg CreateUserParams) (User, error)
|
CreateUser(ctx context.Context, arg CreateUserParams) (User, error)
|
||||||
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)
|
||||||
GetTalkgroupTags(ctx context.Context, systemID int, tgid 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)
|
||||||
GetUserByID(ctx context.Context, id int32) (User, error)
|
GetUserByID(ctx context.Context, id int32) (User, error)
|
||||||
|
@ -26,7 +27,7 @@ type Querier interface {
|
||||||
GetUserByUsername(ctx context.Context, username string) (User, error)
|
GetUserByUsername(ctx context.Context, username string) (User, error)
|
||||||
GetUsers(ctx context.Context) ([]User, error)
|
GetUsers(ctx context.Context) ([]User, error)
|
||||||
SetCallTranscript(ctx context.Context, iD uuid.UUID, transcript *string) error
|
SetCallTranscript(ctx context.Context, iD uuid.UUID, transcript *string) error
|
||||||
SetTalkgroupTags(ctx context.Context, tags []string, tgid int) error
|
SetTalkgroupTags(ctx context.Context, sys int, tg int, tags []string) error
|
||||||
UpdatePassword(ctx context.Context, username string, password string) error
|
UpdatePassword(ctx context.Context, username string, password string) error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,20 +9,30 @@ import (
|
||||||
"context"
|
"context"
|
||||||
)
|
)
|
||||||
|
|
||||||
const getTalkgroupTags = `-- name: GetTalkgroupTags :one
|
const bulkSetTalkgroupTags = `-- name: BulkSetTalkgroupTags :exec
|
||||||
SELECT tags FROM talkgroups
|
UPDATE talkgroups SET tags = $2
|
||||||
WHERE system_id = $1 AND tgid = $2
|
WHERE id = ANY($1)
|
||||||
`
|
`
|
||||||
|
|
||||||
func (q *Queries) GetTalkgroupTags(ctx context.Context, systemID int, tgid int) ([]string, error) {
|
func (q *Queries) BulkSetTalkgroupTags(ctx context.Context, iD int64, tags []string) error {
|
||||||
row := q.db.QueryRow(ctx, getTalkgroupTags, systemID, tgid)
|
_, err := q.db.Exec(ctx, bulkSetTalkgroupTags, iD, tags)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
const getTalkgroupTags = `-- name: GetTalkgroupTags :one
|
||||||
|
SELECT tags FROM talkgroups
|
||||||
|
WHERE id = systg2id($1, $2)
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) GetTalkgroupTags(ctx context.Context, sys int, tg int) ([]string, error) {
|
||||||
|
row := q.db.QueryRow(ctx, getTalkgroupTags, sys, tg)
|
||||||
var tags []string
|
var tags []string
|
||||||
err := row.Scan(&tags)
|
err := row.Scan(&tags)
|
||||||
return tags, err
|
return tags, err
|
||||||
}
|
}
|
||||||
|
|
||||||
const getTalkgroupsWithAllTags = `-- name: GetTalkgroupsWithAllTags :many
|
const getTalkgroupsWithAllTags = `-- name: GetTalkgroupsWithAllTags :many
|
||||||
SELECT system_id, tgid, name, tg_group, frequency, metadata, tags FROM talkgroups
|
SELECT id, system_id, tgid, name, tg_group, frequency, metadata, tags FROM talkgroups
|
||||||
WHERE tags && ARRAY[$1]
|
WHERE tags && ARRAY[$1]
|
||||||
`
|
`
|
||||||
|
|
||||||
|
@ -36,6 +46,7 @@ func (q *Queries) GetTalkgroupsWithAllTags(ctx context.Context, tags []string) (
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
var i Talkgroup
|
var i Talkgroup
|
||||||
if err := rows.Scan(
|
if err := rows.Scan(
|
||||||
|
&i.ID,
|
||||||
&i.SystemID,
|
&i.SystemID,
|
||||||
&i.Tgid,
|
&i.Tgid,
|
||||||
&i.Name,
|
&i.Name,
|
||||||
|
@ -55,7 +66,7 @@ func (q *Queries) GetTalkgroupsWithAllTags(ctx context.Context, tags []string) (
|
||||||
}
|
}
|
||||||
|
|
||||||
const getTalkgroupsWithAnyTags = `-- name: GetTalkgroupsWithAnyTags :many
|
const getTalkgroupsWithAnyTags = `-- name: GetTalkgroupsWithAnyTags :many
|
||||||
SELECT system_id, tgid, name, tg_group, frequency, metadata, tags FROM talkgroups
|
SELECT id, system_id, tgid, name, tg_group, frequency, metadata, tags FROM talkgroups
|
||||||
WHERE tags @> ARRAY[$1]
|
WHERE tags @> ARRAY[$1]
|
||||||
`
|
`
|
||||||
|
|
||||||
|
@ -69,6 +80,7 @@ func (q *Queries) GetTalkgroupsWithAnyTags(ctx context.Context, tags []string) (
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
var i Talkgroup
|
var i Talkgroup
|
||||||
if err := rows.Scan(
|
if err := rows.Scan(
|
||||||
|
&i.ID,
|
||||||
&i.SystemID,
|
&i.SystemID,
|
||||||
&i.Tgid,
|
&i.Tgid,
|
||||||
&i.Name,
|
&i.Name,
|
||||||
|
@ -88,11 +100,11 @@ func (q *Queries) GetTalkgroupsWithAnyTags(ctx context.Context, tags []string) (
|
||||||
}
|
}
|
||||||
|
|
||||||
const setTalkgroupTags = `-- name: SetTalkgroupTags :exec
|
const setTalkgroupTags = `-- name: SetTalkgroupTags :exec
|
||||||
UPDATE talkgroups SET tags = $1
|
UPDATE talkgroups SET tags = $3
|
||||||
WHERE system_id = $1 AND tgid = $2
|
WHERE id = systg2id($1, $2)
|
||||||
`
|
`
|
||||||
|
|
||||||
func (q *Queries) SetTalkgroupTags(ctx context.Context, tags []string, tgid int) error {
|
func (q *Queries) SetTalkgroupTags(ctx context.Context, sys int, tg int, tags []string) error {
|
||||||
_, err := q.db.Exec(ctx, setTalkgroupTags, tags, tgid)
|
_, err := q.db.Exec(ctx, setTalkgroupTags, sys, tg, tags)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// protoc-gen-go v1.34.2
|
// protoc-gen-go v1.34.2
|
||||||
// protoc v5.27.1
|
// protoc v5.27.2
|
||||||
// source: stillbox.proto
|
// source: stillbox.proto
|
||||||
|
|
||||||
package pb
|
package pb
|
||||||
|
|
|
@ -23,19 +23,42 @@ CREATE TABLE IF NOT EXISTS systems(
|
||||||
name TEXT NOT NULL
|
name TEXT NOT NULL
|
||||||
);
|
);
|
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION systg2id(_sys INTEGER, _tg INTEGER) RETURNS INT8 LANGUAGE plpgsql AS
|
||||||
|
$$
|
||||||
|
BEGIN
|
||||||
|
RETURN ((_sys::BIGINT << 32) | _tg);
|
||||||
|
END
|
||||||
|
$$;
|
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION tgfromid(_id INT8) RETURNS INTEGER LANGUAGE plpgsql AS
|
||||||
|
$$
|
||||||
|
BEGIN
|
||||||
|
RETURN (_id & x'ffffffff'::BIGINT);
|
||||||
|
END
|
||||||
|
$$;
|
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION sysfromid(_id INT8) RETURNS INTEGER LANGUAGE plpgsql AS
|
||||||
|
$$
|
||||||
|
BEGIN
|
||||||
|
RETURN (_id >> 32);
|
||||||
|
END
|
||||||
|
$$;
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS talkgroups(
|
CREATE TABLE IF NOT EXISTS talkgroups(
|
||||||
system_id INTEGER REFERENCES systems(id) NOT NULL,
|
id INT8 PRIMARY KEY,
|
||||||
tgid INTEGER,
|
system_id INTEGER REFERENCES systems(id) NOT NULL GENERATED ALWAYS AS (id >> 32) STORED,
|
||||||
|
tgid INTEGER GENERATED ALWAYS AS (id & x'ffffffff'::BIGINT) STORED,
|
||||||
name TEXT,
|
name TEXT,
|
||||||
tg_group TEXT,
|
tg_group TEXT,
|
||||||
frequency INTEGER,
|
frequency INTEGER,
|
||||||
metadata JSONB,
|
metadata JSONB,
|
||||||
tags TEXT[] NOT NULL DEFAULT '{}',
|
tags TEXT[] NOT NULL DEFAULT '{}'
|
||||||
PRIMARY KEY (system_id, tgid)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS talkgroup_id_tags ON talkgroups USING GIN (tags);
|
CREATE INDEX IF NOT EXISTS talkgroup_id_tags ON talkgroups USING GIN (tags);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS talkgroups_learned(
|
CREATE TABLE IF NOT EXISTS talkgroups_learned(
|
||||||
id SERIAL PRIMARY KEY,
|
id SERIAL PRIMARY KEY,
|
||||||
system_id INTEGER REFERENCES systems(id) NOT NULL,
|
system_id INTEGER REFERENCES systems(id) NOT NULL,
|
||||||
|
|
|
@ -8,8 +8,12 @@ WHERE tags && ARRAY[$1];
|
||||||
|
|
||||||
-- name: GetTalkgroupTags :one
|
-- name: GetTalkgroupTags :one
|
||||||
SELECT tags FROM talkgroups
|
SELECT tags FROM talkgroups
|
||||||
WHERE system_id = $1 AND tgid = $2;
|
WHERE id = systg2id($1, $2);
|
||||||
|
|
||||||
-- name: SetTalkgroupTags :exec
|
-- name: SetTalkgroupTags :exec
|
||||||
UPDATE talkgroups SET tags = $1
|
UPDATE talkgroups SET tags = $3
|
||||||
WHERE system_id = $1 AND tgid = $2;
|
WHERE id = systg2id($1, $2);
|
||||||
|
|
||||||
|
-- name: BulkSetTalkgroupTags :exec
|
||||||
|
UPDATE talkgroups SET tags = $2
|
||||||
|
WHERE id = ANY($1);
|
||||||
|
|
Loading…
Reference in a new issue