Metadata-Version: 2.1
Name: dictquery
Version: 0.4.0
Summary: Library to query python dicts
Home-page: https://github.com/cyberlis/dictquery
Author: Denis Lisovik
Author-email: cyberlis@rccraft.ru
License: MIT
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"
            }
          ]
        }
        ```
        
Platform: UNKNOWN
Classifier: Development Status :: 3 - Alpha
Classifier: License :: OSI Approved :: MIT License
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 2.7
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.3
Classifier: Programming Language :: Python :: 3.4
Classifier: Programming Language :: Python :: 3.5
Classifier: Programming Language :: Python :: 3.6
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Programming Language :: Python :: Implementation :: PyPy
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Description-Content-Type: text/markdown
