2010-04-21 8 views
1

Je suis en utilisant des rails 3.0 et MySql 5.1filtrage exclusif par tag

Je ces trois modèles:

Question, Tag et QuestionTag.

La balise a une colonne appelée nom.

La question comporte de nombreuses balises dans QuestionTags et vice versa. Supposons que j'ai n. Comment puis-je trouver seulement les questions qui ont toutesn étiquettes, identifiées par le nom de l'étiquette.

Et comment le faire en une seule requête.

(Si vous pouvez me convaincre que de le faire dans plus d'une requête est optimale, je serai ouvert à cette idée)

A rails pur 3 solution serait préférable, mais je ne suis pas défavorable à un pur Solution SQL soit.

S'il vous plaît notez que la difficulté est à faire une requête qui ne donne pas toutes les questions qui ont l'une des balises, mais seulement les questions qui ont toutes les balises.

Répondre

0

C'est la solution que j'ai trouvée pour moi-même. Non modifié, il ne fonctionnera que dans Rails 3 (ou supérieur).

Dans le modèle Tag:

scope :find_by_names, lambda { |names| 
    unless names.empty? 
    where("tags.name IN (#{Array.new(names.length, "?").join(",")})", *names) 
    else 
    where("false") 
    end 
} 

Dans le modèle Question:

scope :tagged_with, lambda { |tag_names| 
    unless tag_names.blank? 
    joins(:question_tags). 
    where("questions.id = question_tags.question_id"). 
    joins(:tags).where("tags.id = question_tags.tag_id"). 
    group("questions.id"). 
    having("count(questions.id) = ?", tag_names.count) & Tag.find_by_names(tag_names) 
    else 
    scoped 
    end 
} 

Le & Tag.find_by_names(tag_names) combine les deux champs d'application tels que la jointure sur tags est vraiment une jointure sur le modèle scope.

[Mise à jour]

Mon sql-fu a amélioré un peu, donc je pensais que je propose une solution pure SQL aussi:

SELECT q.* 
FROM (
SELECT DISTINCT q.* 
FROM `questions` q 
JOIN question_tags qt 
ON qt.question_id = q.id 
JOIN tags t 
ON t.id = qt.tag_id 
WHERE t.name = 'dogs' 
) AS q 
JOIN question_tags qt 
ON qt.question_id = q.id 
JOIN tags t 
ON t.id = qt.tag_id 
WHERE t.name = 'cats' 

Ceci trouve toutes les questions qui ont été marqués avec les «chats» et les «chiens». L'idée est d'avoir une sous-requête imbriquée pour chaque tag que je veux filtrer.

Il y a plusieurs autres façons d'y parvenir. Je ne suis pas sûr si cela fait une différence d'avoir la sous-requête dans la clause FROM au lieu de la clause WHERE. Toute idée serait appréciée.