Reputation: 6828
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!
}
Upvotes: 1
Views: 430
Reputation: 2362
Assuming that Article to Category is a one-to-many relationship and that the Category has a many-to-one property called Article here is a VERY dirty way of doing this (I am really not proud of this but it works)
List<long> catkeys = new List<long>() { 4, 5, 6, 7 };
if (catkeys.Count == 0)
return;
var cr = Session.CreateCriteria<Article>("article")
.CreateCriteria("Categories", "cat0")
.Add(Restrictions.Eq("cat0.Id", catkeys[0]));
if (catkeys.Count > 1)
{
for (int i = 1; i < catkeys.Count; i++)
{
cr = cr.CreateCriteria("Article", "a" + i)
.CreateCriteria("Categories", "cat" + i)
.Add(Restrictions.Eq("cat" + i + ".Id", catkeys[i]));
}
}
var results = cr.List<Article>();
What it does is to re-join the relationship over and over again guaranteeing you the AND between category Ids. It should be very slow query especially if the list of Ids gets big.
I am offering this solution as NOT a recommended way but at least you can have something working while looking for a proper one.
Upvotes: 0
Reputation: 9757
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>
Upvotes: 1
Reputation: 12601
I'm not 100% sure, but I think query by example may be what you want.
Upvotes: 0