These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 10 posts ] 
Author Message
 Post subject: Criteria query help?
PostPosted: Wed May 21, 2008 5:09 pm 
Newbie

Joined: Thu Jul 27, 2006 10:41 pm
Posts: 6
I have a class Product which has a collection of Category classes, i.e. Product.Categories. The relationship is many to one with an intermediate table PRODUCT_CATEGORY_MAPPINGS. I want to be able to query products which have not just one but ALL of the categories I specifiy. For example, I would want to query for products that have the categories Computer, Notebook & WindowsXP. Up until now I have been unable to figure out how to code this with a criteria query or HQL. I managed to do the filter in code but it was quite inefficient. Is there a way to do this with a criteria query or HQL? Thanks.


Top
 Profile  
 
 Post subject:
PostPosted: Wed May 21, 2008 7:04 pm 
Newbie

Joined: Mon Nov 06, 2006 12:59 pm
Posts: 5
From the reference manual:

Code:
List cats = sess.createCriteria(Cat.class)
.add( Restrictions.like("name", "F%") )
.createCriteria("kittens")
.add( Restrictions.like("name", "F%") )
.list();


This reduces to the Cats with kittens (this is a collection) which name starts with "F".

So in your case
Code:
List Prod = sess.createCriteria(Prod.class).createCriteria("categories")
.add(Restirctions.or(Restrictions.eq("name", "Computer"), Restrictions.eq("name", "Notebook")))


Top
 Profile  
 
 Post subject:
PostPosted: Wed May 21, 2008 8:01 pm 
Newbie

Joined: Thu Jul 27, 2006 10:41 pm
Posts: 6
Thanks for your reply. Actually that was one of the first things I tried but unfortunately it returns a superset of the data that I need. It returns products that have ANY rather than All of the categories. In the example I gave, products with the Notebook category would always also have the Computer category but there are other cases where that assumption is false. For example, I could have the categories Toy and Electronic, which are unrelated.


Top
 Profile  
 
 Post subject: Re: Criteria query help?
PostPosted: Wed May 21, 2008 10:16 pm 
Newbie

Joined: Wed Jul 20, 2005 12:57 am
Posts: 15
Location: Sydney, Australia
mahbouni wrote:
... I want to be able to query products which have not just one but ALL of the categories I specifiy. For example, I would want to query for products that have the categories Computer, Notebook & WindowsXP.


In SQL to select all or nothing you'd need to join to the Category table for each type and assign an alias. eg:

Code:
select *
from   product p
    join
          category cmp
    on  p.ProductId = cmp.ProductId
    join
         category ntBk
    on  p.ProductId = cmp.ProductId
// etc
where cmp.Name = 'Computer'
and   ntBk.Name = 'Notebook'
// etc


So for a Criteria you'd go with much the same. Try something like...

Code:
// Untestested...
ICriteria prodCriteria = sess.CreateCriteria( typeof( Product ), "prod" ).CreateAlias( "Categories", "computerCat" )
    .add( Expression.Eq( "computerCat.name", "Computer" ) );
prodCriteria.CreateAlias( "Categories", "noteBookCat" )
    .add( Expression.Eq( "noteBookCat.name", "Notebook" ) );

IList<Product> prods = prodCriteria.List< Product >();

_________________
Christian Maslen


Top
 Profile  
 
 Post subject:
PostPosted: Thu May 22, 2008 7:43 am 
Newbie

Joined: Thu Jul 27, 2006 10:41 pm
Posts: 6
Thanks for your reply. I did also try something like that but NHibernate will not allow me to alias the same association twice.

NHibernate.QueryException: "duplicate association path: Categories"


Top
 Profile  
 
 Post subject:
PostPosted: Thu May 22, 2008 8:26 pm 
Newbie

Joined: Wed Jul 20, 2005 12:57 am
Posts: 15
Location: Sydney, Australia
Quote:
I did also try something like that but NHibernate will not allow me to alias the same association twice.

NHibernate.QueryException: "duplicate association path: Categories"


I never actually tried it myself so sorry about that. The HQL would be:

Code:

select product
from   Product product
    join
       product.Categories computer
    join
       product.Categories notebook
where  computer.Name = 'Computer'
and    notebook.Name = 'Notebook'



But again not tested. If it doesn't work the SQL will, but I don't blame you for avoiding it. Personally for dynamic queries I try to avoid HQL and SQL.

_________________
Christian Maslen


Top
 Profile  
 
 Post subject:
PostPosted: Fri May 23, 2008 3:45 am 
Expert
Expert

Joined: Thu Dec 14, 2006 5:57 am
Posts: 1185
Location: Zurich, Switzerland
I had a similar problem and solved it like this:

This criteria should retrieve a list of distinct category ids out of a supplied list of categories (catIdList) that have been assigned to a product:

Code:
DetachedCriteria categories = DetachedCriteria.For( typeof(Product), pc)
   .Add( Expression.EqProperty( "pc.Id", "p.Id" ) )
   .CreateCriteria("Categories", "c")
       .Add( Expression.In( "c.Id", catIdList));
       .SetProjection(Projections.CountDistinct("c.Id"));


Now, you can use this in a subquery. The number of selected categories hast to be less or equal to the "count" returned from the subquery:

Code:
ICriteria products = session.CreateCriteria(typeof(Product),"p")
    .Add(Subqueries.Le(catIdList.Count, categories));


My scenario was slightly different and I tried this out of my head, so maybe there're some errors, but the idea should work.

_________________
--Wolfgang


Top
 Profile  
 
 Post subject:
PostPosted: Sat May 24, 2008 10:56 am 
Newbie

Joined: Thu Jul 27, 2006 10:41 pm
Posts: 6
Thank you both cmaslen and wolli. I tried first the HQL because it was easier to understand, and it worked. I still can not quite understand what the Criteria query is doing but it works and it also seems to be a bit faster than the HQL, so I'm going to go with it for now and try to understand it as best I can. Thanks again.


Top
 Profile  
 
 Post subject:
PostPosted: Mon May 26, 2008 3:18 am 
Expert
Expert

Joined: Thu Dec 14, 2006 5:57 am
Posts: 1185
Location: Zurich, Switzerland
I'll try to explain in more detail:

Let's assume, you want all products, that have assigned categories "Computer" and "XP" (and maybe more than that).

1) for each product, we count the number of categories out of this list, that have been assigned. Possible results are
0: none of the categories is assigned
1: either "Computer" or "XP" is assigned
2: both are assigned

2) No were are looking for all products, which have a count of 1) that is greater or equal to the number of categories that we're interested in:
count 1) >= 2
This will give us all products that have at least the wanted categories assigned.

1) is done in the deteached criteria, 2) is done in the subquery that uses the detached criteria.

Especially if you are looking for a lot of categories at once, that approach will be faster then building a join for each category.

_________________
--Wolfgang


Top
 Profile  
 
 Post subject:
PostPosted: Mon May 26, 2008 3:47 pm 
Newbie

Joined: Thu Jul 27, 2006 10:41 pm
Posts: 6
That explains things quite clearly now. There is a possibility to search on several categories so not having multiple joins is definitely an advantage. Thanks a lot.


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 10 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.