Joining Orchard part records in HQL

In yesterday’s post, I showed the basics of HQL querying in Orchard. It is by far the most flexible way to  query Orchard’s database, but one thing I didn’t show is how this works in relation to Orchard’s content type system. Querying over records is nice, but if those records are part records, you need to be really careful and check that they correspond to a real content item, that his content hasn’t been archived, and that its publication state is what you need it to be. In order to do that, you’ll have to join with ContentItemRecord and ContentItemVersionRecord. But how do you express joins in HQL in a way that works with Orchard records?

I would recommend you always start from the content item version record:

FROM Orchard.ContentManagement.Records.ContentItemVersionRecord ItemVersion

Then, you can join that to the content item record:

JOIN ItemVersion.ContentItemRecord Item

It’s easy to understand what’s going on here, because the ContentItemVersionRecord does have a ContentItemRecord property, that is of type ContentItemRecord. The mapping convention is easy and explicit here.

Joining with other part records, such as a UserPartRecord, is a little more puzzling, but works almost magically:

JOIN Item.UserPartRecord User

There is no UserPartRecord property on ContentItemRecord, so how does this work? Well, remember that Orchard creates mappings automatically from a bunch of conventions. It knows in particular that content items are aggregated from parts, and that when those parts are associated with a part record, joins should be made on the Id column of the part record and of the item record.

Finally, you’ll want to add some constraints, if you want to restrict what versions you want to get. For example, if you only want published versions, you’d add:

WHERE ItemVersion.Published = true

Wrapping it all together, here is a query that will get you all the ids for users that have no role:

SELECT DISTINCT User.Id
FROM Orchard.ContentManagement.Records.ContentItemVersionRecord ItemVersion
JOIN ItemVersion.ContentItemRecord Item
JOIN Item.UserPartRecord User
WHERE ItemVersion.Published = true
AND User.Id NOT IN (SELECT Role.UserId FROM Orchard.Roles.Models.UserRolesPartRecord Role)

Can you feel this series building itself? Next time, we’ll see how to get actual results out of those queries.

3 Comments

  • Although being able to use HQL is a "nice to have" feature for advanced edge-cases I really think that using it shouldn't be the norm for content querying. We should really design a good content querying API, and one should be able to avoid using string queries for 99,9% of the cases.

  • We already have two content querying APIs, one of which is a thin layer over nHibernate. What would we put in a new API that those are missing, and that is supported by nHibernate? I mean, the gold standard for a querying API is LINQ, so we'd need an implementation of IQueryable over the Orchard type system. Unfortunately, there are holes in the underlying nHibernate implementation of IQueryable that will still make it necessary to go to string queries in some advanced cases.

  • We talked about surfacing the strongly-types NHibernate QueryOver API with Orchard-specific extensions. I second this.

Comments have been disabled for this content.