Owner - lazy loading

Topics: Core, Writing modules
Nov 18, 2011 at 8:56 PM

How to load a long list of blogs (~/Admin/Blogs) without lazy loading of information about the owners of blogs in separate queries. This is because the Owner is LazyField <IUser>. How to join users to CommonPartRecord?

Coordinator
Nov 18, 2011 at 9:02 PM

The content manager queries can do joins. Did you try that?

Nov 18, 2011 at 9:15 PM

In this case you probably can't do a join because the IUser is a separate content item and has to load separately thru ContentManager.

Coordinator
Nov 18, 2011 at 9:49 PM

Well, there are new attributes in 1.3 (used in tags) that enable you to instruct the content manager to do eager loading.

Nov 18, 2011 at 9:50 PM
Edited Nov 18, 2011 at 9:52 PM

@randompete

Exactly, the code:

_contentManager.Query<BlogPart, BlogPartRecord>("Blog")
                                            .Join<CommonPartRecord>()
                                            .Join<UserPartRecord>()
                                            .List();

 

joins UserPartRecord to ContentItemRecord by Id and not by OwnerId. And this query returns 0 rows.

How to do it differently?

Nov 18, 2011 at 10:00 PM
bertrandleroy wrote:

Well, there are new attributes in 1.3 (used in tags) that enable you to instruct the content manager to do eager loading.

You mean [Aggregate]? But why use [Aggregate] in Record and LazyField in Part?

Coordinator
Nov 18, 2011 at 10:01 PM

I don't know.

Nov 18, 2011 at 10:08 PM
Edited Nov 18, 2011 at 10:09 PM

@tjedrzejczak, the code you've shown is doing this query: Find all content items of type "Blog" that have both CommonPart and UserPart. But no "Blog" items have UserPart, hence there are 0 matching items. The only content type that has UserPart is "User", which is a content type all of its own.

The thing about content items is that they themselves are built up of a sometimes quite large join between multiple tables. Each ContentPart usually contributes a record of its own, but there's never any way for the API to know in advance exactly which records to join to a ContentItem. So any time you load a ContentItem it has to be through a new query on ContentManager; simply put, you can't eager load content items. The [Aggregate] attribute Bertrand mentions unfortunately will only work for simple record-to-record joins; ContentItems are a whole separate story!

But - depending on precisely what information you want from the User items and how many users you actually have on your site, you could consider some sort of caching to optimise it. If the slowness is because the User is then itself lazy-loading all its attached parts, then you could actually do some eager loading there so at least all the parts are loaded together, and bypass the LazyField. How long is the list exactly? Can't you page the blog list itself?

Nov 19, 2011 at 11:35 AM

This code is a response to a proposal to use ContentManager - it's impossible.
I have hundreds of blogs. But regardless of whether there are 5 or 500 you should always optimize the system. If the database will execute only 5 unnecessary SQL queries, and so it is about 5 too many.
Paging only hides the problem rather than solve it.
Unfortunately Orchard can not optimally display some lists of content.

Nov 19, 2011 at 12:55 PM

There's a viewpoint that you only optimise something when it becomes a bottleneck. Otherwise you could spend all your time optimising every last little detail for no actual gain.

You're ignoring my suggestion of caching. Caching is a perfectly valid way to optimise this scenario.

Nov 19, 2011 at 1:02 PM

By the way - if you'd really like to see this made possible (and it would be handy, for me as well), I guess it would be possible to create some kind of ContentJoin(..) method on ContentQuery for performing content-to-content joins. Why not have a look into ContentManager's internals to see how to do it, create a fork, and submit a patch. It's a community project after all; that's why it's open source ;)