Contents
Usage
The queries module provides a set of tools for defining MongoDB queries in a more Pythonic way (at least IMO). The best way to demonstrate this is by example so let's start with something simple:
from mongoframes import *
class Dragon(Frame):
_fields = {
'name',
'breed',
'age',
'friends',
'father',
'stashed_items'
}
# Find the first dragon not named Burt using a raw query
burt = Dragon.one({'name': {'$ne': 'Burt'}})
# Same as above but using `Q` to build the query
burt = Dragon.one(Q.name != 'Burt')
The Q class provides the start point for constructing conditions. Starting with Q, dot notation is used to define the field name or path (e.g Q.address.town) followed by an operator (==, !=, >, >=, <, <=) and finally a value. The value can be;
- any JSON type value,
- a regular expression,
- a Frame or SubFrame instance (or list of).
We can combine condition with And, Or and Nor groups:
And(Q.age != 'Burt', Q.age > 10)
> {'$and': [{'name': {'$ne': 'Burt'}}, {'age': {'$gt': 10}}]}
Other common MongoDB operators are available through operator functions including All, ElemMatch, Exists, In, Not, NotIn, Size and Type:
In(Q.friends, Dragon.ids(Q.breed == 'Fire-drake'))
> {'friends': {'$in': [...list of IDs for fire-drake dragons...]}}
Any Frame method that accepts a filter argument can accept a query or dictionary. Using the to_dict method and to_refs function you can convert queries to a dictionary suitable for use with the PyMongo directly:
# `to_dict` converts a query to a dictionary
(Q.name == 'burt').to_dict()
> {'name': 'burt'}
# `to_refs` will convert any Frame instances to ObjectIds
to_refs((Q.father == burt).to_dict())
> {'father': ObjectId(...)}
You can also combine a dictionary you'd use as a filter in PyMongo with queries generated using the Q object:
And(
ElemMatch(Q.stashed_items, Q.worth > 10),
{'breed': 'Fire drake'}
)
> {'$and': [
{'stashed_items': {'$elemMatch': {'worth': {'$gt': 10}}}},
{'breed': 'Fire drake'}
]}
The aim is to cover common operations not all operations, use of Q is entirely optional and MongoFrame methods that accept filters will handle dictionaries and queries generated with Q.
Should you want to run queries against a collection using PyMongo access to the underlying database collection is available via using the get_collection method:
Dragon.get_collection().find({'name': 'Burt'})
Reference
Q
Start point for the query creation, the Q class is a special type of class that's initialized by appending an attribute, for example:
Q.hit_points > 100
Condition(field, value, operator)
The result of using the Q class in an expression (as above) is a Condition. You won't typically use Condition directly and the class isn't imported by default, instead along with the Group class conditions provide the building blocks for a query.
condition.to_dict()
Return a dictionary suitable for use with PyMongo as a filter.
Operators
The following operators can be used with the Q class in their Python form ==, !=, >, >=, <, <=. This isn't possible for all operators though and so functions are provided instead.
Operator functions are capitalized to prevent clashes with Python keywords (e.g not, in, type, etc.) Whilst this is a compromise in practice operators work much like the Group classes (And, Or, and Nor) and this approach provides the most consistent interface.
Much of the documentation below is lifted directly from the MongoDB docs and only modified to fit the context of using MongoFrames' operator functions.
All(field, values)
The All operator selects documents where the field value is in the given list of values.
All(Q.rankings, [1, 2, 3])
> {'rankings': {'$all': [1, 2, 3]}}
ElemMatch(field, *conditions)
The ElemMatch operator matches documents that contain an array field with at least one element that matches all the specified conditions.
ElemMatch(Q.stashed_items, Q.value > 10)
> {'stashed_items': {'$elemMatch': {'value': {'$gt': 10}}}}
Exists(field, exists)
When exists is True, Exists matches the documents that contain the field, including documents where the field value is None/null. If exists is False, the query returns only the documents that do not contain the field.
Exists(Q.spare_wheel, True)
> {'spare_wheel': {'$exists': true}}
In(field, values)
The In operator selects the documents where the value of the field equals any value in the specified list of values.
In(Q.languages, ['CoffeeScript', 'elixir', 'Lua', 'Python'])
> {'languages': {'$in': ['CoffeeScript', 'elixir', 'Lua', 'Python']}}
Not(condition)
Not performs a logical NOT operation on the specified condition and selects documents that do not match. This includes documents that do not contain the field.
Not(Q.age > 100)
> {'age': {'$not': {'$gt': 100}}}
The Not operator only affects other operators and cannot check fields and documents. So, use the Not operator for logical disjunctions and the != operator to test the content of fields.
NotIn(field, values)
The NotIn operator selects documents where the field value is not in the specified list of values or the field does not exist.
NotIn(Q.languages, ['CoffeeScript', 'elixir', 'Lua', 'Python'])
> {'languages': {'$nin': ['CoffeeScript', 'elixir', 'Lua', 'Python']}}
Size(field, size)
The Size operator matches any document where the field contains a list with the number of elements specified by size.
Size(Q.stashed_items, 2)
{'stashed_items': {'$size': 2}}
Type(field, type)
Type selects documents where the value of the field is an instance of the specified BSON type.
Type(Q.name, 2) # 2 represents a "string"
> {'name': {'$type': 2}}
Groups
Group(*conditions)
The Group class is used as a base class for operators that group together two or more conditions. You won't typically use Group directly and the class isn't imported by default, instead along with the Condition class groups provide the building blocks for a query.
Groups can be combined as follows and help keep things more concise and readable:
Or(
And(
Q.hit_points < 100,
Q.mana < 200
),
Exists(Q.armour, False)
)
> {'$or': [
{'$and': [
{'hit_points': {'$lt': 100}},
{'mana': {'$lt': 200}}
]},
{'armour': {'$exists': false}}
]
}
group.to_dict()
Return a dictionary suitable for use with PyMongo as a filter.
And(*conditions)
And performs a logical AND operation on a list of two or more conditions and selects documents that satisfy all of them.
And(Q.hit_points < 100, Q.mana < 200)
> {'$and': [{'hit_points': {'$lt': 100}}, {'mana': {'$lt': 200}}]}
Or(*conditions)
The Or operator performs a logical OR operation on a list of two or more conditions and selects documents that satisfy at least one of them.
Or(Q.hit_points >= 100, Q.mana >= 200)
> {'$or': [{'hit_points': {'$gte': 100}}, {'mana': {'$gte': 200}}]}
Nor(*conditions)
Nor performs a logical NOR operation on a list of two or more conditions and selects documents that fail all of them.
# Select the same documents as our `And` example
Nor(Q.hit_points >= 100, Q.mana >= 200)
> {'$nor': [{'hit_points': {'$gte': 100}}, {'mana': {'$gte': 200}}]}
Utils
to_refs(value)
Return a copy of value with all Frame instances converted to ObjectIds.