Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 69 additions & 0 deletions ats_schema_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -396,3 +396,72 @@ func TestATSSchemaBundleMatchesOGC(t *testing.T) {
ogcBundlePath, ogcBundleURL, len(vendored), len(published))
}
}

// /conf/movingfeatures/tproperty-get-success — the `type` a TemporalProperty
// document names is one the standard defines.
//
// ⛔ THE ADMITTED SET IS READ OUT OF THE VENDORED DOCUMENT, never written here: a
// constant chosen in this file would agree with whatever the tier does and assert
// nothing. The schema states the enum once, at
// components/schemas/temporalProperty/properties/type.
//
// ⛔ THE WHOLE DOCUMENT CANNOT BE VALIDATED THE WAY ITS SIBLINGS ARE, and the
// reason is the standard's, not the tier's: `temporalPrimitiveValue` declares
// `datetimes` an array of `minItems: 2` beside `values` as a single scalar
// (`oneOf` number/string/boolean), so no document carrying a value per instant
// satisfies it; and its `interpolation` enum reads Discrete/Step/Linear/Regression
// where the temporal-geometry half of the same document names Stepwise. This
// asserts the cell that has one authority, which is the type token.
func TestATSSchemaTemporalPropertyType(t *testing.T) {
f, err := os.Open(ogcBundlePath)
if err != nil {
t.Fatal(err)
}
defer f.Close()
var doc struct {
Components struct {
Schemas struct {
TemporalProperty struct {
Properties struct {
Type struct {
Enum []string `json:"enum"`
} `json:"type"`
} `json:"properties"`
} `json:"temporalProperty"`
} `json:"schemas"`
} `json:"components"`
}
if err := json.NewDecoder(f).Decode(&doc); err != nil {
t.Fatalf("reading %s: %v", ogcBundlePath, err)
}
admitted := doc.Components.Schemas.TemporalProperty.Properties.Type.Enum
if len(admitted) == 0 {
t.Fatal("the vendored document declares no type enum, so this test would assert nothing")
}
in := func(s string) bool {
for _, a := range admitted {
if a == s {
return true
}
}
return false
}
// Every spelling the tier accepts, so a token is checked per stored type rather
// than for the one type a single case would happen to cover.
for _, stored := range []string{
"TReal", "tfloat", "measure", "number",
"TInteger", "tint", "integer", "int",
"TText", "tstring", "text", "string",
"TBoolean", "tbool", "boolean", "bool",
} {
tt, ok := tPropType(stored)
if !ok {
t.Errorf("the tier does not resolve the stored type %q", stored)
continue
}
if !in(tt.ogc) {
t.Errorf("a %q property is written as type %q, which the standard does not define; it admits %v",
stored, tt.ogc, admitted)
}
}
}
6 changes: 3 additions & 3 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -704,11 +704,11 @@ func tPropType(t string) (tType, bool) {
case "", "treal", "tfloat", "measure", "real", "float", "double", "number":
return tType{"MovingFloat", "vfloat", "tfloat", "TReal", "Linear"}, true
case "tint", "tinteger", "integer", "int":
return tType{"MovingInteger", "vint", "tint", "TInt", "Step"}, true
return tType{"MovingInteger", "vint", "tint", "TInteger", "Step"}, true
case "ttext", "tstring", "text", "string":
return tType{"MovingText", "vtext", "ttext", "TText", "Discrete"}, true
case "tbool", "tboolean", "boolean", "bool":
return tType{"MovingBoolean", "vbool", "tbool", "TBool", "Step"}, true
return tType{"MovingBoolean", "vbool", "tbool", "TBoolean", "Step"}, true
}
return tType{}, false
}
Expand Down Expand Up @@ -1138,7 +1138,7 @@ func apiDoc(w http.ResponseWriter, r *http.Request) {
"/collections/{cid}/items/{fid}/tproperties": map[string]any{
"get": withParams(op("Stored temporal properties of a feature"),
limitParam, datetimeParam, subTemporalValueParam),
"post": op("Add one or more temporal properties (TReal | TInt | TText | TBool) to a feature"),
"post": op("Add one or more temporal properties (TReal | TInteger | TText | TBoolean) to a feature"),
},
"/collections/{cid}/items/{fid}/tproperties/{pname}": map[string]any{
"get": withParams(op("A stored temporal property as an OGC temporalProperty"),
Expand Down
12 changes: 6 additions & 6 deletions stream.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import (
// Instant is one stream record: a temporal-property value at a timestamp. The
// timestamp is carried verbatim as its ISO-8601 string so it round-trips
// through MEOS without a parse/format step. V holds a numeric value (TReal /
// TInt); S holds a text value (TText) or "t"/"f" (TBool).
// TInteger); S holds a text value (TText) or "t"/"f" (TBoolean).
type Instant struct {
T string `json:"datetime"`
V float64 `json:"value"`
Expand All @@ -51,7 +51,7 @@ type QuerySpec struct {
Arg float64 // operand for scalar ops (add/sub/mul/div); ignored otherwise
Agg string // a window aggregation, e.g. "AVG" or "MAX"
Window Window // the window over which Agg is computed
Ptype string // the property's OGC type (TReal | TInt | TText | TBool)
Ptype string // the property's OGC type (TReal | TInteger | TText | TBoolean)
Interval time.Duration // pacing between emitted records
Engine string // per-query engine override ("flink"|"kafka"|"meos-local"); empty = the process default
}
Expand All @@ -72,10 +72,10 @@ type Window struct {
// the window's values; text and boolean aggregations reduce the MEOS value
// arrays.
var aggByType = map[string]map[string]bool{
"TReal": {"COUNT": true, "SUM": true, "AVG": true, "MIN": true, "MAX": true},
"TInt": {"COUNT": true, "SUM": true, "AVG": true, "MIN": true, "MAX": true},
"TText": {"COUNT": true, "COUNT_DISTINCT": true},
"TBool": {"COUNT": true, "ANY": true, "ALL": true, "COUNT_TRUE": true, "COUNT_FALSE": true},
"TReal": {"COUNT": true, "SUM": true, "AVG": true, "MIN": true, "MAX": true},
"TInteger": {"COUNT": true, "SUM": true, "AVG": true, "MIN": true, "MAX": true},
"TText": {"COUNT": true, "COUNT_DISTINCT": true},
"TBoolean": {"COUNT": true, "ANY": true, "ALL": true, "COUNT_TRUE": true, "COUNT_FALSE": true},
}

// aggregations is the union of all valid aggregation names (the engine validates
Expand Down
2 changes: 1 addition & 1 deletion stream_engine_meos.go
Original file line number Diff line number Diff line change
Expand Up @@ -361,7 +361,7 @@ func windowAggregate(ptype, agg string, win []Instant) (any, int, error) {
switch ptype {
case "TText":
return textAggregate(agg, win)
case "TBool":
case "TBoolean":
return boolAggregate(agg, win)
default:
return numAggregate(agg, win)
Expand Down
2 changes: 1 addition & 1 deletion stream_meos_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ func TestMeosTextBoolAggregate(t *testing.T) {
}{{"ANY", true}, {"ALL", false}, {"COUNT_TRUE", 1.0}} {
ctx, cancel := context.WithCancel(context.Background())
src := make(chan Instant, 3)
h, err := e.Submit(ctx, QuerySpec{Ptype: "TBool", Agg: c.agg, Window: Window{Type: "COUNT", Size: 3}}, src)
h, err := e.Submit(ctx, QuerySpec{Ptype: "TBoolean", Agg: c.agg, Window: Window{Type: "COUNT", Size: 3}}, src)
if err != nil {
t.Fatal(err)
}
Expand Down
8 changes: 4 additions & 4 deletions tproperties_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@ func TestTPropType(t *testing.T) {
"": {"MovingFloat", "vfloat", "tfloat", "TReal", "Linear"},
"TReal": {"MovingFloat", "vfloat", "tfloat", "TReal", "Linear"},
"measure": {"MovingFloat", "vfloat", "tfloat", "TReal", "Linear"},
"TInt": {"MovingInteger", "vint", "tint", "TInt", "Step"},
"integer": {"MovingInteger", "vint", "tint", "TInt", "Step"},
"TInt": {"MovingInteger", "vint", "tint", "TInteger", "Step"},
"integer": {"MovingInteger", "vint", "tint", "TInteger", "Step"},
"TText": {"MovingText", "vtext", "ttext", "TText", "Discrete"},
"string": {"MovingText", "vtext", "ttext", "TText", "Discrete"},
"TBool": {"MovingBoolean", "vbool", "tbool", "TBool", "Step"},
"BOOLEAN ": {"MovingBoolean", "vbool", "tbool", "TBool", "Step"},
"TBool": {"MovingBoolean", "vbool", "tbool", "TBoolean", "Step"},
"BOOLEAN ": {"MovingBoolean", "vbool", "tbool", "TBoolean", "Step"},
}
for in, want := range cases {
got, ok := tPropType(in)
Expand Down
Loading