From 3cdecdccec1fd7ea0db3a91d1fc9437f6062867d Mon Sep 17 00:00:00 2001 From: AzenKain Date: Sun, 24 May 2026 17:31:32 +0700 Subject: [PATCH] feat: reimplement geometry module with database schema, repository, service layer, and API documentation --- db/migrations/000009_geometries.up.sql | 6 +- db/query/geometries.sql | 25 ++++--- db/schema.sql | 2 +- docs/docs.go | 19 +++--- docs/swagger.json | 19 +++--- docs/swagger.yaml | 15 ++--- internal/dtos/request/geometry.go | 1 + internal/dtos/request/snapshot.go | 4 +- internal/dtos/response/geometry.go | 2 +- .../dtos/response/geometryEntitySearch.go | 19 +++--- internal/gen/sqlc/geometries.sql.go | 67 ++++++++++--------- internal/gen/sqlc/models.go | 2 +- internal/models/geometry.go | 6 +- internal/repositories/geometryRepository.go | 16 ++--- internal/services/geometryService.go | 14 +++- internal/services/submissionService.go | 14 +++- 16 files changed, 130 insertions(+), 101 deletions(-) diff --git a/db/migrations/000009_geometries.up.sql b/db/migrations/000009_geometries.up.sql index fdd549b..7ab68b6 100644 --- a/db/migrations/000009_geometries.up.sql +++ b/db/migrations/000009_geometries.up.sql @@ -7,7 +7,7 @@ CREATE TABLE IF NOT EXISTS geometries ( project_id UUID NOT NULL REFERENCES projects(id) ON DELETE CASCADE, geo_type SMALLINT NOT NULL DEFAULT 1, draw_geometry JSONB NOT NULL, - binding JSONB, + bound_with UUID REFERENCES geometries(id) ON DELETE SET NULL, time_start INT, time_end INT, bbox GEOMETRY(Polygon, 4326), @@ -40,8 +40,8 @@ WHERE is_deleted = false; CREATE INDEX idx_entity_geometries_geometry ON entity_geometries(geometry_id); -CREATE INDEX idx_geom_binding -ON geometries USING GIN (binding); +CREATE INDEX idx_geom_bound_with +ON geometries (bound_with); CREATE INDEX idx_geom_updated_at ON geometries (updated_at DESC) diff --git a/db/query/geometries.sql b/db/query/geometries.sql index dec2728..2838f59 100644 --- a/db/query/geometries.sql +++ b/db/query/geometries.sql @@ -1,15 +1,15 @@ -- name: CreateGeometry :one INSERT INTO geometries ( - id, geo_type, draw_geometry, binding, time_start, time_end, bbox, project_id + id, geo_type, draw_geometry, bound_with, time_start, time_end, bbox, project_id ) VALUES ( COALESCE(sqlc.narg('id')::uuid, uuidv7()), $1, $2, $3, $4, $5, ST_MakeEnvelope(sqlc.arg('min_lng')::float8, sqlc.arg('min_lat')::float8, sqlc.arg('max_lng')::float8, sqlc.arg('max_lat')::float8, 4326), $6 ) -RETURNING id, geo_type, draw_geometry, binding, time_start, time_end, project_id, +RETURNING id, geo_type, draw_geometry, bound_with, time_start, time_end, project_id, ST_XMin(bbox)::float8 as min_lng, ST_YMin(bbox)::float8 as min_lat, ST_XMax(bbox)::float8 as max_lng, ST_YMax(bbox)::float8 as max_lat, is_deleted, created_at, updated_at; -- name: GetGeometryById :one -SELECT id, geo_type, draw_geometry, binding, time_start, time_end, project_id, +SELECT id, geo_type, draw_geometry, bound_with, time_start, time_end, project_id, ST_XMin(bbox)::float8 as min_lng, ST_YMin(bbox)::float8 as min_lat, ST_XMax(bbox)::float8 as max_lng, ST_YMax(bbox)::float8 as max_lat, is_deleted, created_at, updated_at FROM geometries @@ -20,7 +20,7 @@ UPDATE geometries SET geo_type = COALESCE(sqlc.narg('geo_type'), geo_type), draw_geometry = COALESCE(sqlc.narg('draw_geometry'), draw_geometry), - binding = COALESCE(sqlc.narg('binding'), binding), + bound_with = COALESCE(sqlc.narg('bound_with'), bound_with), time_start = COALESCE(sqlc.narg('time_start'), time_start), time_end = COALESCE(sqlc.narg('time_end'), time_end), project_id = COALESCE(sqlc.narg('project_id'), project_id), @@ -31,7 +31,7 @@ SET END, updated_at = now() WHERE id = sqlc.arg('id') AND is_deleted = false -RETURNING id, geo_type, draw_geometry, binding, time_start, time_end, project_id, +RETURNING id, geo_type, draw_geometry, bound_with, time_start, time_end, project_id, ST_XMin(bbox)::float8 as min_lng, ST_YMin(bbox)::float8 as min_lat, ST_XMax(bbox)::float8 as max_lng, ST_YMax(bbox)::float8 as max_lat, is_deleted, created_at, updated_at; @@ -43,7 +43,7 @@ WHERE id = $1; -- name: SearchGeometries :many SELECT - g.id, g.geo_type, g.draw_geometry, g.binding, g.time_start, g.time_end, g.project_id, + g.id, g.geo_type, g.draw_geometry, g.bound_with, g.time_start, g.time_end, g.project_id, ST_XMin(g.bbox)::float8 as min_lng, ST_YMin(g.bbox)::float8 as min_lat, ST_XMax(g.bbox)::float8 as max_lng, @@ -82,6 +82,11 @@ WHERE g.is_deleted = false AND eg.entity_id = sqlc.narg('entity_id')::uuid ) ) + AND ( + sqlc.narg('has_bound')::boolean IS NULL OR + sqlc.narg('has_bound')::boolean = true OR + g.bound_with IS NULL + ) ORDER BY g.id DESC; -- name: SearchGeometriesByEntityName :many @@ -104,7 +109,7 @@ SELECT g.id AS geometry_id, g.geo_type, g.draw_geometry, - g.binding, + g.bound_with, g.time_start, g.time_end FROM matched_entities me @@ -133,7 +138,7 @@ WHERE project_id = $1; -- name: GetGeometriesByIDs :many SELECT - id, geo_type, draw_geometry, binding, time_start, time_end, project_id, + id, geo_type, draw_geometry, bound_with, time_start, time_end, project_id, ST_XMin(bbox)::float8 as min_lng, ST_YMin(bbox)::float8 as min_lat, ST_XMax(bbox)::float8 as max_lng, @@ -144,7 +149,7 @@ WHERE id = ANY($1::uuid[]) AND is_deleted = false; -- name: GetGeometriesByProjectId :many SELECT - id, geo_type, draw_geometry, binding, time_start, time_end, project_id, + id, geo_type, draw_geometry, bound_with, time_start, time_end, project_id, ST_XMin(bbox)::float8 as min_lng, ST_YMin(bbox)::float8 as min_lat, ST_XMax(bbox)::float8 as max_lng, @@ -174,7 +179,7 @@ SELECT g.id AS geometry_id, g.geo_type, g.draw_geometry, - g.binding, + g.bound_with, g.time_start, g.time_end FROM ( diff --git a/db/schema.sql b/db/schema.sql index 1a4af03..315843d 100644 --- a/db/schema.sql +++ b/db/schema.sql @@ -127,7 +127,7 @@ CREATE TABLE IF NOT EXISTS geometries ( id UUID PRIMARY KEY DEFAULT uuidv7(), geo_type SMALLINT NOT NULL DEFAULT 1, draw_geometry JSONB NOT NULL, - binding JSONB, + bound_with UUID REFERENCES geometries(id) ON DELETE SET NULL, time_start INT, time_end INT, bbox GEOMETRY(Polygon, 4326), diff --git a/docs/docs.go b/docs/docs.go index 5f15e1c..220ed67 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -791,6 +791,11 @@ const docTemplate = `{ "name": "entity_id", "in": "query" }, + { + "type": "boolean", + "name": "has_bound", + "in": "query" + }, { "maximum": 90, "minimum": -90, @@ -4769,11 +4774,8 @@ const docTemplate = `{ "id" ], "properties": { - "binding": { - "type": "array", - "items": { - "type": "string" - } + "bound_with": { + "type": "string" }, "entity_id": { "type": "string" @@ -4875,11 +4877,8 @@ const docTemplate = `{ "bbox": { "$ref": "#/definitions/history-api_internal_dtos_request.BBox" }, - "binding": { - "type": "array", - "items": { - "type": "string" - } + "bound_with": { + "type": "string" }, "draw_geometry": { "type": "array", diff --git a/docs/swagger.json b/docs/swagger.json index 5ffb329..d077aeb 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -784,6 +784,11 @@ "name": "entity_id", "in": "query" }, + { + "type": "boolean", + "name": "has_bound", + "in": "query" + }, { "maximum": 90, "minimum": -90, @@ -4762,11 +4767,8 @@ "id" ], "properties": { - "binding": { - "type": "array", - "items": { - "type": "string" - } + "bound_with": { + "type": "string" }, "entity_id": { "type": "string" @@ -4868,11 +4870,8 @@ "bbox": { "$ref": "#/definitions/history-api_internal_dtos_request.BBox" }, - "binding": { - "type": "array", - "items": { - "type": "string" - } + "bound_with": { + "type": "string" }, "draw_geometry": { "type": "array", diff --git a/docs/swagger.yaml b/docs/swagger.yaml index c818430..1ba75b8 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -301,10 +301,8 @@ definitions: type: object history-api_internal_dtos_request.FeatureProperties: properties: - binding: - items: - type: string - type: array + bound_with: + type: string entity_id: type: string entity_ids: @@ -374,10 +372,8 @@ definitions: type: string bbox: $ref: '#/definitions/history-api_internal_dtos_request.BBox' - binding: - items: - type: string - type: array + bound_with: + type: string draw_geometry: items: type: integer @@ -1216,6 +1212,9 @@ paths: - in: query name: entity_id type: string + - in: query + name: has_bound + type: boolean - in: query maximum: 90 minimum: -90 diff --git a/internal/dtos/request/geometry.go b/internal/dtos/request/geometry.go index f31fc34..7d12027 100644 --- a/internal/dtos/request/geometry.go +++ b/internal/dtos/request/geometry.go @@ -9,6 +9,7 @@ type SearchGeometryDto struct { TimeRange *int32 `json:"time_range" query:"time_range" validate:"omitempty,number"` EntityID *string `json:"entity_id" query:"entity_id" validate:"omitempty,uuid"` ProjectID *string `json:"project_id" query:"project_id" validate:"omitempty,uuid"` + HasBound *bool `json:"has_bound" query:"has_bound" validate:"omitempty"` } type SearchGeometriesByEntityNameDto struct { diff --git a/internal/dtos/request/snapshot.go b/internal/dtos/request/snapshot.go index c0231e3..8b99250 100644 --- a/internal/dtos/request/snapshot.go +++ b/internal/dtos/request/snapshot.go @@ -36,7 +36,7 @@ type FeatureProperties struct { GeometryPreset string `json:"geometry_preset,omitempty"` TimeStart *float64 `json:"time_start,omitempty"` TimeEnd *float64 `json:"time_end,omitempty"` - Binding []string `json:"binding,omitempty"` + BoundWith *string `json:"bound_with,omitempty"` EntityID string `json:"entity_id,omitempty" validate:"omitempty,uuidv7"` EntityIDs []string `json:"entity_ids,omitempty" validate:"omitempty,dive,uuidv7"` EntityName string `json:"entity_name,omitempty"` @@ -64,7 +64,7 @@ type GeometrySnapshot struct { Operation string `json:"operation,omitempty" validate:"omitempty,oneof=create update delete reference"` Type string `json:"type,omitempty" validate:"omitempty"` DrawGeometry json.RawMessage `json:"draw_geometry,omitempty"` - Binding []string `json:"binding,omitempty"` + BoundWith *string `json:"bound_with,omitempty"` TimeStart *float64 `json:"time_start,omitempty"` TimeEnd *float64 `json:"time_end,omitempty"` BBox *BBox `json:"bbox,omitempty" validate:"omitempty"` diff --git a/internal/dtos/response/geometry.go b/internal/dtos/response/geometry.go index cabdc2c..877cfb2 100644 --- a/internal/dtos/response/geometry.go +++ b/internal/dtos/response/geometry.go @@ -16,7 +16,7 @@ type GeometryResponse struct { ID string `json:"id"` GeoType int16 `json:"geo_type"` DrawGeometry json.RawMessage `json:"draw_geometry"` - Binding json.RawMessage `json:"binding,omitempty"` + BoundWith *string `json:"bound_with,omitempty"` TimeStart int32 `json:"time_start,omitempty"` TimeEnd int32 `json:"time_end,omitempty"` Bbox *Bbox `json:"bbox,omitempty"` diff --git a/internal/dtos/response/geometryEntitySearch.go b/internal/dtos/response/geometryEntitySearch.go index 8d538f8..46869c4 100644 --- a/internal/dtos/response/geometryEntitySearch.go +++ b/internal/dtos/response/geometryEntitySearch.go @@ -2,24 +2,23 @@ package response import "encoding/json" - type SearchGeometriesByEntityNameResponse struct { Items []*EntityGeometriesSearchItem `json:"items"` - NextCursor string `json:"next_cursor,omitempty"` + NextCursor string `json:"next_cursor,omitempty"` } type EntityGeometriesSearchItem struct { - EntityID string `json:"entity_id"` - Name string `json:"name"` - Description string `json:"description"` + EntityID string `json:"entity_id"` + Name string `json:"name"` + Description string `json:"description"` Geometries []*EntityGeometrySearchGeo `json:"geometries"` } type EntityGeometrySearchGeo struct { - ID string `json:"id"` - GeoType int16 `json:"geo_type"` + ID string `json:"id"` + GeoType int16 `json:"geo_type"` DrawGeometry json.RawMessage `json:"draw_geometry"` - Binding json.RawMessage `json:"binding,omitempty"` - TimeStart *int32 `json:"time_start,omitempty"` - TimeEnd *int32 `json:"time_end,omitempty"` + BoundWith *string `json:"bound_with,omitempty"` + TimeStart *int32 `json:"time_start,omitempty"` + TimeEnd *int32 `json:"time_end,omitempty"` } diff --git a/internal/gen/sqlc/geometries.sql.go b/internal/gen/sqlc/geometries.sql.go index 4dd7b8b..a9ed7c3 100644 --- a/internal/gen/sqlc/geometries.sql.go +++ b/internal/gen/sqlc/geometries.sql.go @@ -69,11 +69,11 @@ func (q *Queries) CreateEntityGeometries(ctx context.Context, arg CreateEntityGe const createGeometry = `-- name: CreateGeometry :one INSERT INTO geometries ( - id, geo_type, draw_geometry, binding, time_start, time_end, bbox, project_id + id, geo_type, draw_geometry, bound_with, time_start, time_end, bbox, project_id ) VALUES ( COALESCE($7::uuid, uuidv7()), $1, $2, $3, $4, $5, ST_MakeEnvelope($8::float8, $9::float8, $10::float8, $11::float8, 4326), $6 ) -RETURNING id, geo_type, draw_geometry, binding, time_start, time_end, project_id, +RETURNING id, geo_type, draw_geometry, bound_with, time_start, time_end, project_id, ST_XMin(bbox)::float8 as min_lng, ST_YMin(bbox)::float8 as min_lat, ST_XMax(bbox)::float8 as max_lng, ST_YMax(bbox)::float8 as max_lat, is_deleted, created_at, updated_at ` @@ -81,7 +81,7 @@ RETURNING id, geo_type, draw_geometry, binding, time_start, time_end, project_id type CreateGeometryParams struct { GeoType int16 `json:"geo_type"` DrawGeometry json.RawMessage `json:"draw_geometry"` - Binding []byte `json:"binding"` + BoundWith pgtype.UUID `json:"bound_with"` TimeStart pgtype.Int4 `json:"time_start"` TimeEnd pgtype.Int4 `json:"time_end"` ProjectID pgtype.UUID `json:"project_id"` @@ -96,7 +96,7 @@ type CreateGeometryRow struct { ID pgtype.UUID `json:"id"` GeoType int16 `json:"geo_type"` DrawGeometry json.RawMessage `json:"draw_geometry"` - Binding []byte `json:"binding"` + BoundWith pgtype.UUID `json:"bound_with"` TimeStart pgtype.Int4 `json:"time_start"` TimeEnd pgtype.Int4 `json:"time_end"` ProjectID pgtype.UUID `json:"project_id"` @@ -113,7 +113,7 @@ func (q *Queries) CreateGeometry(ctx context.Context, arg CreateGeometryParams) row := q.db.QueryRow(ctx, createGeometry, arg.GeoType, arg.DrawGeometry, - arg.Binding, + arg.BoundWith, arg.TimeStart, arg.TimeEnd, arg.ProjectID, @@ -128,7 +128,7 @@ func (q *Queries) CreateGeometry(ctx context.Context, arg CreateGeometryParams) &i.ID, &i.GeoType, &i.DrawGeometry, - &i.Binding, + &i.BoundWith, &i.TimeStart, &i.TimeEnd, &i.ProjectID, @@ -199,7 +199,7 @@ SELECT g.id AS geometry_id, g.geo_type, g.draw_geometry, - g.binding, + g.bound_with, g.time_start, g.time_end FROM ( @@ -224,7 +224,7 @@ type GetEntityGeometriesByPairsRow struct { GeometryID pgtype.UUID `json:"geometry_id"` GeoType int16 `json:"geo_type"` DrawGeometry json.RawMessage `json:"draw_geometry"` - Binding []byte `json:"binding"` + BoundWith pgtype.UUID `json:"bound_with"` TimeStart pgtype.Int4 `json:"time_start"` TimeEnd pgtype.Int4 `json:"time_end"` } @@ -245,7 +245,7 @@ func (q *Queries) GetEntityGeometriesByPairs(ctx context.Context, arg GetEntityG &i.GeometryID, &i.GeoType, &i.DrawGeometry, - &i.Binding, + &i.BoundWith, &i.TimeStart, &i.TimeEnd, ); err != nil { @@ -261,7 +261,7 @@ func (q *Queries) GetEntityGeometriesByPairs(ctx context.Context, arg GetEntityG const getGeometriesByIDs = `-- name: GetGeometriesByIDs :many SELECT - id, geo_type, draw_geometry, binding, time_start, time_end, project_id, + id, geo_type, draw_geometry, bound_with, time_start, time_end, project_id, ST_XMin(bbox)::float8 as min_lng, ST_YMin(bbox)::float8 as min_lat, ST_XMax(bbox)::float8 as max_lng, @@ -275,7 +275,7 @@ type GetGeometriesByIDsRow struct { ID pgtype.UUID `json:"id"` GeoType int16 `json:"geo_type"` DrawGeometry json.RawMessage `json:"draw_geometry"` - Binding []byte `json:"binding"` + BoundWith pgtype.UUID `json:"bound_with"` TimeStart pgtype.Int4 `json:"time_start"` TimeEnd pgtype.Int4 `json:"time_end"` ProjectID pgtype.UUID `json:"project_id"` @@ -301,7 +301,7 @@ func (q *Queries) GetGeometriesByIDs(ctx context.Context, dollar_1 []pgtype.UUID &i.ID, &i.GeoType, &i.DrawGeometry, - &i.Binding, + &i.BoundWith, &i.TimeStart, &i.TimeEnd, &i.ProjectID, @@ -325,7 +325,7 @@ func (q *Queries) GetGeometriesByIDs(ctx context.Context, dollar_1 []pgtype.UUID const getGeometriesByProjectId = `-- name: GetGeometriesByProjectId :many SELECT - id, geo_type, draw_geometry, binding, time_start, time_end, project_id, + id, geo_type, draw_geometry, bound_with, time_start, time_end, project_id, ST_XMin(bbox)::float8 as min_lng, ST_YMin(bbox)::float8 as min_lat, ST_XMax(bbox)::float8 as max_lng, @@ -339,7 +339,7 @@ type GetGeometriesByProjectIdRow struct { ID pgtype.UUID `json:"id"` GeoType int16 `json:"geo_type"` DrawGeometry json.RawMessage `json:"draw_geometry"` - Binding []byte `json:"binding"` + BoundWith pgtype.UUID `json:"bound_with"` TimeStart pgtype.Int4 `json:"time_start"` TimeEnd pgtype.Int4 `json:"time_end"` ProjectID pgtype.UUID `json:"project_id"` @@ -365,7 +365,7 @@ func (q *Queries) GetGeometriesByProjectId(ctx context.Context, projectID pgtype &i.ID, &i.GeoType, &i.DrawGeometry, - &i.Binding, + &i.BoundWith, &i.TimeStart, &i.TimeEnd, &i.ProjectID, @@ -388,7 +388,7 @@ func (q *Queries) GetGeometriesByProjectId(ctx context.Context, projectID pgtype } const getGeometryById = `-- name: GetGeometryById :one -SELECT id, geo_type, draw_geometry, binding, time_start, time_end, project_id, +SELECT id, geo_type, draw_geometry, bound_with, time_start, time_end, project_id, ST_XMin(bbox)::float8 as min_lng, ST_YMin(bbox)::float8 as min_lat, ST_XMax(bbox)::float8 as max_lng, ST_YMax(bbox)::float8 as max_lat, is_deleted, created_at, updated_at FROM geometries @@ -399,7 +399,7 @@ type GetGeometryByIdRow struct { ID pgtype.UUID `json:"id"` GeoType int16 `json:"geo_type"` DrawGeometry json.RawMessage `json:"draw_geometry"` - Binding []byte `json:"binding"` + BoundWith pgtype.UUID `json:"bound_with"` TimeStart pgtype.Int4 `json:"time_start"` TimeEnd pgtype.Int4 `json:"time_end"` ProjectID pgtype.UUID `json:"project_id"` @@ -419,7 +419,7 @@ func (q *Queries) GetGeometryById(ctx context.Context, id pgtype.UUID) (GetGeome &i.ID, &i.GeoType, &i.DrawGeometry, - &i.Binding, + &i.BoundWith, &i.TimeStart, &i.TimeEnd, &i.ProjectID, @@ -436,7 +436,7 @@ func (q *Queries) GetGeometryById(ctx context.Context, id pgtype.UUID) (GetGeome const searchGeometries = `-- name: SearchGeometries :many SELECT - g.id, g.geo_type, g.draw_geometry, g.binding, g.time_start, g.time_end, g.project_id, + g.id, g.geo_type, g.draw_geometry, g.bound_with, g.time_start, g.time_end, g.project_id, ST_XMin(g.bbox)::float8 as min_lng, ST_YMin(g.bbox)::float8 as min_lat, ST_XMax(g.bbox)::float8 as max_lng, @@ -475,6 +475,11 @@ WHERE g.is_deleted = false AND eg.entity_id = $8::uuid ) ) + AND ( + $9::boolean IS NULL OR + $9::boolean = true OR + g.bound_with IS NULL + ) ORDER BY g.id DESC ` @@ -487,13 +492,14 @@ type SearchGeometriesParams struct { TimePoint pgtype.Int4 `json:"time_point"` TimeRange pgtype.Int4 `json:"time_range"` EntityID pgtype.UUID `json:"entity_id"` + HasBound pgtype.Bool `json:"has_bound"` } type SearchGeometriesRow struct { ID pgtype.UUID `json:"id"` GeoType int16 `json:"geo_type"` DrawGeometry json.RawMessage `json:"draw_geometry"` - Binding []byte `json:"binding"` + BoundWith pgtype.UUID `json:"bound_with"` TimeStart pgtype.Int4 `json:"time_start"` TimeEnd pgtype.Int4 `json:"time_end"` ProjectID pgtype.UUID `json:"project_id"` @@ -516,6 +522,7 @@ func (q *Queries) SearchGeometries(ctx context.Context, arg SearchGeometriesPara arg.TimePoint, arg.TimeRange, arg.EntityID, + arg.HasBound, ) if err != nil { return nil, err @@ -528,7 +535,7 @@ func (q *Queries) SearchGeometries(ctx context.Context, arg SearchGeometriesPara &i.ID, &i.GeoType, &i.DrawGeometry, - &i.Binding, + &i.BoundWith, &i.TimeStart, &i.TimeEnd, &i.ProjectID, @@ -570,7 +577,7 @@ SELECT g.id AS geometry_id, g.geo_type, g.draw_geometry, - g.binding, + g.bound_with, g.time_start, g.time_end FROM matched_entities me @@ -595,7 +602,7 @@ type SearchGeometriesByEntityNameRow struct { GeometryID pgtype.UUID `json:"geometry_id"` GeoType pgtype.Int2 `json:"geo_type"` DrawGeometry []byte `json:"draw_geometry"` - Binding []byte `json:"binding"` + BoundWith pgtype.UUID `json:"bound_with"` TimeStart pgtype.Int4 `json:"time_start"` TimeEnd pgtype.Int4 `json:"time_end"` } @@ -616,7 +623,7 @@ func (q *Queries) SearchGeometriesByEntityName(ctx context.Context, arg SearchGe &i.GeometryID, &i.GeoType, &i.DrawGeometry, - &i.Binding, + &i.BoundWith, &i.TimeStart, &i.TimeEnd, ); err != nil { @@ -635,7 +642,7 @@ UPDATE geometries SET geo_type = COALESCE($1, geo_type), draw_geometry = COALESCE($2, draw_geometry), - binding = COALESCE($3, binding), + bound_with = COALESCE($3, bound_with), time_start = COALESCE($4, time_start), time_end = COALESCE($5, time_end), project_id = COALESCE($6, project_id), @@ -646,7 +653,7 @@ SET END, updated_at = now() WHERE id = $12 AND is_deleted = false -RETURNING id, geo_type, draw_geometry, binding, time_start, time_end, project_id, +RETURNING id, geo_type, draw_geometry, bound_with, time_start, time_end, project_id, ST_XMin(bbox)::float8 as min_lng, ST_YMin(bbox)::float8 as min_lat, ST_XMax(bbox)::float8 as max_lng, ST_YMax(bbox)::float8 as max_lat, is_deleted, created_at, updated_at ` @@ -654,7 +661,7 @@ RETURNING id, geo_type, draw_geometry, binding, time_start, time_end, project_id type UpdateGeometryParams struct { GeoType pgtype.Int2 `json:"geo_type"` DrawGeometry []byte `json:"draw_geometry"` - Binding []byte `json:"binding"` + BoundWith pgtype.UUID `json:"bound_with"` TimeStart pgtype.Int4 `json:"time_start"` TimeEnd pgtype.Int4 `json:"time_end"` ProjectID pgtype.UUID `json:"project_id"` @@ -670,7 +677,7 @@ type UpdateGeometryRow struct { ID pgtype.UUID `json:"id"` GeoType int16 `json:"geo_type"` DrawGeometry json.RawMessage `json:"draw_geometry"` - Binding []byte `json:"binding"` + BoundWith pgtype.UUID `json:"bound_with"` TimeStart pgtype.Int4 `json:"time_start"` TimeEnd pgtype.Int4 `json:"time_end"` ProjectID pgtype.UUID `json:"project_id"` @@ -687,7 +694,7 @@ func (q *Queries) UpdateGeometry(ctx context.Context, arg UpdateGeometryParams) row := q.db.QueryRow(ctx, updateGeometry, arg.GeoType, arg.DrawGeometry, - arg.Binding, + arg.BoundWith, arg.TimeStart, arg.TimeEnd, arg.ProjectID, @@ -703,7 +710,7 @@ func (q *Queries) UpdateGeometry(ctx context.Context, arg UpdateGeometryParams) &i.ID, &i.GeoType, &i.DrawGeometry, - &i.Binding, + &i.BoundWith, &i.TimeStart, &i.TimeEnd, &i.ProjectID, diff --git a/internal/gen/sqlc/models.go b/internal/gen/sqlc/models.go index 1ded2f6..1a512e8 100644 --- a/internal/gen/sqlc/models.go +++ b/internal/gen/sqlc/models.go @@ -81,7 +81,7 @@ type Geometry struct { ID pgtype.UUID `json:"id"` GeoType int16 `json:"geo_type"` DrawGeometry json.RawMessage `json:"draw_geometry"` - Binding []byte `json:"binding"` + BoundWith pgtype.UUID `json:"bound_with"` TimeStart pgtype.Int4 `json:"time_start"` TimeEnd pgtype.Int4 `json:"time_end"` Bbox interface{} `json:"bbox"` diff --git a/internal/models/geometry.go b/internal/models/geometry.go index 1301131..7378ed2 100644 --- a/internal/models/geometry.go +++ b/internal/models/geometry.go @@ -10,7 +10,7 @@ type GeometryEntity struct { ID string `json:"id"` GeoType int16 `json:"geo_type"` DrawGeometry json.RawMessage `json:"draw_geometry"` - Binding json.RawMessage `json:"binding"` + BoundWith *string `json:"bound_with"` TimeStart int32 `json:"time_start"` TimeEnd int32 `json:"time_end"` Bbox *response.Bbox `json:"bbox"` @@ -27,7 +27,7 @@ type EntityGeometriesSearchEntity struct { GeometryID string `json:"id"` GeoType int16 `json:"geo_type"` DrawGeometry json.RawMessage `json:"draw_geometry"` - Binding json.RawMessage `json:"binding,omitempty"` + BoundWith *string `json:"bound_with,omitempty"` TimeStart *int32 `json:"time_start,omitempty"` TimeEnd *int32 `json:"time_end,omitempty"` } @@ -40,7 +40,7 @@ func (g *GeometryEntity) ToResponse() *response.GeometryResponse { ID: g.ID, GeoType: g.GeoType, DrawGeometry: g.DrawGeometry, - Binding: g.Binding, + BoundWith: g.BoundWith, TimeStart: g.TimeStart, TimeEnd: g.TimeEnd, Bbox: g.Bbox, diff --git a/internal/repositories/geometryRepository.go b/internal/repositories/geometryRepository.go index bb786ee..1da8365 100644 --- a/internal/repositories/geometryRepository.go +++ b/internal/repositories/geometryRepository.go @@ -97,7 +97,7 @@ func (r *geometryRepository) getByIDsWithFallback(ctx context.Context, ids []str ID: convert.UUIDToString(row.ID), GeoType: row.GeoType, DrawGeometry: row.DrawGeometry, - Binding: row.Binding, + BoundWith: convert.UUIDToStringPtr(row.BoundWith), TimeStart: convert.Int4ToInt32(row.TimeStart), TimeEnd: convert.Int4ToInt32(row.TimeEnd), Bbox: &response.Bbox{ @@ -159,7 +159,7 @@ func (r *geometryRepository) GetByID(ctx context.Context, id pgtype.UUID) (*mode ID: convert.UUIDToString(row.ID), GeoType: row.GeoType, DrawGeometry: row.DrawGeometry, - Binding: row.Binding, + BoundWith: convert.UUIDToStringPtr(row.BoundWith), TimeStart: convert.Int4ToInt32(row.TimeStart), TimeEnd: convert.Int4ToInt32(row.TimeEnd), Bbox: &response.Bbox{ @@ -198,7 +198,7 @@ func (r *geometryRepository) Search(ctx context.Context, params sqlc.SearchGeome ID: convert.UUIDToString(row.ID), GeoType: row.GeoType, DrawGeometry: row.DrawGeometry, - Binding: row.Binding, + BoundWith: convert.UUIDToStringPtr(row.BoundWith), TimeStart: convert.Int4ToInt32(row.TimeStart), TimeEnd: convert.Int4ToInt32(row.TimeEnd), Bbox: &response.Bbox{ @@ -237,7 +237,7 @@ func (r *geometryRepository) Create(ctx context.Context, params sqlc.CreateGeome ID: convert.UUIDToString(row.ID), GeoType: row.GeoType, DrawGeometry: row.DrawGeometry, - Binding: row.Binding, + BoundWith: convert.UUIDToStringPtr(row.BoundWith), TimeStart: convert.Int4ToInt32(row.TimeStart), TimeEnd: convert.Int4ToInt32(row.TimeEnd), Bbox: &response.Bbox{ @@ -264,7 +264,7 @@ func (r *geometryRepository) Update(ctx context.Context, params sqlc.UpdateGeome ID: convert.UUIDToString(row.ID), GeoType: row.GeoType, DrawGeometry: row.DrawGeometry, - Binding: row.Binding, + BoundWith: convert.UUIDToStringPtr(row.BoundWith), TimeStart: convert.Int4ToInt32(row.TimeStart), TimeEnd: convert.Int4ToInt32(row.TimeEnd), Bbox: &response.Bbox{ @@ -328,7 +328,7 @@ func (r *geometryRepository) GetByProjectID(ctx context.Context, projectID pgtyp ID: convert.UUIDToString(row.ID), GeoType: row.GeoType, DrawGeometry: row.DrawGeometry, - Binding: row.Binding, + BoundWith: convert.UUIDToStringPtr(row.BoundWith), TimeStart: convert.Int4ToInt32(row.TimeStart), TimeEnd: convert.Int4ToInt32(row.TimeEnd), Bbox: &response.Bbox{ @@ -430,7 +430,7 @@ func (r *geometryRepository) getSearchByIDsWithFallback(ctx context.Context, pai GeometryID: convert.UUIDToString(row.GeometryID), GeoType: row.GeoType, DrawGeometry: row.DrawGeometry, - Binding: row.Binding, + BoundWith: convert.UUIDToStringPtr(row.BoundWith), TimeStart: convert.Int4ToPtr(row.TimeStart), TimeEnd: convert.Int4ToPtr(row.TimeEnd), } @@ -485,7 +485,7 @@ func (r *geometryRepository) SearchByEntityName(ctx context.Context, params sqlc GeometryID: convert.UUIDToString(row.GeometryID), GeoType: convert.Int2ToInt16(row.GeoType), DrawGeometry: row.DrawGeometry, - Binding: row.Binding, + BoundWith: convert.UUIDToStringPtr(row.BoundWith), TimeStart: convert.Int4ToPtr(row.TimeStart), TimeEnd: convert.Int4ToPtr(row.TimeEnd), } diff --git a/internal/services/geometryService.go b/internal/services/geometryService.go index 28f14d4..eb75690 100644 --- a/internal/services/geometryService.go +++ b/internal/services/geometryService.go @@ -73,6 +73,18 @@ func (s *geometryService) SearchGeometries(ctx context.Context, req *request.Sea params.EntityID = entityId } + if req.ProjectID != nil { + projectId, err := convert.StringToUUID(*req.ProjectID) + if err != nil { + return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid project ID format") + } + params.ProjectID = projectId + } + + if req.HasBound != nil { + params.HasBound = pgtype.Bool{Bool: *req.HasBound, Valid: true} + } + geometries, err := s.geometryRepo.Search(ctx, params) if err != nil { return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to search geometries") @@ -132,7 +144,7 @@ func (s *geometryService) SearchGeometriesByEntityName( ID: row.GeometryID, GeoType: row.GeoType, DrawGeometry: row.DrawGeometry, - Binding: row.Binding, + BoundWith: row.BoundWith, TimeStart: row.TimeStart, TimeEnd: row.TimeEnd, }) diff --git a/internal/services/submissionService.go b/internal/services/submissionService.go index 6d5cb90..04333c6 100644 --- a/internal/services/submissionService.go +++ b/internal/services/submissionService.go @@ -716,7 +716,15 @@ func (s *submissionService) applySnapshot(ctx context.Context, tx pgx.Tx, projec return fiber.NewError(fiber.StatusInternalServerError, "Invalid geometry ID") } - binding, _ := json.Marshal(geo.Binding) + var boundWith pgtype.UUID + if geo.BoundWith != nil && *geo.BoundWith != "" { + var err error + boundWith, err = convert.StringToUUID(*geo.BoundWith) + if err != nil { + return fiber.NewError(fiber.StatusBadRequest, "Invalid bound_with geometry ID") + } + } + geoTypeCode := int16(0) if geo.Type != "" { if n, err := strconv.ParseInt(geo.Type, 10, 16); err == nil { @@ -729,7 +737,7 @@ func (s *submissionService) applySnapshot(ctx context.Context, tx pgx.Tx, projec ID: geometryUUID, GeoType: pgtype.Int2{Int16: geoTypeCode, Valid: true}, DrawGeometry: geo.DrawGeometry, - Binding: binding, + BoundWith: boundWith, TimeStart: convert.PtrFloat64ToInt4(geo.TimeStart), TimeEnd: convert.PtrFloat64ToInt4(geo.TimeEnd), ProjectID: projectUUID, @@ -754,7 +762,7 @@ func (s *submissionService) applySnapshot(ctx context.Context, tx pgx.Tx, projec ID: geometryUUID, GeoType: geoTypeCode, DrawGeometry: geo.DrawGeometry, - Binding: binding, + BoundWith: boundWith, TimeStart: convert.PtrFloat64ToInt4(geo.TimeStart), TimeEnd: convert.PtrFloat64ToInt4(geo.TimeEnd), ProjectID: projectUUID,