R – NHibernate: how to retrieve an entity that “has” all entities with a certain predicate in Criteria

criterianhibernate

I have an Article with a Set of Category.
How can I query, using the criteria interface, for all Articles that contain all Categories with a certain Id?

This is not an "in", I need exclusively those who have all necessary categories – and others. Partial matches should not come in there.

Currently my code is failing with this desperate attempt:

var c = session.CreateCriteria<Article>("a");
if (categoryKeys.HasItems())
{
    c.CreateAlias("a.Categories", "c");
    foreach (var key in categoryKeys)
        c.Add(Restrictions.Eq("c", key)); //bogus, I know!
}

Best Solution

Use the "IN" restriction, but supplement to ensure that the number of category matches is equal to the count of all the categories you're looking for to make sure that all the categories are matched and not just a subset.

For an example of what I mean, you might want to take a look at this page, especially the "Intersection" query under the "Toxi solution" heading. Replace "bookmarks" with "articles" and "tags" with "categories" to map that back to your specific problem. Here's the SQL that they show there:

SELECT b.*
FROM tagmap bt, bookmark b, tag t
WHERE bt.tag_id = t.tag_id
AND (t.name IN ('bookmark', 'webservice', 'semweb'))
AND b.id = bt.bookmark_id
GROUP BY b.id
HAVING COUNT( b.id )=3

I believe you can also represent this using a subquery that may be easier to represent with the Criteria API

SELECT Article.Id
FROM Article 
INNER JOIN (
      SELECT ArticleId, count(*) AS MatchingCategories 
      FROM ArticleCategoryMap  
      WHERE CategoryId IN (<list of category ids>)
      GROUP BY ArticleId
) subquery ON subquery.ArticleId = EntityTable.Id 
WHERE subquery.MatchingCategories = <number of category ids in list>
Related Question