diff --git a/config/pg_featureserv.toml.example b/config/pg_featureserv.toml.example index cb35560f..536ae64c 100644 --- a/config/pg_featureserv.toml.example +++ b/config/pg_featureserv.toml.example @@ -68,6 +68,9 @@ WriteTimeoutSec = 30 # Publish functions from these schemas (default is publish postgisftw) # FunctionIncludes = [ "postgisftw", "schema2" ] +# Designate a column as the feature ID (where primary key is not available e.g. views/functions) +# IDColumn = "id" + [Paging] # The default number of features in a response LimitDefault = 20 diff --git a/demo/initdb/02-functions.sql b/demo/initdb/02-functions.sql index 1b185954..49ea88e9 100644 --- a/demo/initdb/02-functions.sql +++ b/demo/initdb/02-functions.sql @@ -19,7 +19,7 @@ BEGIN dlat := (lat_max - lat_min) / num_y; RETURN QUERY SELECT - x.x::text || '_' || y.y::text AS fid, + x.x::text || '_' || y.y::text AS id, ST_MakeEnvelope( lon_min + (x.x - 1) * dlon, lat_min + (y.y - 1) * dlat, lon_min + x.x * dlon, lat_min + y.y * dlat, 4326 @@ -35,7 +35,7 @@ COMMENT ON FUNCTION postgisftw.us_grid IS 'Generates a grid of rectangles coveri CREATE OR REPLACE FUNCTION postgisftw.us_grid_noid( num_x integer DEFAULT 10, num_y integer DEFAULT 10) -RETURNS TABLE(geom geometry) +RETURNS TABLE(value text, geom geometry) AS $$ DECLARE lon_min CONSTANT numeric := -128; @@ -49,6 +49,7 @@ BEGIN dlat := (lat_max - lat_min) / num_y; RETURN QUERY SELECT + x.x::text || '_' || y.y::text AS value, ST_MakeEnvelope( lon_min + (x.x - 1) * dlon, lat_min + (y.y - 1) * dlat, lon_min + x.x * dlon, lat_min + y.y * dlat, 4326 diff --git a/hugo/content/installation/configuration.md b/hugo/content/installation/configuration.md index f0b3273e..77da281c 100644 --- a/hugo/content/installation/configuration.md +++ b/hugo/content/installation/configuration.md @@ -119,6 +119,9 @@ WriteTimeoutSec = 30 # Publish functions from these schemas (default is publish postgisftw) # FunctionIncludes = [ "postgisftw", "schema2" ] +# Designate a column as the feature ID (where primary key is not available e.g. views/functions) +# IDColumn = "id" + [Paging] # The default number of features in a response LimitDefault = 20 @@ -243,6 +246,12 @@ Overrides items specified in `TableIncludes`. A list of the schemas to publish functions from. The default is to publish functions in the `postgisftw` schema. +#### IDColumn + +The column to use as the feature ID in cases where a primary key is not available. The default is `id`. + +> NOTE: The presence of a primary key will supercede this setting. + #### LimitDefault The default number of features in a response, diff --git a/internal/conf/config.go b/internal/conf/config.go index fd81b0ff..d668d17f 100644 --- a/internal/conf/config.go +++ b/internal/conf/config.go @@ -44,6 +44,7 @@ func setDefaultConfig() { viper.SetDefault("Database.TableIncludes", []string{}) viper.SetDefault("Database.TableExcludes", []string{}) viper.SetDefault("Database.FunctionIncludes", []string{"postgisftw"}) + viper.SetDefault("Database.IDColumn", "id") viper.SetDefault("Paging.LimitDefault", 10) viper.SetDefault("Paging.LimitMax", 1000) @@ -94,6 +95,7 @@ type Database struct { TableIncludes []string TableExcludes []string FunctionIncludes []string + IDColumn string } // Metadata config diff --git a/internal/data/catalog_db.go b/internal/data/catalog_db.go index 281b42f3..25bd38eb 100644 --- a/internal/data/catalog_db.go +++ b/internal/data/catalog_db.go @@ -354,6 +354,13 @@ func scanTable(rows pgx.Rows) *Table { colDesc[i] = props.Elements[elmPos+2].String } + // detect ID column if primary key not defined. + if idColumn == "" && conf.Configuration.Database.IDColumn != "" { + if _, ok := datatypes[conf.Configuration.Database.IDColumn]; ok { + idColumn = conf.Configuration.Database.IDColumn + } + } + // Synthesize a title for now title := id // synthesize a description if none provided diff --git a/internal/data/catalog_db_fun.go b/internal/data/catalog_db_fun.go index 1afe2fe0..f5f51a9e 100644 --- a/internal/data/catalog_db_fun.go +++ b/internal/data/catalog_db_fun.go @@ -26,7 +26,6 @@ import ( ) // FunctionIDColumnName is the name for a function-supplied ID -const FunctionIDColumnName = "id" const SchemaPostGISFTW = "postgisftw" @@ -116,6 +115,13 @@ func scanFunctionDef(rows pgx.Rows) *Function { geomCol := geometryColumn(outNames, datatypes) + idColumn := "" + if conf.Configuration.Database.IDColumn != "" { + if _, ok := datatypes[conf.Configuration.Database.IDColumn]; ok { + idColumn = conf.Configuration.Database.IDColumn + } + } + funDef := Function{ ID: id, Schema: schema, @@ -131,6 +137,7 @@ func scanFunctionDef(rows pgx.Rows) *Function { OutJSONTypes: outJSONTypes, Types: datatypes, GeometryColumn: geomCol, + IDColumn: idColumn, } //fmt.Printf("DEBUG: Function definitions: %v\n", funDef) return &funDef @@ -191,7 +198,7 @@ func (cat *catalogDB) FunctionFeatures(ctx context.Context, name string, args ma return nil, errArg } propCols := removeNames(param.Columns, fn.GeometryColumn, "") - idColIndex := indexOfName(propCols, FunctionIDColumnName) + idColIndex := indexOfName(propCols, fn.IDColumn) sql, argValues := sqlGeomFunction(fn, args, propCols, param) log.Debugf("Function features query: %v", sql) log.Debugf("Function %v Args: %v", name, argValues) diff --git a/internal/service/handler.go b/internal/service/handler.go index a0d5fe46..6cbf8161 100644 --- a/internal/service/handler.go +++ b/internal/service/handler.go @@ -636,7 +636,7 @@ func writeFunItemsHTML(w http.ResponseWriter, name string, query string, urlBase context.Group = "Functions" context.Title = fn.ID context.Function = fn - context.IDColumn = data.FunctionIDColumnName + context.IDColumn = fn.IDColumn // features are not needed for items page (page queries for them) return writeHTML(w, nil, context, ui.PageFunctionItems())