Hi,

I'm trying to use the 'contains_eager' option but experiencing some 
difficulty figuring out how to make it generate what I need.

Short background:

The code is part of an automated search-generation algorithm that takes 
kw=val constructs (such as user.name='bob', user.job.title='engineer', 
user.job.desk.desk_make='steelcase') and joins related models as necessary 
to produce the final query.

(1) because the query is generated automatically, the code typically joins 
on aliased models to avoid potential conflicts.
(2) there is no predetermined limit to the number of relationships the 
query can span, and there are a number of cases where spanning 2 
relationships is very useful.
(3) it is necessary to be able to filter on any or all of the referenced 
models

I believe that (2) and (3) pretty much force me to explicitly handle the 
joins myself and then use 'contains_eager' to eagerly load the related 
model records, as described in:

http://www.sqlalchemy.org/docs/orm/loading.html#contains-eager

Here is a made-up example:

Given models such as:

class User:
    user_id = Integer
    job = Relationship(Job)
    (...)

class Job:
    job_id = Integer
    desk=Relationship(Cubicle)

class Cubicle:
    cubicle_id = Integer
    desk_make = Text
    cubicle_location = Integer

If I want to identify Users whose job provides them with a desk made by 
steelcase,

My code looks something like:

q=Session.Query(User)

job_alias=Aliased(Job)
cubicle_alias=Aliased(Cubicle)

q=q.join(job_alias, from_jointpoint=True)
q=q.join(cube_alias, from_jointpoint=True)

# now I wish to force eager loading.  According to the sqlalchemy docs, 
# I need to provide the complete path from User to Cubicle, 
# which would lead me to use something like:

q=q.options(contains_eager(User.job, Job.desk))

# but also according to the docs, if I am using an alias, 
# I need to supply that to the contains_eager option 
# so that the aliased table can be properly identified.

# the example in the docs looks like:

q=q.options(contains_eager(User.job, alias=job_alias))

# if one of these approaches worked, then, of course, I could filter like 
so:

q.filter(cubicle_alias.desk_make == 'steelcase')

----

Unfortunately I do not see how to provide the necessary full path from the 
root of the query to the tip of the join and at the same time provide a 
value for alias for each node of the join.

I have taken a cursory look at the contains_eager function and followed the 
trail of inherited methods/models back a couple of steps, but I do not 
immediately see any way to induce it to handle both a list of joins and a 
list of corresponding aliases.

For what it is worth, I've written an earlier version of the search 
function which works by recursively joining as necessary across related 
models.  It is functional, but because there is no eager loading, it can be 
slow on large collections, given the number of separate queries it 
generates.

If I can't get contains_eager to work with joins across multiple aliased 
models, I can write a more sophisticated algorithm which will only create 
aliases as necessary.  Then it would have to 'chunk' the eager loading, 
breaking it at joins to aliased models.  This would reduce the performance 
somewhat but should solve the problem.

It does seem though, that there is no reason why contains_eager should not 
handle the case I am describing, so I feel like I'm just missing something 
obvious.

Any help or direction would be most appreciated.

Thanks,

Justin Thiessen

-- 
You received this message because you are subscribed to the Google Groups 
"sqlalchemy" group.
To view this discussion on the web visit 
https://groups.google.com/d/msg/sqlalchemy/-/nMPaY2XiBmcJ.
To post to this group, send email to sqlalchemy@googlegroups.com.
To unsubscribe from this group, send email to 
sqlalchemy+unsubscr...@googlegroups.com.
For more options, visit this group at 
http://groups.google.com/group/sqlalchemy?hl=en.

Reply via email to