Skip to content

Commit 8fea7f9

Browse files
Updated field annotations for queryables, roles, and json_path (#61)
1 parent f6b9b3f commit 8fea7f9

3 files changed

Lines changed: 166 additions & 11 deletions

File tree

‎apis/datasets/v1/data_access.proto‎

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ package datasets.v1;
77
import "buf/validate/validate.proto";
88
import "datasets/v1/core.proto";
99
import "datasets/v1/well_known_types.proto";
10+
import "google/protobuf/duration.proto";
11+
import "google/protobuf/timestamp.proto";
1012
import "tilebox/v1/id.proto";
1113
import "tilebox/v1/query.proto";
1214

@@ -41,6 +43,125 @@ message QueryFilters {
4143
tilebox.v1.IDInterval datapoint_interval = 2;
4244

4345
SpatialFilter spatial_extent = 3;
46+
47+
// Additional expressions over fields marked queryable in the dataset schema.
48+
// All top-level expressions are combined with each other and with the interval and spatial filters using logical AND.
49+
// Use a nested LogicalExpression for explicit OR, NOT, or grouped boolean logic.
50+
repeated FilterExpression expressions = 4 [(buf.validate.field).repeated.max_items = 100];
51+
}
52+
53+
// FilterExpression is a typed filter expression compatible with the Basic CQL2 property-to-literal subset.
54+
message FilterExpression {
55+
option (buf.validate.message).oneof = {
56+
fields: [
57+
"logical",
58+
"comparison",
59+
"is_null"
60+
]
61+
required: true
62+
};
63+
64+
// Exactly one expression node must be set.
65+
LogicalExpression logical = 1;
66+
FieldComparison comparison = 2;
67+
FieldNullCheck is_null = 3;
68+
}
69+
70+
// LogicalExpression combines nested filter expressions.
71+
message LogicalExpression {
72+
option (buf.validate.message).cel = {
73+
id: "logical_expression.operand_arity"
74+
message: "NOT requires exactly one operand; AND and OR require at least two"
75+
expression: "this.operator == 3 ? this.operands.size() == 1 : this.operands.size() >= 2"
76+
};
77+
78+
// The logical operation to apply to operands.
79+
LogicalOperator operator = 1 [(buf.validate.field).enum = {
80+
defined_only: true
81+
not_in: [0]
82+
}];
83+
// Operands for the logical operation. AND and OR require at least two; NOT requires exactly one.
84+
repeated FilterExpression operands = 2 [(buf.validate.field).repeated = {
85+
min_items: 1
86+
max_items: 100
87+
}];
88+
}
89+
90+
// LogicalOperator specifies how nested filter expressions are combined.
91+
enum LogicalOperator {
92+
LOGICAL_OPERATOR_UNSPECIFIED = 0;
93+
LOGICAL_OPERATOR_AND = 1;
94+
LOGICAL_OPERATOR_OR = 2;
95+
LOGICAL_OPERATOR_NOT = 3;
96+
}
97+
98+
// FieldComparison compares a queryable dataset field to a typed literal value.
99+
message FieldComparison {
100+
// Name of a top-level field in the dataset schema.
101+
string field_name = 1 [(buf.validate.field).string = {
102+
min_len: 1
103+
max_len: 100
104+
pattern: "^[a-z][a-z0-9_]*$"
105+
}];
106+
// Comparison operation to apply.
107+
FieldComparisonOperator operator = 2 [(buf.validate.field).enum = {
108+
defined_only: true
109+
not_in: [0]
110+
}];
111+
// Literal value. Its kind must match the field's protobuf descriptor.
112+
FieldQueryValue value = 3 [(buf.validate.field).required = true];
113+
}
114+
115+
// FieldComparisonOperator specifies a Basic CQL2 comparison operation.
116+
enum FieldComparisonOperator {
117+
FIELD_COMPARISON_OPERATOR_UNSPECIFIED = 0;
118+
FIELD_COMPARISON_OPERATOR_EQUAL = 1;
119+
FIELD_COMPARISON_OPERATOR_NOT_EQUAL = 2;
120+
FIELD_COMPARISON_OPERATOR_LESS_THAN = 3;
121+
FIELD_COMPARISON_OPERATOR_LESS_THAN_OR_EQUAL = 4;
122+
FIELD_COMPARISON_OPERATOR_GREATER_THAN = 5;
123+
FIELD_COMPARISON_OPERATOR_GREATER_THAN_OR_EQUAL = 6;
124+
}
125+
126+
// FieldNullCheck matches datapoints for which a queryable field is absent or explicitly null.
127+
// IS NOT NULL is represented by wrapping this expression in LOGICAL_OPERATOR_NOT.
128+
message FieldNullCheck {
129+
// Name of a top-level field in the dataset schema.
130+
string field_name = 1 [(buf.validate.field).string = {
131+
min_len: 1
132+
max_len: 100
133+
pattern: "^[a-z][a-z0-9_]*$"
134+
}];
135+
}
136+
137+
// FieldQueryValue is a typed scalar or well-known-type literal used in a field comparison.
138+
message FieldQueryValue {
139+
option (buf.validate.message).oneof = {
140+
fields: [
141+
"bool_value",
142+
"int64_value",
143+
"uint64_value",
144+
"double_value",
145+
"string_value",
146+
"timestamp_value",
147+
"duration_value",
148+
"enum_name",
149+
"bytes_value"
150+
]
151+
required: true
152+
};
153+
154+
// Exactly one typed literal must be set. Explicit presence preserves zero and empty values.
155+
bool bool_value = 1 [features.field_presence = EXPLICIT];
156+
int64 int64_value = 2 [features.field_presence = EXPLICIT];
157+
uint64 uint64_value = 3 [features.field_presence = EXPLICIT];
158+
double double_value = 4 [features.field_presence = EXPLICIT];
159+
string string_value = 5 [features.field_presence = EXPLICIT];
160+
google.protobuf.Timestamp timestamp_value = 6;
161+
google.protobuf.Duration duration_value = 7;
162+
// Symbolic value from the field's enum descriptor.
163+
string enum_name = 8 [features.field_presence = EXPLICIT];
164+
bytes bytes_value = 9 [features.field_presence = EXPLICIT];
44165
}
45166

46167
// SpatialFilterMode specifies how geometries are compared to a given spatial filter.

‎apis/datasets/v1/dataset_type.proto‎

Lines changed: 43 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,19 +12,19 @@ import "tilebox/v1/id.proto";
1212

1313
option features.field_presence = IMPLICIT;
1414

15-
// Field describes a field of a dataset.
15+
// Field is the request representation of a dataset field. It intentionally combines only a protobuf field descriptor
16+
// with its Tilebox annotation, matching the information represented separately by AnnotatedType.
1617
message Field {
18+
reserved 3;
19+
reserved queryable;
20+
1721
// The descriptor contains the name of the field, the type, optional labels (e.g. repeated) and other information.
1822
// If the type is TYPE_MESSAGE, then the type_name must be a fully qualified name to a well known type, e.g.
1923
// `datasets.v1.Vec3` or `google.protobuf.Timestamp`.
2024
google.protobuf.FieldDescriptorProto descriptor = 1 [(buf.validate.field).required = true];
2125

22-
// An optional description and example value for the field.
26+
// Additional documentation, source mapping, and query metadata for the field.
2327
FieldAnnotation annotation = 2;
24-
25-
// A flag indicating whether the field should be queryable. This means we will build an index for the field, and
26-
// allow users to query for certain values server-side.
27-
bool queryable = 3 [(buf.validate.field).bool.const = false];
2828
}
2929

3030
// DatasetKind is an enum describing the kind of dataset. A dataset kind specifies a set of default fields, that
@@ -40,13 +40,44 @@ enum DatasetKind {
4040
DATASET_KIND_SPATIOTEMPORAL = 2;
4141
}
4242

43-
// FieldAnnotation contains additional information about a field in a dataset
43+
// FieldRole describes a semantic display role fulfilled by a dataset field.
44+
// Thumbnail discovery is intentionally not represented here: thumbnails are assets whose STAC asset roles include
45+
// thumbnail.
46+
enum FieldRole {
47+
FIELD_ROLE_UNSPECIFIED = 0;
48+
49+
// The primary human-readable title or name used when displaying a datapoint.
50+
FIELD_ROLE_PRIMARY_TITLE = 1;
51+
}
52+
53+
// FieldAnnotation contains additional information about a field in a dataset.
4454
message FieldAnnotation {
4555
string description = 1;
4656
string example_value = 2;
57+
58+
// RFC 6901 JSON Pointer locating the field value in the source document.
59+
// This allows flattened dataset fields to be reconstructed without relying on their protobuf field names.
60+
string source_json_pointer = 3 [features.field_presence = EXPLICIT];
61+
62+
// Whether the field is projected into query storage and may be used in server-side filter expressions.
63+
// Queryable fields are not necessarily backed by a secondary database index.
64+
bool queryable = 4;
65+
66+
// Optional JSON Schema reference to emit as `$ref` when advertising this field as a queryable.
67+
// For example, this may reference the canonical STAC extension schema definition for a property.
68+
string queryable_json_schema_ref = 5 [features.field_presence = EXPLICIT];
69+
70+
// Semantic display roles fulfilled by this field.
71+
repeated FieldRole roles = 6 [
72+
(buf.validate.field).repeated.unique = true,
73+
(buf.validate.field).repeated.items.enum = {
74+
defined_only: true
75+
not_in: [0]
76+
}
77+
];
4778
}
4879

49-
// DatasetType describes the type of a dataset.
80+
// DatasetType is the request representation of a dataset type, used when creating or updating a dataset.
5081
message DatasetType {
5182
// kind denotes the kind of dataset this type describes. We do not rely on the default fields to be set in our
5283
// array of fields - since that way users could circumvent those by manually sending requests not from our Console.
@@ -59,12 +90,15 @@ message DatasetType {
5990
repeated Field fields = 2;
6091
}
6192

62-
// AnnotatedType describes a message type
93+
// AnnotatedType is the resolved representation returned on Dataset messages, including GetDataset responses.
94+
// It combines the generated protobuf descriptors with the corresponding Tilebox field annotations.
6395
message AnnotatedType {
6496
google.protobuf.FileDescriptorSet descriptor_set = 1;
6597
// the url of the type, one of the types defined in the descriptor
6698
string type_url = 2;
6799
reserved 3;
100+
// Annotations corresponding by index to the FieldDescriptorProto entries of the message selected by type_url.
101+
// Empty annotations must be retained so this positional mapping remains stable.
68102
repeated FieldAnnotation field_annotations = 4;
69103
DatasetKind kind = 5;
70104
}

‎apis/datasets/v1/datasets.proto‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ message CreateDatasetRequest {
1919
(buf.validate.field).string.min_len = 1,
2020
(buf.validate.field).string.max_len = 100
2121
];
22-
// message type of the dataset to create.
22+
// Message type of the dataset to create, including per-field source mappings, queryable metadata, and roles.
2323
DatasetType type = 2 [(buf.validate.field).required = true];
2424
// short text summary of the dataset to create.
2525
string summary = 3 [(buf.validate.field).string.max_len = 50000];
@@ -62,7 +62,7 @@ message UpdateDatasetRequest {
6262
(buf.validate.field).string.min_len = 1,
6363
(buf.validate.field).string.max_len = 100
6464
];
65-
// updated type of the dataset.
65+
// Updated type of the dataset, including per-field source mappings, queryable metadata, and roles.
6666
DatasetType type = 3 [features.field_presence = EXPLICIT];
6767
// updated summary of the dataset.
6868
string summary = 4 [

0 commit comments

Comments
 (0)