Library to query python dicts
Project description
DictQuery
Library to query python dicts
Several syntax examples:
"age >= 12"
"`user.name` == 'cyberlis'"
"`user.email` MATCH /\w+@\w+\.com/ AND age != 11"
"`user.friends.age` > 12 AND `user.friends.name` LIKE 'Ra*ond'"
"email LIKE 'mariondelgado?bleendot?com'"
"eyeColor IN ['blue', 'green', 'black']"
"isActive AND (gender == 'female' OR age == 27)"
"latitude != longitude"
Supported data types
type | example |
---|---|
KEY | name, age, `friends.name.firstname`, `friends.age` |
NUMBER | 42, -12, 34.7 |
STRING | 'hello', "hellow" |
BOOLEAN | true, false |
NONE | none, null |
NOW | utc current datetime |
REGEXP | /\d+\d+\w+/ |
ARRAY | list of any items and any types |
Keys
Key literals must start with a letter or an underscore, such as:
_underscore
underscore_
The remainder of your variable name may consist of letters, numbers and underscores.
password1
n00b
un_der_scores
If you need a key with separator character (.
or /
) because you use nested keys, or need spaces or other punctuation characters in key, use back-ticks (``)
DictQuery supports nested dicts splited by dot .
or any separator specified in key_separator
param. Default key_separator='.'
>>> import dictquery as dq
>>> dq.match(data, "`friends.age` <= 26")
True
>>> compiled = dq.compile("`friends/age` <= 26", key_separator='/')
>>> compiled.match(data)
True
if you don't need nested keys parsing and want get keys as is or if your keys contain separator char, you can disable nested keys behaviour by setting use_nested_keys=False
>>> import dictquery as dq
>>> dq.match(data, "`user.address`")
False
>>> dq.match(data, "age")
True
>>> compiled = dq.compile("`user.address`", use_nested_keys=False)
>>> compiled.match(data)
True
In query you can use dict keys 'as is' without any binary operation. DictQuery will get value by the key and evaluate it to bool
>>> import dictquery as dq
>>> dq.match(data, "isActive")
False
>>> dq.match(data, "isActive == false")
True
if key is not found by default this situation evaluates to boolean False
(no exception raised).
You can set raise_keyerror=True
to raise keyerror if key would not be found.
>>> import dictquery as dq
>>> dq.match(data, "favoriteFruit")
False
>>> compiled = dq.compile("`favoriteFruit`", raise_keyerror=True)
>>> compiled.match(data)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File ".../dictquery/dictquery/visitors.py", line 41, in match
return self.evaluate(data)
File ".../dictquery/dictquery/visitors.py", line 35, in evaluate
result = bool(self.ast.accept(self))
File ".../dictquery/dictquery/parsers.py", line 80, in accept
return visitor.visit_key(self)
File ".../dictquery/dictquery/visitors.py", line 84, in visit_key
values=self._get_dict_value(expr.value),
File ".../dictquery/dictquery/visitors.py", line 30, in _get_dict_value
self.key_separator, self.raise_keyerror)
File ".../dictquery/dictquery/datavalue.py", line 112, in query_value
raise DQKeyError("Key '{}' not found".format(data_key))
dictquery.exceptions.DQKeyError: "Key 'favoriteFruit' not found"
Comparisons
Operation | Meaning |
---|---|
< | strictly less than |
<= | less than or equal |
> | strictly greater than |
>= | greater than or equal |
== | equal |
!= | not equal |
>>> import dictquery as dq
>>> dq.match(data, "age == 26")
True
>>> dq.match(data, "latitude > 12")
True
>>> dq.match(data, "longitude < 30")
True
>>> dq.match(data, "`friends.age` <= 26")
True
>>> dq.match(data, "longitude >= -130")
True
>>> dq.match(data, "id != 0")
True
>>> dq.match(data, "gender == 'male'")
False
String comparisons and matching
String literals are written in a variety of ways:
- Single quotes: 'allows embedded "double" quotes'
- Double quotes: "allows embedded 'single' quotes".
Operation | Meaning |
---|---|
MATCH | regexp matching |
LIKE | glob like matching |
IN | dict item substring in string |
CONTAINS | dict item substring contains string |
< , <= , > , >= , == , != works same way with strings as python
>>> import dictquery as dq
>>> dq.match(data, "eyeColor == 'green'")
True
>>> dq.match(data, "`name.firstname` != 'Ratliff'")
True
>>> dq.match(data, "eyeColor IN 'string with green color'")
True
>>> dq.match(data, "email CONTAINS '.com'")
True
>>> dq.match(data, r"email MATCH /\w+@\w+\.\w+/")
True
>>> dq.match(data, r"email LIKE 'mariondelgado@*'")
True
>>> dq.match(data, r"email LIKE 'mariondelgado?bleendot?com'")
True
By default all string related operations are case sensitive. To change this behaviour you have to create instance of DictQuery with case_sensitive=False
>>> import dictquery as dq
>>> dq.match(data, "`name.firstname` == 'marion'")
False
>>> compiled = dq.compile("`name.firstname` == 'marion'", case_sensitive=False)
>>> compiled.match(data)
True
Array comparisons
Operation | Meaning |
---|---|
IN | dict item in array |
CONTAINS | dict item contains matching item |
>>> import dictquery as dq
>>> dq.match(data, "tags CONTAINS 'dolor'")
True
>>> dq.match(data, "eyeColor IN ['blue', 'green', 'black']")
True
Key presence in dict
CONTAINS
can be used with dict items to check if key in dict
>>> import dictquery as dq
>>> dq.match(data, "name CONTAINS 'firstname'")
True
>>> dq.match(data, "name CONTAINS 'thirdname'")
False
Datetime comparisons with NOW
NOW
returns current utc datetime
dict item can be compared with NOW
using standard operations (< , <= , > , >= , == , !=)
>>> import dictquery as dq
>>> dq.match(data, "registered < NOW")
True
>>> dq.match(data, "registered != NOW")
True
Logical operators
Operator | Meaning | Example |
---|---|---|
and | True if both the operands are true | x and y |
or | True if either of the operands is true | x or y |
not | True if operand is false (complements the operand) | not x |
>>> import dictquery as dq
>>> dq.match(data, "isActive AND gender == 'female'")
False
>>> dq.match(data, "isActive OR gender == 'female'")
True
>>> dq.match(data, "NOT isActive AND gender == 'female'")
True
You can use parentheses to group statements or change evaluation order
>>> import dictquery as dq
>>> dq.match(data, "isActive AND gender == 'female' OR age == 27")
True
>>> dq.match(data, "isActive AND (gender == 'female' OR age == 27)")
False
Data for examples above:
from datetime import datetime
data = {
"_id": 10,
"isActive": False,
"age": 27,
"eyeColor": "green",
"name": {
"firstname": "Marion",
"secondname": "Delgado",
},
"gender": "female",
"email": "mariondelgado@bleendot.com",
"registered": datetime.strptime("2015-03-29T06:07:58", "%Y-%m-%dT%H:%M:%S"),
"latitude": 74.785608,
"longitude": -112.366088,
"tags": [
"voluptate",
"ex",
"dolor",
"aute"
],
"user.address": "155 Village Road, Enetai, Puerto Rico, 2634",
"friends": [
{
"id": 0,
"name": {
"firstname": "Ratliff",
"secondname": "Becker",
},
"age": 27,
"eyeColor": "green"
},
{
"id": 1,
"name": {
"firstname": "Raymond",
"secondname": "Albert",
},
"age": 19,
"eyeColor": "brown"
},
{
"id": 2,
"name": {
"firstname": "Mavis",
"secondname": "Sheppard",
},
"age": 34,
"eyeColor": "blue"
}
]
}
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
File details
Details for the file dictquery-0.5.0.tar.gz
.
File metadata
- Download URL: dictquery-0.5.0.tar.gz
- Upload date:
- Size: 13.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/4.0.1 CPython/3.10.9
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 15d056231e68a24ea13995c2e437cbb6867e60863dfc736148acaec92e1e3ab8 |
|
MD5 | 3a52cbf0d5ecd80c9e030a1053d39dba |
|
BLAKE2b-256 | 30c63bc0359007aa09be047c5dbb48afb1e89673507b4c03f16b1310a87312d8 |
File details
Details for the file dictquery-0.5.0-py2.py3-none-any.whl
.
File metadata
- Download URL: dictquery-0.5.0-py2.py3-none-any.whl
- Upload date:
- Size: 11.4 kB
- Tags: Python 2, Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/4.0.1 CPython/3.10.9
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 868f5bd93fd59af990c9452d2b7bde1048d1a7aa2b675f163cb1f74c71011dea |
|
MD5 | c4c00855ba87534b80ca18366951fe5a |
|
BLAKE2b-256 | c6807fcdf5c5b19bfcaf4291c5d593d55b6a6d47f7dbc7a9bee90d25fb19a041 |