2010-12-10 4 views
1

J'ai une liste de questions, chacune liée à une liste d'étiquette.NHibernate plusieurs-à-plusieurs critères

Et les données suivantes:

Question1 : Tag1 
Question2 : Tag1, Tag2 
Question3 : Tag1, Tag2, Tag3 
Question4 : Tag1, Tag3 

Les critères suivants:

var tagsIds = new int[] { tag1, tag2 }; 

var res = session.CreateCriteria<Question>() 
    .CreateCriteria("Tags") 
    .Add(Restrictions.In("id", tagsIds)) 
    .List<Question>(); 

retours (je comprends pourquoi, le "dans" agit comme un OR)

Question1, Question2, Question3, Question4 

Ou Je voudrais obtenir seulement

Question2, Question3 

car ils ont tous les deux tag1 et tag2. Y at-il un moyen de le faire?

Dans SQL, je ferais quelque chose comme:

SELECT * 
FROM Question q 
WHERE EXISTS (
    SELECT * 
    FROM QuestionsToTags qtt 
    WHERE qtt.Question_id = q.Id 
    AND qtt.Tag_id IN (1, 2) 
    GROUP BY qtt.Question_id 
    HAVING COUNT(qtt.Question_id) >= 2 
) 

Répondre

0

En utilisant hql:

var q = NHibernateSession.CreateQuery(
@"from Question question 
    where exists( 
     select q.id from Question q 
     join q.Tags t 
     where 
      t.id in (:ids) 
      and q.id = question.id 
     group by q.id 
     having count(t.id)=:c_count)"); 

q.SetParameterList("ids", tagIds); 
q.SetInt32("c_count", tagIds.Length); 

Et en utilisant un ICriteria:

// here is the exists part 
var dCriteria = DetachedCriteria.For<Question>("q") 
    .SetProjection(Projections.GroupProperty(Projections.Id())) 
    .Add(Restrictions.Eq(Projections.Count(Projections.Id()), tagIds.Length)) 
    // here we filter on the "parent" criteria 
    .Add(Restrictions.EqProperty("q.id", "question.Id")) 
    .CreateCriteria("Tags") 
    .Add(Restrictions.In("id", tagIds)); 

var crit = NHibernateSession 
    .CreateCriteria<Question>("question") 
    .Add(Subqueries.Exists(dCriteria)); 
1

Si vous avez seulement deux puis utilisez une et restriction

var res = session.CreateCriteria<Question>() 
    .CreateCriteria("Tags") 
    .Add(Restrictions.And(Restrictions.Eq("id", tag1), Restrictions.Eq("id", tag2)) 
    .List<Question>(); 

Si vous avez plus de deux ou un nombre inconnu puis utilisez Conjonction :

var conj = new Conjunction(); 
// use a loop to add all tags if number is unknown 
conj.Add(Restrictions.Eq("id", tag1); 
conj.Add(Restrictions.Eq("id", tag2); 

var res = session.CreateCriteria<Question>() 
    .CreateCriteria("Tags") 
    .Add(conj) 
    .List<Question>(); 

Il existe également une classe de disjonction pour gérer plusieurs conditions Or.

+0

Merci, mais cela ne fonctionne pas, car il génère l'instruction SQL suivante: SELECT [snipped] DE "Question" this_ jointure interne QuestionsToTags tags3_ sur this_.Id = tags3_.Question_id jointure interne "Tag" tag1_ sur tags3_.Tag_id = tag1_.Id O WH (tag1_.Id = 1 et tag1_.Id = 2). Donc, rien n'est renvoyé, car id ne peut pas être à la fois 1 et 2 – mathieu

Questions connexes