Skip to content
heynemann edited this page Sep 13, 2010 · 11 revisions

Home | Installing | Expression Trees | Pynq Factory

The Pynq Factory is the way to create expression trees and have the selected provider evaluate them.

Basically you use chained method calls that inform the provider of what you expect it to do.

There are two types of calls in the factory, other than the From call: definition and executive.

Definition Calls

Definition calls are calls that do not execute the provider’s parse method and, as such, just build on the expression tree.

Pynq is organized in that way to minimize the chance that you will be doing several calls at a time to the provider (which can be very expensive, depending on the scenario).

The way definition calls work is that they keep building on an object called Query that has some expression tree related properties that can be later on parsed by the selected provider.

Where

The where call is how you filter your query. The where clause takes as parameter any valid python expression that is covered within the range of supported expressions (check Expression Trees).

Let’s see an example:

From(some_collection).where("item.salary > 2000 and item.salary < 4000").select_many()

The select_many call will be explained below. Suffice to say it executes the query you passed in. Now let’s check what the given where clause is doing. The “item” keyword tells Pynq that this expression should get the specified property out of each item in the specified collection. Pynq will then execute the specified expression (after it’s parsed to an expression tree) against each item in the collection.

Please note that this behavior is only found in the CollectionProvider and can be completely different in other providers. The major thing to understand here is that the where clause does NOT execute anything. It just adds the parsed expression tree to the expressions that compose the given query.

Order By

The order_by clause specifies how you want your query to be ordered. In the collection provider this means that the specified list of expressions will be used as values for the sort method of the list class. Please be advised again that this behavior may be different in other providers.

Let’s see an example:

result = From(self.col).order_by("second", "-third").select_many()

This tells Pynq to include and order by expression for “second” attribute of each item in ascending order and a “third” attribute of each item in descending order (note the “-” sign before the “third” attribute name).

Just as it happened with every other Definition Call, calling order_by DOES NOT sort your query or executes anything. It just builds up the query so it can be executed by the specified provider.

You can also order by expressions, like:

result = From(self.col).order_by("item.second + item.third").select_many()

This tells Pynq to order by the sum of the “second” and “third” properties of each item. This allows for a great deal of flexibility when ordering your collections. Please be advised that this operation is orders of magnitude slower than a simple field ordering. The “item.” keyword is needed in order for Pynq to be able to understand that you’re ordering by expressions.

Pynq allows you to use descending order as well with the “-” sign, like this:

result = From(self.col).order_by("-(item.second + item.third)").select_many()

This tells Pynq to order by the negation of the sum of “second” and “third” properties of each item, which means descending order.

The CollectionProvider implements this sorting by evaluating the provided expression, so you could sort by any valid expression.

Executive Calls

Executive calls are calls that actually execute the query in the context of the provider. What those calls do depends a lot on the specified provider.

Select Many

The select_many call executes the provider without passing anything other than what was specified in the definitive calls.

Even though this can have different meanings in different providers, what this usually means is that you’ll be getting an unmodified record of whatever it is that your provider is getting.

In the case of the collection provider you’ll get another collection filled with the same items found in the collection that passed the query filtering, ordering and anything else specified in the query.

There are many examples to illustrate the select_many call, but let’s look at the most basic one:

From(col).select_many()

This call returns EXACTLY the same elements that were present in col. It does not filter any of them, does not order anything. Just returns a list with the same elements.

Select Fields

The select call allows you to specify expressions that you want Pynq to return. This is a way of transforming the result of your query without having to do it manually.

In the CollectionProvider what this means is that a list of items that are transformations of items in the previous list will be returned. This sounds complex, but it couldn’t be simpler.

All that Pynq really does in the CollectionProvider is build a collection of items of class DynamicItem(which is an empty class) and fill it with only the specified attributes from the original element.

Let’s see an example:


class TestClass(object):
    def __init__(self, first, second, third):
        self.first = first
        self.second = second
        self.third = third

entity1 = self.TestClass(1, 2, 3)
entity2 = self.TestClass(4, 5, 6)
entity3 = self.TestClass(7, 8, 9)

col = [self.entity1, self.entity2, self.entity3]

result = From(col).select("second","third")

When select is executed, the result variable will contain a list of items that, instead of having three attributes (“first”,“second” and “third”) will only feature “second” and “third” attributes.

While this is mostly a syntactic-sugar feature in this scenario (since we could return “first” and not use it), it makes for very interesting scenarios if you remember that select takes expressions and not only strings. This means you could do:

result = From(col).select("name","salary + bonus")

Any expression that could be evaluated based on each item’s properties can be used. Please be advised that this is slower than selecting only the properties you need (for the CollectionProvider), even if you’d only feel it for very large sets.

In order to use this expression field in the resulting collection (for CollectionProvider), you’ll be using a property called dynamic_{index of the column}. This sound more complicate than it is. Let’s use the example above:


result = From(col).select("name","salary + bonus")
print result[0].dynamic_1

This code prints the salary + bonus for the first row returned (if any was returned).

Count

The count call returns the number of items that the given expression returned.

How this is implemented depends heavily on the provider. For the collection provider, it returns the length of the collection. For a DB provider it might do a “select count” for a given table.

A count is easily done like this:

number_of_items = From(col).where("item.value > 10").count()

This code returns the count of items in the given collection that have a value greater than 10.

Average

The average call returns the arithmetic mean (Wikipedia) for the items that the given expression returned.

How this is implemented depends heavily on the provider. For the collection provider, it returns the average of the specified property for items in the collection. For a DB provider it might do a “select avg(field)” for a given table.

An average mean is easily done like this:

number_of_items = From(col).where("item.value > 10").avg("salary")

This code returns the mean average of the “salary” property for all the items in the given collection that have a value greater than 10.

Please note that if the specified property is not a number an error will be raised.

Sum

The sum call returns the sum for the items that the given expression returned.

How this is implemented depends heavily on the provider. For the collection provider, it returns the sum of the specified property for items in the collection. For a DB provider it might do a “select sum(field)” for a given table.

A sum is easily done like this:

avg = From(col).where("item.value > 10").sum("salary")

This code returns the sum of the “salary” property for all the items in the given collection that have a value greater than 10.

Please note that if the specified property is not a number an error will be raised.

Max

The max call returns the max item for the items that the given expression returned.

How this is implemented depends heavily on the provider. For the collection provider, it returns the max() of the specified property for items in the collection. For a DB provider it might do a “select max(field)” for a given table.

A max is easily done like this:

max = From(col).where("item.value > 10").max("salary")

This code returns the greatest “salary” property for all the items in the given collection that have a value greater than 10.

Min

The min call returns the min item for the items that the given expression returned.

How this is implemented depends heavily on the provider. For the collection provider, it returns the min() of the specified property for items in the collection. For a DB provider it might do a “select min(field)” for a given table.

A min is easily done like this:

min = From(col).where("item.value > 10").min("salary")

This code returns the lowest “salary” property for all the items in the given collection that have a value greater than 10.

Clone this wiki locally