query Module

Rom - the Redis object mapper for Python

Copyright 2013-2016 Josiah Carlson

Released under the LGPL license version 2.1 and version 3 (you can choose which you’d like to be bound under).

class rom.query.Query(model, filters=(), order_by=None, limit=None, select=None)

Bases: object

This is a query object. It behaves a lot like other query objects. Every operation performed on Query objects returns a new Query object. The old Query object does not have any updated filters.

all()

Alias for execute().

cached_result(timeout)

This will execute the query, returning the key where a ZSET of your results will be stored for pagination, further operations, etc.

The timeout must be a positive integer number of seconds for which to set the expiration time on the key (this is to ensure that any cached query results are eventually deleted, unless you make the explicit step to use the PERSIST command).

Note

Limit clauses are ignored and not passed.

Usage:

ukey = User.query.endswith(email='@gmail.com').cached_result(30)
for i in xrange(0, conn.zcard(ukey), 100):
    # refresh the expiration
    conn.expire(ukey, 30)
    users = User.get(conn.zrange(ukey, i, i+99))
    ...
count()

Will return the total count of the objects that match the specified filters.:

# counts the number of users created in the last 24 hours
User.query.filter(created_at=(time.time()-86400, time.time())).count()
delete(blocksize=100)

Will delete the entities that match at the time the query is executed.

Used like:

MyModel.query.filter(email=...).delete()
MyModel.query.endswith(email='@host.com').delete()

Warning

can’t be used on models on either side of a OneToMany, ManyToOne, or OneToOne relationship.

endswith(**kwargs)

When provided with keyword arguments of the form col=suffix, this will limit the entities returned to those that have a word with the provided suffix in the specified column(s). This requires that the suffix=True option was provided during column definition.

Usage:

User.query.endswith(email='@gmail.com').execute()
execute()

Actually executes the query, returning any entities that match the filters, ordered by the specified ordering (if any), limited by any earlier limit calls.

filter(**kwargs)

Only columns/attributes that have been specified as having an index with the index=True option on the column definition can be filtered with this method. Prefix, suffix, and pattern match filters must be provided using the .startswith(), .endswith(), and the .like() methods on the query object, respectively. Geo location queries should be performed using the .near() method.

Filters should be of the form:

# for numeric ranges, use None for open-ended ranges
attribute=(min, max)

# you can also query for equality by passing a single number
attribute=value

# for string searches, passing a plain string will require that
# string to be in the index as a literal
attribute=string

# to perform an 'or' query on strings, you can pass a list of
# strings
attribute=[string1, string2]

As an example, the following will return entities that have both hello and world in the String column scol and has a Numeric column ncol with value between 2 and 10 (including the endpoints):

results = MyModel.query \
    .filter(scol='hello') \
    .filter(scol='world') \
    .filter(ncol=(2, 10)) \
    .all()

If you only want to match a single value as part of your range query, you can pass an integer, float, or Decimal object by itself, similar to the Model.get_by() method:

results = MyModel.query \
    .filter(ncol=5) \
    .execute()

Note

Trying to use a range query attribute=(min, max) on indexed string columns won’t return any results.

Note

This method only filters columns that have been defined with index=True.

first()

Returns only the first result from the query, if any.

iter_result(timeout=30, pagesize=100, no_hscan=False)

Iterate over the results of your query instead of getting them all with .all(). Will only perform a single query. If you expect that your processing will take more than 30 seconds to process 100 items, you should pass timeout and pagesize to reflect an appropriate timeout and page size to fetch at once.

Usage:

for user in User.query.endswith(email='@gmail.com').iter_result():
    # do something with user
    ...
like(**kwargs)

When provided with keyword arguments of the form col=pattern, this will limit the entities returned to those that include the provided pattern. Note that ‘like’ queries require that the prefix=True option must have been provided as part of the column definition.

Patterns allow for 4 wildcard characters, whose semantics are as follows:

  • ? - will match 0 or 1 of any character

  • * - will match 0 or more of any character

  • + - will match 1 or more of any character

  • ! - will match exactly 1 of any character

As an example, imagine that you have enabled the required prefix matching on your User.email column. And lets say that you want to find everyone with an email address that contains the name ‘frank’ before the @ sign. You can use either of the following patterns to discover those users.

  • *frank*@

  • *frank*@

Note

Like queries implicitly start at the beginning of strings checked, so if you want to match a pattern that doesn’t start at the beginning of a string, you should prefix it with one of the wildcard characters (like * as we did with the ‘frank’ pattern).

limit(offset, count)

Will limit the number of results returned from a query:

# returns the most recent 25 users
User.query.order_by('-created_at').limit(0, 25).execute()
near(name, lon, lat, distance, measure, count=None)
order_by(column)

When provided with a column name, will sort the results of your query:

# returns all users, ordered by the created_at column in
# descending order
User.query.order_by('-created_at').execute()
replace(**kwargs)

Copy the Query object, optionally replacing the filters, order_by, or limit information on the copy. This is mostly an internal detail that you can ignore.

select(*column_names, **kwargs)

Select the provided column names from the model, do not return an entity, do not involve the rom session, just get the raw and/or processed column data from Redis.

Keyword-only arguments:

  • include_pk=False - whether to include the primary key in the

    returned data (we need to get this in some cases, so we fetch it anyway; if you want it, we can return it to you - just be careful with the namedtuple option - see the warning below)

  • decode=True - whether to take a pass through normal data

    decoding in the model (will not return an entity/model)

  • ff=_dict_data_factory - the type of data to return from the

    select after all filters/limits/order_by are applied

Warning

If include_pk = True and if you don’t provide the primary key column, it will be appended to your list of columns.

Note

if you want to provide a new factory function for the returned data, it must be of the form (below is the actual dict factory function)

def _dict_data_factory(columns):
    _dict = dict
    _zip = zip
    def make(data):
        # do whatever you need to turn your tuple of columns plus
        # your list of data into whatever you want:
        return _dict(_zip(columns, data))
    return make

Available factory functions:

  • ``rom.query._dict_data_factory`` - default

  • ``rom.query._list_data_factory`` - lowest overhead, as the data passed in above is a list that you can do anything to

  • ``rom.query._tuple_data_factory`` - when you want tuples instead

  • ``rom.query._namedtuple_data_factory`` - get namedtuples, see see warning below

Warning

If you use the _namedtuple_data_factory, and your columns include underscore prefixes, they will be stripped. If this results in a name collision, you will get an exception. If you want different behavior, write your own 20 line factory function that does exactly what you want, and pass it; they are really easy!

startswith(**kwargs)

When provided with keyword arguments of the form col=prefix, this will limit the entities returned to those that have a word with the provided prefix in the specified column(s). This requires that the prefix=True option was provided during column definition.

Usage:

User.query.startswith(email='user@').execute()