Queries

Norman features a flexible and extensible query API, the basis of which is the Query class. Queries are constructed by manipulating Field and other Query objects; the result of each operation is another Query.

Contents

Examples

The following examples explain the basic concepts behind Norman queries.

Queries are constructed as a series of field comparisons, for example:

q1 = MyTable.age > 4
q2 = MyTable.parent.name == 'Bill'

These can be joined together with set combination operators:

q3 = (MyTable.age > 4) | (MyTable.parent.name == 'Bill')

Containment in an iterable can be checked using the & operator. This is the same usage as in set:

q4 = MyTable.parent.name & ['Bill', 'Bob', 'Bruce']

Since queries are themselves iterable, another query can be used as the container:

q5 = MyTable.age & OtherTable.age

A custom function can be used for filtering records from a Table or another Query:

def isvalid(record):
    return record.parrot.endswith('notlob')

q6 = query(isvalid, q5)

If the filter function is omitted, then all records are assumed to pass. This is useful for creating a query of a whole table:

q7 = query(MyTable)

The result of each of these is a Query object, which can be iterated over to yield records. The query is not evaluated until a result is requested from it (including len). An existing query can be refreshed after the base data has changed by calling it as a function. The return value is the query iteself, so to ensure that the result is up to date, you could call:

latest_size = len(q7())

API

norman.query([func, ]table)

Return a new Query for records in table for which func is True.

table is a Table or Query object. If func is missing, all records are assumed to pass. If it is specified, is should accept a record as its argument and return True for passing records.

class norman.Query(op, *args, **kwargs)

This object should never be instantiated directly, instead it should be created as the result of a Field comparison or by using the query function. The interface allows most operations permitted on sets, such as unions and intersections, but returns a new Query object instead of any results. The following operations are supported:

Operation Description
r in q Return True if record r is in the results of query q.
len(q) Return the number of results in q.
iter(q) Return an iterator over records in q.
q1 == q2 Return True if q1 and q2 contain the same records.
q1 != q2 Return True if not a == b
q1 & q2 Return a new Query object containing records in both q1 and q2.
q1 | q2 Return a new Query object containing records in either q1 or q2.
q1 ^ q2 Return a new Query object containing records in either q1 or q2, but not both.
q1 - q2 Return a new Query object containing records in q1 which are not in q2.

Queries evaluate to True if they contain any results, and False if they do not.

Calling a query forces it to be re-evaluated, and the query object is returned.

table

Return the table queried. If no single table is queried, None is returned.

add([arg, **kwargs])

Add a record based on the query criteria, and return the new record. There are two modes of operation for this method, depending on the query. For either mode, the query must be defined by a clear set of field values for a single Table. This includes queries such as (MyTable.field1` == 1) & (MyTable.field2` == 2) but not MyTable.field1` > 1.

The first mode accepts keyword arguments, which are combined with the parameters used to construct the query and passed to the table constructor. For example:

``((MyTable.a` == 1) & (MyTable.b` == 2)).add(c=3)``

evaluates to:

MyTable(a=1, b=2, c=3)

The second mode is used when the query has been created by field. In this case, a single argument is expected which is the record to apply to the field. For example:

(Table1.id == 4).field('table2').add(table2_instance)

is the same as:

(Table1.id == 4).add(table2=table2_instance)
delete()

Delete all records matching the query from their table. If no records match, nothing is deleted.

field(fieldname)

Return a new Query containing records in a single field.

The set of records returned by this is similar to:

set(getattr(r, fieldname) for r in query)

However, the returned object is another Query instead of a set. Only instances of a Table subclass are contained in the results, other values are dropped. This is functionally similar to a SQL query on a foreign key. If the target field is a Join, then all the results of each join are concatenated.

one([default])

Return a single value from the query results. If the query is empty and default is specified, then it is returned instead, otherwise an IndexError is raised.