@@ -1116,6 +1116,63 @@ def service_request_async(self, service, params, *, pagesize=None, page=None, **
11161116
11171117 return self ._portal_api_connection .service_request_async (service , params , pagesize , page , ** kwargs )
11181118
1119+ def _normalize_filter_value (self , key : str , val ) -> list :
1120+ """
1121+ Normalize a filter value into a list suitable for MAST filters.
1122+
1123+ Parameters
1124+ ----------
1125+ key : str
1126+ Parameter name (used for error messages).
1127+ val : any
1128+ Raw filter value.
1129+
1130+ Returns
1131+ -------
1132+ list
1133+ Normalized filter values.
1134+ """
1135+ # Range filters must be dicts with 'min' and 'max'
1136+ if isinstance (val , dict ):
1137+ if not {"min" , "max" }.issubset (val .keys ()):
1138+ raise InvalidQueryError (
1139+ f'Range filter for "{ key } " must be a dictionary with "min" and "max" keys.'
1140+ )
1141+ return [val ]
1142+
1143+ # Convert numpy arrays to lists
1144+ if isinstance (val , np .ndarray ):
1145+ val = val .tolist ()
1146+
1147+ # Convert numpy arrays, sets, or tuples to lists
1148+ if isinstance (val , (set , tuple )):
1149+ val = list (val )
1150+
1151+ # Wrap scalars into a list
1152+ return val if isinstance (val , list ) else [val ]
1153+
1154+ def _build_filters (self , service_params ):
1155+ """
1156+ Construct filters for filtered services.
1157+
1158+ Parameters
1159+ ----------
1160+ service_params : dict
1161+ Parameters not classified as request/position keys.
1162+
1163+ Returns
1164+ -------
1165+ list of dict
1166+ Filters suitable for a MAST filtered query.
1167+ """
1168+ filters = []
1169+ for key , val in service_params .items ():
1170+ filters .append ({
1171+ "paramName" : key ,
1172+ "values" : self ._normalize_filter_value (key , val )
1173+ })
1174+ return filters
1175+
11191176 def mast_query (self , service , columns = None , ** kwargs ):
11201177 """
11211178 Given a Mashup service and parameters as keyword arguments, builds and excecutes a Mashup query.
@@ -1124,53 +1181,57 @@ def mast_query(self, service, columns=None, **kwargs):
11241181 ----------
11251182 service : str
11261183 The Mashup service to query.
1127- columns : str, optional
1184+ columns : str or list , optional
11281185 Specifies the columns to be returned as a comma-separated list, e.g. "ID, ra, dec".
11291186 **kwargs :
11301187 Service-specific parameters and MashupRequest properties. See the
11311188 `service documentation <https://mast.stsci.edu/api/v0/_services.html>`__ and the
11321189 `MashupRequest Class Reference <https://mast.stsci.edu/api/v0/class_mashup_1_1_mashup_request.html>`__
11331190 for valid keyword arguments.
11341191
1192+ For filtered services (i.e. those with "filtered" in the service name),
1193+ parameters that are not related to position or MashupRequest properties
1194+ are treated as filters. If the column has discrete values, the parameter value should be a
1195+ single value or list of values, and values will be matched exactly. If the column is continuous,
1196+ you can filter by a single value, a list of values, or a range of values. If filtering by a range of values,
1197+ the parameter value should be a dict in the form ``{'min': minVal, 'max': maxVal}``.
1198+
11351199 Returns
11361200 -------
11371201 response : `~astropy.table.Table`
11381202 """
11391203 # Specific keywords related to positional and MashupRequest parameters.
1140- position_keys = [ 'ra' , 'dec' , 'radius' , 'position' ]
1141- request_keys = [ 'format' , 'data' , 'filename' , 'timeout' , 'clearcache' ,
1142- 'removecache' , 'removenullcolumns' , 'page' , 'pagesize' ]
1204+ position_keys = { 'ra' , 'dec' , 'radius' , 'position' }
1205+ request_keys = { 'format' , 'data' , 'filename' , 'timeout' , 'clearcache' ,
1206+ 'removecache' , 'removenullcolumns' , 'page' , 'pagesize' }
11431207
1144- # Explicit formatting for Mast's filtered services
1145- if 'filtered' in service .lower ():
1208+ # Split params into categories
1209+ position_params = {k : v for k , v in kwargs .items () if k .lower () in position_keys }
1210+ request_params = {k : v for k , v in kwargs .items () if k .lower () in request_keys }
1211+ service_params = {k : v for k , v in kwargs .items () if k .lower () not in position_keys | request_keys }
11461212
1147- # Separating the filter params from the positional and service_request method params.
1148- filters = [{'paramName' : k , 'values' : kwargs [k ]} for k in kwargs
1149- if k .lower () not in position_keys + request_keys ]
1150- position_params = {k : v for k , v in kwargs .items () if k .lower () in position_keys }
1151- request_params = {k : v for k , v in kwargs .items () if k .lower () in request_keys }
1213+ # Handle filtered vs. non-filtered services
1214+ if 'filtered' in service .lower ():
1215+ filters = self ._build_filters (service_params )
11521216
1153- # Mast's filtered services require at least one filter
1154- if filters == []:
1155- raise InvalidQueryError ("Please provide at least one filter." )
1217+ if not filters :
1218+ raise InvalidQueryError ('Please provide at least one filter.' )
11561219
1157- # Building 'params' for Mast.service_request
1158- if columns is None :
1159- columns = '*'
1220+ if columns is not None and isinstance (columns , list ):
1221+ columns = ',' .join (columns )
11601222
1161- params = {'columns' : columns ,
1162- 'filters' : filters ,
1163- ** position_params
1164- }
1223+ params = {
1224+ 'columns' : columns or '*' ,
1225+ 'filters' : filters ,
1226+ ** position_params ,
1227+ }
11651228 else :
1166-
1167- # Separating service specific params from service_request method params
1168- params = {k : v for k , v in kwargs .items () if k .lower () not in request_keys }
1169- request_params = {k : v for k , v in kwargs .items () if k .lower () in request_keys }
1170-
1171- # Warning for wrong input
11721229 if columns is not None :
1173- warnings .warn ("'columns' parameter will not mask non-filtered services" , InputWarning )
1230+ warnings .warn (
1231+ "'columns' parameter is ignored for non-filtered services." ,
1232+ InputWarning
1233+ )
1234+ params = {** service_params , ** position_params }
11741235
11751236 return self .service_request (service , params , ** request_params )
11761237
0 commit comments