Skip to main content

A Python package for robust querying of string lists in MongoDB.

Project description

A DSL for robust querying of tags in MongoDB

This module converts a text query into a dict that can be directly used in a MongoDB query of a string array field.

To get started, install this package with python3 -m pip install tag_query.

Example Usage

To query a collection for any documents for which the field field_name contains value1 and value2:

from tag_query import compile_query, exceptions

try:
  mongo_query = compile_query(
    expression = 'value1 and value2',
    field = 'field_name'
  )
  # Or just `compile_query('value1 and value2', 'field_name')`
except exceptions.ParseError as e:
  print(e)
  exit(1)

print(mongo_query) #will output -> {'$and': [{'field_name': 'value1'}, {'field_name': 'value2'}]}

Syntax

All query expressions are case-insensitive; that is, the mongodb query is always output in lowercase. The only exception is the field name, which is case-sensitive. Also, note that query expressions are evaluated from left to right, unless otherwise denoted by parentheses. So a and b or c is the same as (a and b) or c, and a or b and c is the same as (a or b) and c.

The following is the entire syntax specification:

expression := binary

binary :=
  | binary (`and`|`+`) value
  | binary (`or`|`/`) value
  | binary (`not`|`-`) value
  | value

value :=
  | `(` expression `)`
  | `not` value
  | function
  | glob
  | regex
  | tag

function :=
  | (`eq`|`equal`|`equals`|`exact`|`exactly`|`=`) number
  | (`ge`|`min`|`minimum`|`>=`) number
  | (`le`|`max`|`maximum`|`<=`) number
  | (`lt`|`fewer`|`below`|`<`) number
  | (`gt`|`greater`|`above`|`>`) number

glob :=
  | tag `*`
  | `*` tag
  | `*` tag `*`

tag := literal+

regex := `\{[^\}]*\}`

literal :=
  | `[a-z0-9_\.]+`
  | `"(\\"|[^"])*"`

number := `[0-9]+`

Parentheses

In some cases, left-to-right parsing may not be wanted, so parentheses can be used to group expressions together in a precise order. For example, a or (b and not c) is different than a or b and not c (the latter is equivalent to (a or b) and not c).

Tags

Any non-keyword, non-function, non-special-character text is a single tag. Additionally, any text inside double quotes (") is a tag. If multiple tags are adjacent, they are concatenated together into a single tag, using a single space for the delimiter.

So for example, some_tag, ThisIsATag123 and "another tag" each count as a single tag (but keep in mind they're case-insensitive!), and tag1 and tag2 tag3 is the same as "tag1" and "tag2 tag3".

Globbing

The Glob operator (*) may be used for simple pattern matching. E.g. *test* will match any tag that begins or ends with "test", such as "contested". *test or test* are also valid, and match tags that end and begin with "test", respectively. Note that only plain text tags can have globbing applied; no regex or any compound expressions.

Regex

Any string inside curly braces is interpreted as a regex pattern. E.g. {^[A-Za-z0-9]+$} matches any purely alphanumeric tag.

Operators

Operators select based on the contents of a field.

  • and, +: Require both operands to be true. E.g. tag1 and tag2 or tag1 + tag2 means that the field must contain both "tag1" and "tag2".
  • or, /: Require either of the operands to be true. E.g. tag1 or tag2 or tag1 / tag2 means that the field must contain "tag1" or "tag2" (or both!).
  • not, -: Invert the selection. E.g. not tag1 means that the field must not contain "tag1", and not (tag1 and tag2) means it must not contain both "tag1" and "tag2", but it may contain one of them, or neither.
    • Note that in a binary expression, not can mean and not. For example tag1 not tag2 or tag1 - tag2 is the same as tag1 and not tag2.

Functions

Instead of selecting the contents of a field, functions select based on how many values the field has.

  • eq,equals,exact,exactly, =: Require the field to have exactly that many tags. E.g. exactly 5.
  • lt,fewer,below, <: Require the field to have fewer than that many tags. E.g. fewer 5.
  • gt,greater,above, >: Require the field to have more than that many tags. E.g. greater 5.
  • le,max,maximum, <=: Require the field to have at most that many tags. E.g. maximum 5.
  • ge,min,minimum, >=: Require the field to have at least that many tags. E.g. minimum 5.

Optimizations

In certain trivial cases, the parser will optimize queries. For example, a and b and a has a redundant value a, so it will be optimized into just a and b. Likewise, a or not a and not b would be optimized into not b, because a or not a just selects everything. As for functions, they may be optimized away if their range includes all numbers (e.g. < 5 or >= 5), and redundant ranges will be removed (e.g. > 2 or = 5 is the same as just > 2).

Contradictions

Any queries that are obviously contradictory are not allowed and will raise an error. For example, a and not a can never match any documents, so it's considered an error. Likewise, expressions like > 3 and < 2 are impossible and also raise an error.


Examples

Here are some example tag queries and their corresponding outputs. The outputs can be directly passed to MongoDB as selection criteria.

Query Expression MongoDB Query Output
tag1 and tag2 {'$and': [{'field_name': 'tag1'}, {'field_name': 'tag2'}]}
tag1 or tag2 {'$or': [{'field_name': 'tag1'}, {'field_name': 'tag2'}]}
not tag1 {'field_name': {'$ne': 'tag1'}}
tag1 and not tag2 {'$and': [{'field_name': 'tag1'}, {'field_name': {'$ne': 'tag2'}}]}
"tag with spaces" {'field_name': 'tag with spaces'}
three tags concatenated {'field_name': 'three tags concatenated'}
{^foo.*} {'field_name': {'$regex': '^foo.*'}}
*test* {'field_name': {'$regex': 'test'}}
exactly 3 {'field_name': {'$size': 3}}
fewer 2 {'field_name.1': {'$exists': False}}
minimum 5 {'field_name.4': {'$exists': True}}
tag1 or (tag2 and not tag3) {'$or': [{'field_name': 'tag1'}, {'$and': [{'field_name': 'tag2'}, {'field_name': {'$ne': 'tag3'}}]}]}

You can use any of these expressions with compile_query(expression, field='field_name').

Project details


Download files

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

Source Distribution

tag_query-1.1.2.tar.gz (25.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

tag_query-1.1.2-py3-none-any.whl (24.8 kB view details)

Uploaded Python 3

File details

Details for the file tag_query-1.1.2.tar.gz.

File metadata

  • Download URL: tag_query-1.1.2.tar.gz
  • Upload date:
  • Size: 25.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for tag_query-1.1.2.tar.gz
Algorithm Hash digest
SHA256 b7cdcb445b14e92b99765013bd14c6159e84fb9f7891a2ec4716c03d063d6f14
MD5 f7681d5c0ad64096d950608a90d79e2d
BLAKE2b-256 00e1744a622969c1e9c0278ee109102a89a3477b0e66070b91b3c5dbde63dc12

See more details on using hashes here.

File details

Details for the file tag_query-1.1.2-py3-none-any.whl.

File metadata

  • Download URL: tag_query-1.1.2-py3-none-any.whl
  • Upload date:
  • Size: 24.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for tag_query-1.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 58dd076e02a8b1f38f93cf2295a9133b143e51a04e42a2e8fb3069e0049c6da6
MD5 d9ad2a13d7cfd4cfbdd7cac2b23f7d3b
BLAKE2b-256 a39e7ed6d78ffbc906af8e416715c78445b10318017c2a4a908a957e6d913802

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page