2009-07-08 6 views
3

J'ai deux modèles, associés à un HABTM (en utilisant actuellement has_many: à travers aux deux extrémités, avec une table de jointure). J'ai besoin de récupérer tous les ModelAs associés aux deux ModelBs. Je ne veux pas que tous les ModelAs pour ModelB_1 soient concaténés avec tous les ModelAs pour ModelB_2. Je veux littéralement tous les ModelAs associés à BOTH ModelB_1 et ModelB_2. Il n'est pas limité à seulement 2 ModelBs, il peut être jusqu'à 50 ModelBs, donc cela doit évoluer.HABTM trouve avec "AND" jointures, pas "OU"

Je peux décrire le problème en utilisant une variété d'analogies, que je pense que mieux décrit mon problème que le paragraphe précédent:

* Find all books that were written by all 3 authors together. 
* Find all movies that had the following 4 actors in them. 
* Find all blog posts that belonged to BOTH the Rails and Ruby categories for each post. 
* Find all users that had all 5 of the following tags: funny, thirsty, smart, thoughtful, and quick. (silly example!) 
* Find all people that have worked in both San Francisco AND San Jose AND New York AND Paris in their lifetimes. 

J'ai pensé à une variété de façons d'y parvenir, mais ils » re grossièrement inefficace et très désapprouvé. En prenant une analogie ci-dessus, disons le dernier, vous pourriez faire quelque chose comme une requête pour toutes les personnes dans chaque ville, puis trouver des éléments dans chaque tableau qui existent dans chaque tableau. C'est un minimum de 5 requêtes, toutes les données de ces requêtes sont renvoyées à l'application, puis l'application doit comparer intensivement les 5 tableaux les uns aux autres (boucles à gogo!). C'est méchant, non?

Une autre solution possible consisterait à enchaîner les trouvailles les unes sur les autres, ce qui ferait essentiellement la même chose que ci-dessus, mais n'éliminerait pas les requêtes multiples et le traitement. En outre, comment dynamiseriez-vous la chaîne si des cases à cocher ou des valeurs soumises par l'utilisateur pouvaient contenir jusqu'à 50 options? Semble sale. Vous auriez besoin d'une boucle. Et encore une fois, cela intensifierait la durée de la recherche. De toute évidence, si possible, nous aimerions que la base de données effectue cela pour nous, donc, les gens m'ont suggéré que je mettais simplement plusieurs conditions. Malheureusement, vous ne pouvez généralement faire qu'un OU avec HABTM.

Une autre solution que j'ai rencontrée est d'utiliser un moteur de recherche, comme sphinx ou UltraSphinx. Pour ma situation particulière, je pense que c'est exagéré, et je préfère l'éviter. Je pense toujours qu'il devrait y avoir une solution qui permettra à un utilisateur d'élaborer une requête pour un nombre arbitraire de ModelB et de trouver toutes les ModelA.

Comment pourriez-vous résoudre ce problème?

+0

Je voudrais particulièrement un moyen d'utiliser JOINs et GROUPings d'une certaine manière dans une requête personnalisée pour aider à effectuer cela. La créativité SQL est la bienvenue! –

Répondre

4

Vous pouvez le faire:

  1. construire une requête à partir de votre modela, joignant ModelB (à travers le joint modèle), le filtrage des ModelBs qui ont l'une des valeurs que vous recherchez, qui met les dans OU (c'est-à-dire where ModelB = 'ModelB_1' or ModelB = 'ModelB_2'). Avec cette requête, l'ensemble de résultats aura plusieurs lignes 'ModelA', exactement une ligne pour chaque condition ModelB satisfaite.

  2. ajouter un groupe par condition à la requête sur les colonnes ModelA dont vous avez besoin (même si vous le souhaitez). Le nombre () pour chaque ligne est égal au nombre de conditions ModelB satisfaites *.

  3. ajouter un 'ayant' état en sélectionnant uniquement les lignes dont count(*) est égal au nombre de conditions ModelB vous devez avoir satisfait

exemple:

model_bs_to_find = [100, 200] 
ModelA.all(:joins=>{:model_a_to_b=>:model_bs}, 
      :group=>"model_as.id", 
      :select=>"model_as.*", 
      :conditions=>["model_bs.id in (?)", model_bs_to_find], 
      :having=>"count(*)=#{model_bs_to_find.size}") 

N.B.les paramètres de groupe et de sélection spécifiés de cette manière fonctionneront dans MySQL, la manière standard de SQL de le faire serait de mettre toute la liste des colonnes model_as dans les paramètres de groupe et de sélection.

+0

Incroyable! Cela fonctionne très bien. L'ajout du GROUP BY et HAVING de cette manière à la requête SQL a parfaitement fonctionné. Je testerai la méthode Ruby illustrée ci-dessus dans peu de temps. –

+0

Existe-t-il un moyen de référencer la table de jointure dans l'instruction: joins sans utiliser de symbole pour une référence de modèle? Ma table de jointure existe dans la base de données, mais ce n'est pas un modèle (puisque j'utilise HABTM plutôt que has_many: through). –

+0

: jointures =>: model_bs a semblé bien fonctionner! –

Questions connexes