Question

I'm storing a hierarchy of categories using the nested sets model, that is, each category has a Left and a Right value and each category has a higher Left and a smaller Right than any parent.

I'd like to query all categories that are sub-categories of a certain category (including that category). That is, I need the whole subtree starting at a given category.

If the id of the root of the subtree is given by @catId, I could use the following SQL to fetch the subtree:

select * 
    from Category c, 
    (select [Left], [Right] from Category where Id = @catId) as t
where c.[Left] >= t.[Left] 
 and c.[Right] <= t.[Right]

Now I'm trying to do similar using NHibernate and the QueryOver API. That's where I'm a bit stuck.

If I split this up into two queries, it's easy:

var cat = session.Get<Category>(catId);

var matches = session.QueryOver<Category>()
                     .Where(x => x.Left >= cat.Left && x.Right <= cat.Right)
                     .List();

But that's two queries - one would be better. I tried to come up with a solution that uses subqueries, and although this technically works, the query probably isn't optimal, because now two subqueries are executed instead of one.

var matches = session.QueryOver<Category>()
                     .WithSubquery.WhereProperty(x => x.Left)
                        .Ge(QueryOver.Of<Category>()
                          .Where(c => c.Id == catId)
                          .Select(c => c.Left))
                      .WithSubquery.WhereProperty(x => x.Right)
                        .Le(QueryOver.Of<Category>()
                           .Where(c => c.Id == catId)
                           .Select(c => c.Right)).List();

(In addition to this, not all DBMS support subqueries that return scalar values, like SqlServerCE, but that's another issue)

Are there better ways to achieve this? If necessary, I can switch the query API for this case. So if e.q. there's a neat way to do it in HQL, I'm fine with it.

Was it helpful?

Solution

I thought to get a sub-tree including the tree in nested sets would be something like (SQL):-

SELECT 
    node.*
FROM 
    Category AS node,
    Category AS parent
WHERE 
    node.left BETWEEN parent.left AND parent.right
    AND parent.TreeID = @catID
ORDER BY 
    node.left;

This would use a CROSS JOIN which is not possible in QueryOver (AFAIK)

However it is possible in either HQL or even better linq.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top