1
1
"""Core client."""
2
2
3
3
import logging
4
+ from collections import deque
4
5
from datetime import datetime as datetime_type
5
6
from datetime import timezone
6
7
from enum import Enum
7
- from typing import Any , Dict , List , Optional , Set , Type , Union
8
+ from typing import Any , Dict , List , Literal , Optional , Set , Type , Union
8
9
from urllib .parse import unquote_plus , urljoin
9
10
10
11
import attr
@@ -905,11 +906,81 @@ def bulk_item_insert(
905
906
return f"Successfully added { len (processed_items )} Items."
906
907
907
908
909
+ _DEFAULT_QUERYABLES = {
910
+ "id" : {
911
+ "description" : "ID" ,
912
+ "$ref" : "https://schemas.stacspec.org/v1.0.0/item-spec/json-schema/item.json#/definitions/core/allOf/2/properties/id" ,
913
+ },
914
+ "collection" : {
915
+ "description" : "Collection" ,
916
+ "$ref" : "https://schemas.stacspec.org/v1.0.0/item-spec/json-schema/item.json#/definitions/core/allOf/2/then/properties/collection" ,
917
+ },
918
+ "geometry" : {
919
+ "description" : "Geometry" ,
920
+ "$ref" : "https://schemas.stacspec.org/v1.0.0/item-spec/json-schema/item.json#/definitions/core/allOf/1/oneOf/0/properties/geometry" ,
921
+ },
922
+ "datetime" : {
923
+ "description" : "Acquisition Timestamp" ,
924
+ "$ref" : "https://schemas.stacspec.org/v1.0.0/item-spec/json-schema/datetime.json#/properties/datetime" ,
925
+ },
926
+ "created" : {
927
+ "description" : "Creation Timestamp" ,
928
+ "$ref" : "https://schemas.stacspec.org/v1.0.0/item-spec/json-schema/datetime.json#/properties/created" ,
929
+ },
930
+ "updated" : {
931
+ "description" : "Creation Timestamp" ,
932
+ "$ref" : "https://schemas.stacspec.org/v1.0.0/item-spec/json-schema/datetime.json#/properties/updated" ,
933
+ },
934
+ "cloud_cover" : {
935
+ "description" : "Cloud Cover" ,
936
+ "$ref" : "https://stac-extensions.github.io/eo/v1.0.0/schema.json#/definitions/fields/properties/eo:cloud_cover" ,
937
+ },
938
+ "cloud_shadow_percentage" : {
939
+ "title" : "Cloud Shadow Percentage" ,
940
+ "description" : "Cloud Shadow Percentage" ,
941
+ "type" : "number" ,
942
+ "minimum" : 0 ,
943
+ "maximum" : 100 ,
944
+ },
945
+ "nodata_pixel_percentage" : {
946
+ "title" : "No Data Pixel Percentage" ,
947
+ "description" : "No Data Pixel Percentage" ,
948
+ "type" : "number" ,
949
+ "minimum" : 0 ,
950
+ "maximum" : 100 ,
951
+ },
952
+ }
953
+
954
+ _ES_MAPPING_TYPE_TO_JSON : Dict [
955
+ str , Literal ["string" , "number" , "boolean" , "object" , "array" , "null" ]
956
+ ] = {
957
+ "date" : "string" ,
958
+ "date_nanos" : "string" ,
959
+ "keyword" : "string" ,
960
+ "match_only_text" : "string" ,
961
+ "text" : "string" ,
962
+ "wildcard" : "string" ,
963
+ "byte" : "number" ,
964
+ "double" : "number" ,
965
+ "float" : "number" ,
966
+ "half_float" : "number" ,
967
+ "long" : "number" ,
968
+ "scaled_float" : "number" ,
969
+ "short" : "number" ,
970
+ "token_count" : "number" ,
971
+ "unsigned_long" : "number" ,
972
+ "geo_point" : "object" ,
973
+ "geo_shape" : "object" ,
974
+ "nested" : "array" ,
975
+ }
976
+
977
+
908
978
@attr .s
909
979
class EsAsyncBaseFiltersClient (AsyncBaseFiltersClient ):
910
980
"""Defines a pattern for implementing the STAC filter extension."""
911
981
912
- # todo: use the ES _mapping endpoint to dynamically find what fields exist
982
+ database : BaseDatabaseLogic = attr .ib ()
983
+
913
984
async def get_queryables (
914
985
self , collection_id : Optional [str ] = None , ** kwargs
915
986
) -> Dict [str , Any ]:
@@ -930,55 +1001,63 @@ async def get_queryables(
930
1001
Returns:
931
1002
Dict[str, Any]: A dictionary containing the queryables for the given collection.
932
1003
"""
933
- return {
1004
+ queryables : Dict [ str , Any ] = {
934
1005
"$schema" : "https://json-schema.org/draft/2019-09/schema" ,
935
1006
"$id" : "https://stac-api.example.com/queryables" ,
936
1007
"type" : "object" ,
937
- "title" : "Queryables for Example STAC API" ,
938
- "description" : "Queryable names for the example STAC API Item Search filter." ,
939
- "properties" : {
940
- "id" : {
941
- "description" : "ID" ,
942
- "$ref" : "https://schemas.stacspec.org/v1.0.0/item-spec/json-schema/item.json#/definitions/core/allOf/2/properties/id" ,
943
- },
944
- "collection" : {
945
- "description" : "Collection" ,
946
- "$ref" : "https://schemas.stacspec.org/v1.0.0/item-spec/json-schema/item.json#/definitions/core/allOf/2/then/properties/collection" ,
947
- },
948
- "geometry" : {
949
- "description" : "Geometry" ,
950
- "$ref" : "https://schemas.stacspec.org/v1.0.0/item-spec/json-schema/item.json#/definitions/core/allOf/1/oneOf/0/properties/geometry" ,
951
- },
952
- "datetime" : {
953
- "description" : "Acquisition Timestamp" ,
954
- "$ref" : "https://schemas.stacspec.org/v1.0.0/item-spec/json-schema/datetime.json#/properties/datetime" ,
955
- },
956
- "created" : {
957
- "description" : "Creation Timestamp" ,
958
- "$ref" : "https://schemas.stacspec.org/v1.0.0/item-spec/json-schema/datetime.json#/properties/created" ,
959
- },
960
- "updated" : {
961
- "description" : "Creation Timestamp" ,
962
- "$ref" : "https://schemas.stacspec.org/v1.0.0/item-spec/json-schema/datetime.json#/properties/updated" ,
963
- },
964
- "cloud_cover" : {
965
- "description" : "Cloud Cover" ,
966
- "$ref" : "https://stac-extensions.github.io/eo/v1.0.0/schema.json#/definitions/fields/properties/eo:cloud_cover" ,
967
- },
968
- "cloud_shadow_percentage" : {
969
- "description" : "Cloud Shadow Percentage" ,
970
- "title" : "Cloud Shadow Percentage" ,
971
- "type" : "number" ,
972
- "minimum" : 0 ,
973
- "maximum" : 100 ,
974
- },
975
- "nodata_pixel_percentage" : {
976
- "description" : "No Data Pixel Percentage" ,
977
- "title" : "No Data Pixel Percentage" ,
978
- "type" : "number" ,
979
- "minimum" : 0 ,
980
- "maximum" : 100 ,
981
- },
982
- },
1008
+ "title" : "Queryables for STAC API" ,
1009
+ "description" : "Queryable names for the STAC API Item Search filter." ,
1010
+ "properties" : _DEFAULT_QUERYABLES ,
983
1011
"additionalProperties" : True ,
984
1012
}
1013
+ if not collection_id :
1014
+ return queryables
1015
+
1016
+ properties = {}
1017
+ queryables .update (
1018
+ {
1019
+ "properties" : properties ,
1020
+ "additionalProperties" : False ,
1021
+ }
1022
+ )
1023
+
1024
+ mapping_data = await self .database .get_items_mapping (collection_id )
1025
+ mapping_properties = next (iter (mapping_data .values ()))["mappings" ]["properties" ]
1026
+ stack = deque (mapping_properties .items ())
1027
+
1028
+ while stack :
1029
+ field_name , field_def = stack .popleft ()
1030
+
1031
+ # Iterate over nested fields
1032
+ field_properties = field_def .get ("properties" )
1033
+ if field_properties :
1034
+ # Fields in Item Properties should be exposed with their un-prefixed names,
1035
+ # and not require expressions to prefix them with properties,
1036
+ # e.g., eo:cloud_cover instead of properties.eo:cloud_cover.
1037
+ if field_name == "properties" :
1038
+ stack .extend (field_properties .items ())
1039
+ else :
1040
+ stack .extend (
1041
+ (f"{ field_name } .{ k } " , v ) for k , v in field_properties .items ()
1042
+ )
1043
+
1044
+ # Skip non-indexed or disabled fields
1045
+ field_type = field_def .get ("type" )
1046
+ if not field_type or not field_def .get ("enabled" , True ):
1047
+ continue
1048
+
1049
+ # Generate field properties
1050
+ field_result = _DEFAULT_QUERYABLES .get (field_name , {})
1051
+ properties [field_name ] = field_result
1052
+
1053
+ field_name_human = field_name .replace ("_" , " " ).title ()
1054
+ field_result .setdefault ("title" , field_name_human )
1055
+ field_result .setdefault ("description" , field_name_human )
1056
+
1057
+ field_type_json = _ES_MAPPING_TYPE_TO_JSON .get (field_type , field_type )
1058
+ field_result .setdefault ("type" , field_type_json )
1059
+
1060
+ if field_type in {"date" , "date_nanos" }:
1061
+ field_result .setdefault ("format" , "date-time" )
1062
+
1063
+ return queryables
0 commit comments