2009-04-25 7 views
0

J'ai une table de relation:Impossible de définir: joint les conditions dans la relation has_many?

create_table "animal_friends", :force => true do |t| 
    t.integer "animal_id" 
    t.integer "animal_friend_id" 
    t.datetime "created_at" 
    t.datetime "updated_at" 
    t.integer "status_id",  :default => 1 
    end 

reliant les animaux à d'autres. La meilleure façon de retreive associations SQL est:

SELECT animals.* 
from animals join animal_friends as af 
    on animals.id = 
    case when af.animal_id = #{id} then af.animal_friend_id else af.animal_id end 
WHERE #{id} in (af.animal_id, af.animal_friend_id) 

Et je ne peux pas trouver un moyen de créer une bonne relation has_many dans des rails avec cela. Apparemment, il n'y a aucun moyen de fournir des conditions de jointure pour has_many.

J'utilise actuellement un finder_sql:

has_many :friends, :class_name => "Animal", :finder_sql => 'SELECT animals.* from animals join animal_friends as af on animals.id = case when af.animal_id = #{id} then af.animal_friend_id else af.animal_id end ' + 
'WHERE #{id} in (af.animal_id, af.animal_friend_id) and status_id = #{Status::CONFIRMED.id}' 

mais cette méthode a le grand inconvénient de briser la magie activerecord. Par exemple:

@animal.friends.first 

exécutera la finder_sql sans limite, aller chercher des milliers de lignes, puis en prenant la première du tableau (et perdre plusieurs secondes précieuses /REQ).

Je pense qu'il est une fonctionnalité manquante de AR, mais je voudrais être sûr d'abord :) Merci

Répondre

2

Vous pourriez résoudre ceci au niveau de la base de données avec une vue, ce qui serait la bonne méthode quand même.

CREATE VIEW with_inverse_animal_friends (
    SELECT id, 
     animal_id, 
     animal_friend_id, 
     created_at, 
     updated_at, 
     status_id 
    FROM animal_friends 
    UNION 
    SELECT id, 
     animal_friend_id AS animal_id, 
     animal_id AS animal_friend_id, 
     created_at, 
     updated_at, 
     status_id 
    FROM animal_friends 
) 

Si vous ne voulez pas avoir des entrées doubles pour les amis des relations dans les deux sens, vous pouvez le faire:

CREATE VIEW unique_animal_friends (
    SELECT MIN(id), animal_id, animal_friend_id, MIN(created_at), MAX(updated_at), MIN(status_id) 
    FROM 
    (SELECT id, 
      animal_id, 
      animal_friend_id, 
      created_at, 
      updated_at, 
      status_id 
     FROM animal_friends 
    UNION 
    SELECT id, 
      animal_friend_id AS animal_id, 
      animal_id AS animal_friend_id, 
      created_at, 
      updated_at, 
      status_id 
     FROM animal_friends) AS all_animal_friends 
    GROUP BY animal_id, animal_friend_id 
) 

Vous auriez besoin d'un moyen de décider status_id à utiliser dans le cas où il y a deux contradictoires ceux J'ai choisi MIN (status_id) mais ce n'est probablement pas ce que vous voulez.

Rails vous pouvez le faire maintenant:

class Animal < ActiveRecord::Base 
    has_many :unique_animal_friends 
    has_many :friends, :through => :unique_animal_friends 
end 

class UniqueAnimalFriend < ActiveRecord::Base 
    belongs_to :animal 
    belongs_to :friend, :class_name => "Animal" 
end 

Ceci est hors de ma tête et non testé. En outre, vous pourriez avoir besoin d'un plugin pour la gestion des vues dans les rails (comme "redhillonrails-core").

+0

Merci, j'aime l'idée! Je vais tester dès que je peux et vous le faire savoir. – Gravis

+0

Désolé pour le délai loooooooong avant de répondre à cela, j'ai finalement eu le temps de tester votre solution, et cela fonctionne comme un charme, hors de la boîte. – Gravis

0

Les deux façons de créer beaucoup de nombreuses relations dans l'enregistrement actif sont has_and_belongs_to_many et has_many: par. This site critique les différences entre les deux. Vous n'avez pas besoin d'écrire de SQL en utilisant ces méthodes.

+0

salut, merci pour le lien! Quoi qu'il en soit, habtm n'autorise aucun type de jointure :( Donc, je vais devoir faire la jointure en utilisant: conditions, et comme j'ai plus de 100.000 lignes (~ 400.000), je souhaite avoir un SQL vraiment optimisé question. En utilisant un habtm, j'obtiendrais seulement la moitié des résultats, puisque je cherche aussi des relations dans foreign_key ET association_foreign_key. merci – Gravis

1

Il existe un plugin qui fait ce que vous voulez.

Il ya un post à ce sujet here.

Il existe une alternative here.

Les deux vous permettent de faire des conditions de jointure et n'utilisent que l'initialisation paresseuse. Vous pouvez donc utiliser des conditions dynamiques. Je trouve l'ancien plus joli, mais vous pouvez utiliser ce dernier si vous ne voulez pas installer de plugins.

Questions connexes