views:

746

answers:

1

I'm in a situation where I need to span Google App Engine entity relationships in a query like in the Django database model. I'm using ListPropertys for one-to-many relationships, like so:

class Foo(db.Model): bars = db.ListProperty(db.Key)
class Bar(db.Model): eggs = db.ListProperty(db.Key)

And I'd like to perform a query that does the following:

# Foo.filter('bars.eggs =', target_egg)
[foo
for egg in eggs if egg == target_egg
for eggs in bar.eggs
for bar in foo.bars
for foo in Foo.all()]

The comprehension seems radically inefficient. I'd really like to perform a query as in the commented out portion, but it doesn't look like the GQL syntax allows for queries against the attributes of attributes:

   SELECT * FROM <kind>
    [WHERE <condition> [AND <condition> ...]]
    [ORDER BY <property> [ASC | DESC] [, <property> [ASC | DESC] ...]]
    [LIMIT [<offset>,]<count>]
    [OFFSET <offset>]

  <condition> := <property> {< | <= | > | >= | = | != } <value>
  <condition> := <property> IN <list>
  <condition> := ANCESTOR IS <entity or key>
+2  A: 

You're right, the App Engine datastore doesn't allow for this sort of query. And you're right that the list comprehension is inefficient. Consider, though, that that is pretty much exactly what a relational database does when you execute a query with joins like your one - the database has to perform the same O(n^3) work you're doing here - the only difference is that you're doing it in Python, and with additional round trip time. Since App Engine is designed to scale, it's not really made for these sort of queries.

Usually, though, there's a way you can denormalise your model a bit to facilitate this, by moving some of the properties you need to access onto the Foo model, or if you are doing aggregates, by moving the total onto the Foo model. It's difficult to give concrete solutions without more of an idea of what problem you're trying to solve, though.

Nick Johnson