Skip to content

Commit 05bf8f8

Browse files
committed
#2: DictDots.filter()
- Begin work for adding the `filter` function to search for lists of matching keys in a dict. Issue: #2 PR: #4
1 parent b9cef9b commit 05bf8f8

File tree

4 files changed

+58
-4
lines changed

4 files changed

+58
-4
lines changed

DictDots.py

Lines changed: 52 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import re
22
import constants
33
from typing import Any
4-
from dd_types import DotSearchable, DotQuery, DotCurrentKey, DotCurrentData
4+
from dd_types import DotSearchable, DotQuery, DotCurrentKey, DotCurrentData, DotFilterResult
55
from dd_exceptions import InvalidQueryString, InvalidDataType, DoesNotExist, KeyNotFound
66

77

88
class 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

dd_types.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,6 @@
1717

1818
"""A type representing the accepted data types for getter functions."""
1919
DotCurrentData = DotSearchable
20+
21+
"""The result of a filter query. Can be empty."""
22+
DotFilterResult = List[Any]

docs/ddql.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
# Potential data language
22

3+
---
34
This is a wishlist of how I want the query language to look.
45
The only things on here that work right now are `get s` and `get ns`.
56

67
I haven't yet decided if I actually want to support sets due to how
78
the indexing works through a hash. It would be cumbersome to try and
89
pass a big value into dictdots in the query string.
10+
911
## Get
1012

1113
Get returns a specific object matching an exact query.

tests/test_dict_dots.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
])
1313
def test_is_valid_query(query, result):
1414
"""Test that queries can be validated."""
15-
assert DictDots.is_valid_query(query) == result
15+
assert DictDots.is_valid_get_query(query) == result
1616

1717

1818
@pytest.mark.parametrize("data,expected", [

0 commit comments

Comments
 (0)