From c7c1c45008e95dbf85db6a8f1b7d7f3933062d3f Mon Sep 17 00:00:00 2001 From: Eli Ribble Date: Thu, 19 Mar 2026 20:49:53 +0000 Subject: [PATCH] Add location to signal --- db/dbinfo/signal.bob.go | 80 +++++++++++++++++++++++-- db/migrations/00120_signal_location.sql | 13 ++++ db/models/signal.bob.go | 38 ++++++++++-- 3 files changed, 122 insertions(+), 9 deletions(-) create mode 100644 db/migrations/00120_signal_location.sql diff --git a/db/dbinfo/signal.bob.go b/db/dbinfo/signal.bob.go index bffd3f25..92d94e18 100644 --- a/db/dbinfo/signal.bob.go +++ b/db/dbinfo/signal.bob.go @@ -105,6 +105,24 @@ var Signals = Table[ Generated: false, AutoIncr: false, }, + Location: column{ + Name: "location", + DBType: "geometry", + Default: "", + Comment: "", + Nullable: false, + Generated: false, + AutoIncr: false, + }, + LocationType: column{ + Name: "location_type", + DBType: "text", + Default: "GENERATED", + Comment: "", + Nullable: true, + Generated: true, + AutoIncr: false, + }, }, Indexes: signalIndexes{ SignalPkey: index{ @@ -124,6 +142,40 @@ var Signals = Table[ Where: "", Include: []string{}, }, + IdxSignalLocation: index{ + Type: "gist", + Name: "idx_signal_location", + Columns: []indexColumn{ + { + Name: "location", + Desc: null.FromCond(false, true), + IsExpression: false, + }, + }, + Unique: false, + Comment: "", + NullsFirst: []bool{false}, + NullsDistinct: false, + Where: "", + Include: []string{}, + }, + IdxSignalLocationType: index{ + Type: "btree", + Name: "idx_signal_location_type", + Columns: []indexColumn{ + { + Name: "location_type", + Desc: null.FromCond(false, true), + IsExpression: false, + }, + }, + Unique: false, + Comment: "", + NullsFirst: []bool{false}, + NullsDistinct: false, + Where: "", + Include: []string{}, + }, }, PrimaryKey: &constraint{ Name: "signal_pkey", @@ -169,6 +221,16 @@ var Signals = Table[ }, }, + Checks: signalChecks{ + ValidLocationTypes: check{ + constraint: constraint{ + Name: "valid_location_types", + Columns: []string{"location_type"}, + Comment: "", + }, + Expression: "(location_type = ANY (ARRAY['POINT'::text, 'POLYGON'::text, 'MULTIPOLYGON'::text]))", + }, + }, Comment: "", } @@ -183,21 +245,25 @@ type signalColumns struct { Title column Type column SiteID column + Location column + LocationType column } func (c signalColumns) AsSlice() []column { return []column{ - c.Addressed, c.Addressor, c.Created, c.Creator, c.ID, c.OrganizationID, c.Species, c.Title, c.Type, c.SiteID, + c.Addressed, c.Addressor, c.Created, c.Creator, c.ID, c.OrganizationID, c.Species, c.Title, c.Type, c.SiteID, c.Location, c.LocationType, } } type signalIndexes struct { - SignalPkey index + SignalPkey index + IdxSignalLocation index + IdxSignalLocationType index } func (i signalIndexes) AsSlice() []index { return []index{ - i.SignalPkey, + i.SignalPkey, i.IdxSignalLocation, i.IdxSignalLocationType, } } @@ -220,8 +286,12 @@ func (u signalUniques) AsSlice() []constraint { return []constraint{} } -type signalChecks struct{} +type signalChecks struct { + ValidLocationTypes check +} func (c signalChecks) AsSlice() []check { - return []check{} + return []check{ + c.ValidLocationTypes, + } } diff --git a/db/migrations/00120_signal_location.sql b/db/migrations/00120_signal_location.sql new file mode 100644 index 00000000..11a0b429 --- /dev/null +++ b/db/migrations/00120_signal_location.sql @@ -0,0 +1,13 @@ +-- +goose Up +DELETE FROM signal; +ALTER TABLE signal ADD COLUMN location Geometry(Geometry, 4326) NOT NULL; +ALTER TABLE signal ADD COLUMN location_type TEXT GENERATED ALWAYS AS (GeometryType(location)) STORED; +ALTER TABLE signal ADD CONSTRAINT valid_location_types + CHECK (location_type IN ('POINT', 'POLYGON', 'MULTIPOLYGON')); +CREATE INDEX idx_signal_location ON signal USING GIST(location); +CREATE INDEX idx_signal_location_type ON signal(location_type); +-- +goose Down +ALTER TABLE signal DROP INDEX idx_signal_location_type; +ALTER TABLE signal DROP INDEX idx_signal_location; +ALTER TABLE signal DROP COLUMN location_type; +ALTER TABLE signal DROP COLUMN location; diff --git a/db/models/signal.bob.go b/db/models/signal.bob.go index 732ffa4a..178c9cea 100644 --- a/db/models/signal.bob.go +++ b/db/models/signal.bob.go @@ -36,6 +36,8 @@ type Signal struct { Title string `db:"title" ` Type enums.Signaltype `db:"type_" ` SiteID null.Val[int32] `db:"site_id" ` + Location string `db:"location" ` + LocationType null.Val[string] `db:"location_type,generated" ` R signalR `db:"-" ` } @@ -61,7 +63,7 @@ type signalR struct { func buildSignalColumns(alias string) signalColumns { return signalColumns{ ColumnsExpr: expr.NewColumnsExpr( - "addressed", "addressor", "created", "creator", "id", "organization_id", "species", "title", "type_", "site_id", + "addressed", "addressor", "created", "creator", "id", "organization_id", "species", "title", "type_", "site_id", "location", "location_type", ).WithParent("signal"), tableAlias: alias, Addressed: psql.Quote(alias, "addressed"), @@ -74,6 +76,8 @@ func buildSignalColumns(alias string) signalColumns { Title: psql.Quote(alias, "title"), Type: psql.Quote(alias, "type_"), SiteID: psql.Quote(alias, "site_id"), + Location: psql.Quote(alias, "location"), + LocationType: psql.Quote(alias, "location_type"), } } @@ -90,6 +94,8 @@ type signalColumns struct { Title psql.Expression Type psql.Expression SiteID psql.Expression + Location psql.Expression + LocationType psql.Expression } func (c signalColumns) Alias() string { @@ -114,10 +120,11 @@ type SignalSetter struct { Title omit.Val[string] `db:"title" ` Type omit.Val[enums.Signaltype] `db:"type_" ` SiteID omitnull.Val[int32] `db:"site_id" ` + Location omit.Val[string] `db:"location" ` } func (s SignalSetter) SetColumns() []string { - vals := make([]string, 0, 10) + vals := make([]string, 0, 11) if !s.Addressed.IsUnset() { vals = append(vals, "addressed") } @@ -148,6 +155,9 @@ func (s SignalSetter) SetColumns() []string { if !s.SiteID.IsUnset() { vals = append(vals, "site_id") } + if s.Location.IsValue() { + vals = append(vals, "location") + } return vals } @@ -182,6 +192,9 @@ func (s SignalSetter) Overwrite(t *Signal) { if !s.SiteID.IsUnset() { t.SiteID = s.SiteID.MustGetNull() } + if s.Location.IsValue() { + t.Location = s.Location.MustGet() + } } func (s *SignalSetter) Apply(q *dialect.InsertQuery) { @@ -190,7 +203,7 @@ func (s *SignalSetter) Apply(q *dialect.InsertQuery) { }) q.AppendValues(bob.ExpressionFunc(func(ctx context.Context, w io.StringWriter, d bob.Dialect, start int) ([]any, error) { - vals := make([]bob.Expression, 10) + vals := make([]bob.Expression, 11) if !s.Addressed.IsUnset() { vals[0] = psql.Arg(s.Addressed.MustGetNull()) } else { @@ -251,6 +264,12 @@ func (s *SignalSetter) Apply(q *dialect.InsertQuery) { vals[9] = psql.Raw("DEFAULT") } + if s.Location.IsValue() { + vals[10] = psql.Arg(s.Location.MustGet()) + } else { + vals[10] = psql.Raw("DEFAULT") + } + return bob.ExpressSlice(ctx, w, d, start, vals, "", ", ", "") })) } @@ -260,7 +279,7 @@ func (s SignalSetter) UpdateMod() bob.Mod[*dialect.UpdateQuery] { } func (s SignalSetter) Expressions(prefix ...string) []bob.Expression { - exprs := make([]bob.Expression, 0, 10) + exprs := make([]bob.Expression, 0, 11) if !s.Addressed.IsUnset() { exprs = append(exprs, expr.Join{Sep: " = ", Exprs: []bob.Expression{ @@ -332,6 +351,13 @@ func (s SignalSetter) Expressions(prefix ...string) []bob.Expression { }}) } + if s.Location.IsValue() { + exprs = append(exprs, expr.Join{Sep: " = ", Exprs: []bob.Expression{ + psql.Quote(append(prefix, "location")...), + psql.Arg(s.Location), + }}) + } + return exprs } @@ -857,6 +883,8 @@ type signalWhere[Q psql.Filterable] struct { Title psql.WhereMod[Q, string] Type psql.WhereMod[Q, enums.Signaltype] SiteID psql.WhereNullMod[Q, int32] + Location psql.WhereMod[Q, string] + LocationType psql.WhereNullMod[Q, string] } func (signalWhere[Q]) AliasedAs(alias string) signalWhere[Q] { @@ -875,6 +903,8 @@ func buildSignalWhere[Q psql.Filterable](cols signalColumns) signalWhere[Q] { Title: psql.Where[Q, string](cols.Title), Type: psql.Where[Q, enums.Signaltype](cols.Type), SiteID: psql.WhereNull[Q, int32](cols.SiteID), + Location: psql.Where[Q, string](cols.Location), + LocationType: psql.WhereNull[Q, string](cols.LocationType), } }