11import re
22import constants
33from typing import Any
4- from dd_types import DotSearchable , DotQuery , DotCurrentKey , DotCurrentData
4+ from dd_types import DotSearchable , DotQuery , DotCurrentKey , DotCurrentData , DotFilterResult
55from dd_exceptions import InvalidQueryString , InvalidDataType , DoesNotExist , KeyNotFound
66
77
88class DictDots :
99 @staticmethod
10- def is_valid_query (query : DotQuery ) -> bool :
10+ def is_valid_get_query (query : DotQuery ) -> bool :
1111 """Check if the query string has only valid characters.
1212
1313 Queries, for the time being, only allow alphanumeric and dots ``.``
@@ -19,6 +19,10 @@ def is_valid_query(query: DotQuery) -> bool:
1919 """
2020 return bool (re .match (r'^[\w.]+$' , query ))
2121
22+ @classmethod
23+ def is_valid_filter_query (query : DotQuery ):
24+ return bool (re .match (r'^[\w.\[\]{}]+$' , query ))
25+
2226 @staticmethod
2327 def is_searchable_type (data : Any ) -> bool :
2428 """Check that the data can be searched by DictDots.
@@ -44,7 +48,7 @@ def _validate_get(searchable: DotSearchable, query: DotQuery) -> None:
4448 if not DictDots .is_searchable_type (searchable ):
4549 raise InvalidDataType (searchable )
4650
47- if not DictDots .is_valid_query (query ):
51+ if not DictDots .is_valid_get_query (query ):
4852 raise InvalidQueryString (query )
4953
5054 @staticmethod
@@ -104,5 +108,50 @@ def get(cls, searchable: DotSearchable, query: DotQuery, default: Any = None) ->
104108
105109 return current_data
106110
111+ @classmethod
112+ def filter (cls , searchable : DotSearchable , query : DotQuery ) -> DotFilterResult :
113+ """Query a searchable
114+
115+ Attempt to find values in a searchable matching the key from the search string.
116+ Returns a list of all matches.
117+ Will return an empty list if no matches are found.
118+
119+ Does not raise an error for invalid keys because this is a search.
120+
121+ :param DotSearchable searchable:
122+ The data to query.
123+ :param DotQuery query:
124+ A query string to search the data for.
125+ :return DotFilterResult:
126+ A list of all data matching the search string in ``query``.
127+ May be empty.
128+ """
129+ DictDots ._validate_get (searchable , query )
130+ keys = query .split ('.' )
131+ # current_data is the value we are currently digging into.
132+ current_data = searchable
133+
134+ type_methods = {
135+ dict : cls ._dict_getter ,
136+ list : cls ._list_getter ,
137+ }
138+
139+ for key in keys :
140+ if key .isnumeric ():
141+ # We don't support numerical strings for now, so convert them to ints.
142+ key = int (key )
143+
144+ method = type_methods [type (current_data )]
145+
146+ try :
147+ current_data = method (key , current_data )
148+ except KeyNotFound as e :
149+ if default :
150+ return default
151+ raise DoesNotExist (query , searchable , e )
152+
153+ return current_data
154+
155+
107156
108157
0 commit comments