Skip to content

Commit

Permalink
Merge pull request #179 from EURODEO/extent-performance
Browse files Browse the repository at this point in the history
Remove filtering parameters from GetExtents.
  • Loading branch information
jo-asplin-met-no authored Sep 20, 2024
2 parents 4a67548 + d0a8170 commit b731890
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 64 deletions.
109 changes: 52 additions & 57 deletions datastore/datastore/storagebackend/postgresql/getextents.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,85 +5,80 @@ import (
"datastore/common"
"datastore/datastore"
"fmt"
"time"

_ "github.com/lib/pq"
"google.golang.org/grpc/codes"
"google.golang.org/protobuf/types/known/timestamppb"
)

// createExtQueryVals creates from request values used for querying extentions.
//
// Values to be used for query placeholders are appended to phVals.
//
// Upon success the function returns two values:
// - time filter used in a 'WHERE ... AND ...' clause
// - string metadata ... ditto
// - nil,
// otherwise (..., ..., error).
func createExtQueryVals(request *datastore.GetExtentsRequest, phVals *[]interface{}) (
string, string, error) {
// getTemporalExtent gets the current temporal extent of all observations in the storage.
func getTemporalExtent(db *sql.DB) (*datastore.TimeInterval, error) {
query := "SELECT min(obstime_instant), max(obstime_instant) FROM observation"
row := db.QueryRow(query)

var start, end time.Time

err := row.Scan(&start, &end)
if err != nil {
return nil, fmt.Errorf("row.Scan() failed: %v", err)
}

loTime, hiTime := common.GetValidTimeRange()
timeFilter := fmt.Sprintf(`
((obstime_instant >= to_timestamp(%d)) AND (obstime_instant <= to_timestamp(%d)))
`, loTime.Unix(), hiTime.Unix())
if start.Before(loTime) {
start = loTime
}
if end.After(hiTime) {
end = hiTime
}

stringMdataFilter, err := getStringMdataFilter(request.GetFilter(), phVals)
return &datastore.TimeInterval{
Start: timestamppb.New(start),
End: timestamppb.New(end),
}, nil
}

// getSpatialExtent gets the current horizontally spatial extent of all observations in the storage.
func getSpatialExtent(db *sql.DB) (*datastore.BoundingBox, error) {
query := `
SELECT ST_XMin(ext), ST_YMin(ext), ST_XMax(ext), ST_YMax(ext)
FROM (SELECT ST_Extent(point::geometry) AS ext FROM geo_point) t
`
row := db.QueryRow(query)

var xmin, ymin, xmax, ymax float64

err := row.Scan(&xmin, &ymin, &xmax, &ymax)
if err != nil {
return "", "", fmt.Errorf("getStringMdataFilter() failed: %v", err)
return nil, fmt.Errorf("row.Scan() failed: %v", err)
}

return timeFilter, stringMdataFilter, nil
return &datastore.BoundingBox{
Left: xmin,
Bottom: ymin,
Right: xmax,
Top: ymax,
}, nil
}

// GetExtents ... (see documentation in StorageBackend interface)
func (sbe *PostgreSQL) GetExtents(request *datastore.GetExtentsRequest) (
func (sbe *PostgreSQL) GetExtents(_ *datastore.GetExtentsRequest) (
*datastore.GetExtentsResponse, codes.Code, string) {

// get values needed for query
phVals := []interface{}{} // placeholder values
timeFilter, stringMdataFilter, err := createExtQueryVals(request, &phVals)
var err error

temporalExtent, err := getTemporalExtent(sbe.Db)
if err != nil {
return nil, codes.Internal, fmt.Sprintf("createQueryVals() failed: %v", err)
return nil, codes.Internal, fmt.Sprintf("getTemporalExtent() failed: %v", err)
}

query := fmt.Sprintf(`
SELECT temp_min, temp_max,
ST_XMin(spat_ext), ST_YMin(spat_ext), ST_XMax(spat_ext), ST_YMax(spat_ext)
FROM (
SELECT min(obstime_instant) AS temp_min, max(obstime_instant) AS temp_max,
ST_Extent(point::geometry) AS spat_ext
FROM observation
JOIN time_series ON observation.ts_id = time_series.id
JOIN geo_point ON observation.geo_point_id = geo_point.id
WHERE %s AND %s
) t
`, timeFilter, stringMdataFilter)

row := sbe.Db.QueryRow(query, phVals...)

var (
start, end sql.NullTime
xmin, ymin, xmax, ymax float64
)

err = row.Scan(&start, &end, &xmin, &ymin, &xmax, &ymax)
if !start.Valid { // indicates no matching rows found!
return nil, codes.NotFound, "no matching data to compute extensions for"
} else if err != nil {
return nil, codes.Internal, fmt.Sprintf("row.Scan() failed: %v", err)
spatialExtent, err := getSpatialExtent(sbe.Db)
if err != nil {
return nil, codes.Internal, fmt.Sprintf("getSpatialExtent() failed: %v", err)
}

return &datastore.GetExtentsResponse{
TemporalExtent: &datastore.TimeInterval{
Start: timestamppb.New(start.Time),
End: timestamppb.New(end.Time),
},
SpatialExtent: &datastore.BoundingBox{
Left: xmin,
Bottom: ymin,
Right: xmax,
Top: ymax,
},
TemporalExtent: temporalExtent,
SpatialExtent: spatialExtent,
}, codes.OK, ""
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
DROP INDEX CONCURRENTLY observation_obstim_instant;
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
CREATE INDEX CONCURRENTLY observation_obstime_instant ON observation (obstime_instant);
8 changes: 1 addition & 7 deletions protobuf/datastore.proto
Original file line number Diff line number Diff line change
Expand Up @@ -257,13 +257,7 @@ message GetTSAGResponse {
//---------------------------------------------------------------------------

message GetExtentsRequest {
// --- BEGIN string metadata (handleable with reflection) -------------------------

// general filter
// - map keys must correspond exactly with string field names in TSMetadata or ObsMetadata
// - if map key F is specified (where F is for example 'platform'), only observations that match
// at least one of these values for F may be returned
map<string, Strings> filter = 1;
// No parameters

// --- END string metadata (handleable with reflection) -------------------------
}
Expand Down

2 comments on commit b731890

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

API Unit Test Coverage Report
FileStmtsMissCoverMissing
\_\_init\_\_.py00100% 
datastore_pb2.py614821%34–81
datastore_pb2_grpc.py542750%15–16, 19, 57–72, 105–107, 112–114, 119–121, 126–128, 132–157, 195, 222, 249, 276
export_metrics.py100100% 
grpc_getter.py201145%15–19, 23–26, 30–32, 36–38
locustfile.py15150%1–31
main.py43784%45, 50, 60, 70–71, 81–82
metadata_endpoints.py632954%45–54, 58, 85, 100–216, 220
response_classes.py50100% 
utilities.py1815669%20, 38, 45, 67–70, 78–89, 94–101, 121, 125, 127, 154, 157, 166, 184–185, 189, 205–210, 215, 219–220, 224–226, 232–240, 250–253, 259–260, 272–276, 299, 304, 317
custom_geo_json
   edr_feature_collection.py60100% 
formatters
   \_\_init\_\_.py110100% 
   covjson.py57198%88
   geojson.py17288%21, 46
openapi
   custom_dimension_examples.py40100% 
   edr_query_parameter_descriptions.py110100% 
   openapi_examples.py130100% 
routers
   \_\_init\_\_.py00100% 
   edr.py102496%350–351, 441–442
   feature.py471960%99–132, 148–153, 159–181
TOTAL72021970% 

API Unit Test Coverage Summary

Tests Skipped Failures Errors Time
30 0 💤 0 ❌ 0 🔥 1.924s ⏱️

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

API Unit Test Coverage Report
FileStmtsMissCoverMissing
\_\_init\_\_.py00100% 
datastore_pb2.py614821%34–81
datastore_pb2_grpc.py542750%15–16, 19, 57–72, 105–107, 112–114, 119–121, 126–128, 132–157, 195, 222, 249, 276
export_metrics.py100100% 
grpc_getter.py201145%15–19, 23–26, 30–32, 36–38
locustfile.py15150%1–31
main.py43784%45, 50, 60, 70–71, 81–82
metadata_endpoints.py632954%45–54, 58, 85, 100–216, 220
response_classes.py50100% 
utilities.py1815669%20, 38, 45, 67–70, 78–89, 94–101, 121, 125, 127, 154, 157, 166, 184–185, 189, 205–210, 215, 219–220, 224–226, 232–240, 250–253, 259–260, 272–276, 299, 304, 317
custom_geo_json
   edr_feature_collection.py60100% 
formatters
   \_\_init\_\_.py110100% 
   covjson.py57198%88
   geojson.py17288%21, 46
openapi
   custom_dimension_examples.py40100% 
   edr_query_parameter_descriptions.py110100% 
   openapi_examples.py130100% 
routers
   \_\_init\_\_.py00100% 
   edr.py102496%350–351, 441–442
   feature.py471960%99–132, 148–153, 159–181
TOTAL72021970% 

API Unit Test Coverage Summary

Tests Skipped Failures Errors Time
30 0 💤 0 ❌ 0 🔥 1.928s ⏱️

Please sign in to comment.