Skip to main content
Join the official Python Developers Survey 2018 and win valuable prizes: Start the survey!

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"
}
]
}



Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Filename, size & hash SHA256 hash help File type Python version Upload date
dictquery-0.2.0-py2.py3-none-any.whl (10.5 kB) Copy SHA256 hash SHA256 Wheel py2.py3 Mar 26, 2018
dictquery-0.2.0.tar.gz (12.3 kB) Copy SHA256 hash SHA256 Source None Mar 26, 2018

Supported by

Elastic Elastic Search Pingdom Pingdom Monitoring Google Google BigQuery Sentry Sentry Error logging AWS AWS Cloud computing DataDog DataDog Monitoring Fastly Fastly CDN SignalFx SignalFx Supporter DigiCert DigiCert EV certificate StatusPage StatusPage Status page