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.frinds.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\` |
+-----------+-----------------------------------+
| NUMBER | 42, -12, 34.7 |
+-----------+-----------------------------------+
| STRING | 'hello', "hellow" |
+-----------+-----------------------------------+
| BOOLEAN | true, false |
+-----------+-----------------------------------+
| NONE | none, null |
+-----------+-----------------------------------+
| NOW | utc current datetime |
+-----------+-----------------------------------+
| REGEXP | /\\d+\\w+\\d+/ |
+-----------+-----------------------------------+
| ARRAY | list of any items and any types |
+-----------+-----------------------------------+
Dict keys
=========
Dict keys 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
>>> 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 evalute 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 evalutes 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.py", line 355, in match
return self._eval_expr(query_dict, self.ast)
File "dictquery.py", line 327, in _eval_expr
dict_value = self._get_dict_value(query_dict, tree.value)
File "dictquery.py", line 302, in _get_dict_value
self.key_separator, self.raise_keyerror)
File "dictquery.py", line 258, in get_dict_value
raise DQKeyError("Key '%s' not found" % dict_key)
dictquery.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 |
+-------------+---------------------------------------+
| CONTAIN | 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` CONTAIN '.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 |
+-------------+------------------------------------+
| CONTAIN | dict item contains matching item |
+-------------+------------------------------------+
::
>>> import dictquery as dq
>>> dq.match(data, "`tags` CONTAIN 'dolor'")
True
>>> dq.match(data, "`eyeColor` IN ['blue', 'green', 'black']")
True
Key presence in dict
====================
``CONTAIN`` can be used with dict items to check if key in dict
::
>>> import dictquery as dq
>>> dq.match(data, "`name` CONTAIN 'firstname'")
True
>>> dq.match(data, "`name` CONTAIN '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 evalution 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
dictquery-0.2.0.tar.gz
(12.3 kB
view hashes)
Built Distribution
Close
Hashes for dictquery-0.2.0-py2.py3-none-any.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | eab6cbc3c3a253f7be2284d2aeac3f4d1d71b83713d694a7ddbd484d21820b1b |
|
MD5 | a86ecf4c0cc15e662abf8cb3f3436fec |
|
BLAKE2b-256 | 0547ea24b587bb212dad3a7097c1c7e64a189743f36071440844cb0bd69bdcfc |