11"""Core client."""
22
33import logging
4+ from collections import deque
45from datetime import datetime as datetime_type
56from datetime import timezone
67from 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
89from urllib .parse import unquote_plus , urljoin
910
1011import attr
@@ -905,11 +906,81 @@ def bulk_item_insert(
905906 return f"Successfully added { len (processed_items )} Items."
906907
907908
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+
908978@attr .s
909979class EsAsyncBaseFiltersClient (AsyncBaseFiltersClient ):
910980 """Defines a pattern for implementing the STAC filter extension."""
911981
912- # todo: use the ES _mapping endpoint to dynamically find what fields exist
982+ database : BaseDatabaseLogic = attr .ib ()
983+
913984 async def get_queryables (
914985 self , collection_id : Optional [str ] = None , ** kwargs
915986 ) -> Dict [str , Any ]:
@@ -930,55 +1001,63 @@ async def get_queryables(
9301001 Returns:
9311002 Dict[str, Any]: A dictionary containing the queryables for the given collection.
9321003 """
933- return {
1004+ queryables : Dict [ str , Any ] = {
9341005 "$schema" : "https://json-schema.org/draft/2019-09/schema" ,
9351006 "$id" : "https://stac-api.example.com/queryables" ,
9361007 "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 ,
9831011 "additionalProperties" : True ,
9841012 }
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