2012-01-20 2 views
2

У меня есть модель пользователя дружба, которую я хочу написать поиск дляобратного отношения в таблице соединение

это отношения HABTM под названием сверстники, относящиеся 2 пользователей вместе. Отношение - это единственное соединение (Joe < -> Стив, а не Джо -> Стив и Стив -> Джо).

Моя таблицу соединения выглядит следующим образом: user_id peer_id

Как хранить идентификатор пользователя в отношениях. Ниже находится HABTM для пользователя.

has_and_belongs_to_many :peers, class_name: 'User', 
foreign_key: 'user_id', association_foreign_key: 'peer_id', 
join_table: 'users_peers' 

Я пытаюсь выяснить искатель SQL, который позволит эту запись в таблице присоединиться, чтобы показать обе стороны. user_id = steve.id peer_id = joe.id

, чтобы показать отношения, когда я вызываю joe.peers и steve.peers. В настоящее время steve.peers возвращает joe, но joe.peers ничего не показывает.

+0

см. related: http://dba.stackexchange.com/questions/10199/how-should-i-design-a-relationship-table-for-friendship – klochner

ответ

2

Обычно отношения лучше всего выражаются в одном направлении или в паре односторонних отношений. Это связано с тем, что в большинстве реляционных баз данных легко установить отношения A-B или B-A, но не оба с одной записью. Вам в основном нужны два запроса, если вы не делаете много предположений и не взламываете.

По моему опыту, использование has_and_belongs_to_many не облегчит вашу жизнь, так как это религия Rails 1.0, которая не так хороша, как метод has_many :through, который заменил его. В вашем случае это, как это будет играть:

class Node < ActiveRecord::Base 
    has_many :peers_of, 
    :class_name => 'Peer', 
    :foreign_key => :of_user_id 

    has_many :peers_to, 
    :class_name => 'Peer', 
    :foreign_key => :to_user_id 

    has_many :peers, 
    :through => :peers_of, 
    :source => :to_user 

    has_many :peers_with, 
    :through => :peers_to, 
    :source => :of_user 
end 

class Peer < ActiveRecord::Base 
    belongs_to :of_user, 
    :class_name => 'User' 
    belongs_to :to_user, 
    :class_name => 'User' 
end 

Семантика получить немного грязный, так что вы, вероятно, хотите, чтобы настроить их. Идея здесь состоит в том, чтобы установить двунаправленную связь при добавлении «равный», где это состоит из пары записей A-> B и B-> A.

В целях запроса вы получите только, например, @user.peers и не должны беспокоиться об обратном отношении peers_with, поскольку это должно приводить к идентичным результатам, если вы сохранили целостность данных.

+0

Спасибо, я буду дайте это посмотреть. Я просто надеялся на какую-то магию :). Двунаправленные самореферентные отношения заставляют мой разум немного болеть ... – Jared

+0

Легче написать задачу для проверки и исправления отношений, если одна из пар отсутствует, чем для выяснить, как сделать однонаправленную функцию отношения как двунаправленную. – tadman

1

Вы могли бы просто написать SQL вручную:

class PeerRelation 
    belongs_to :user1, :class_name=>"User" 
    belongs_to :user2, :class_name=>"User" 
end 

class User 

    def set_peer(user) 
    user1_id, user2_id = [self.id, user.id].sort 
    PeerRelation.find_or_create_by_user1_id_and_user2_id(user1_id, user2_id) 
    end 

    def peers 
    User.joins("inner join peer_relations on 
        peer_relations.user1_id = #{self.id} or 
        peer_relations.user2_id = #{self.id}") 

    end 
end 

Но подход Тадман является умнее с точки зрения в целостности данных, и более в соответствии с тем, что АБД бы вам сказать. (см. мой комментарий к вашему вопросу)

+0

Вы также можете реализовать это как определение 'finder_sql' в отношении, но выполнение' JOIN' с 'OR' в нем заставляет большинство баз данных плакать, если таблица большая. – tadman

+0

не знал, что - спасибо за отзыв – klochner

Смежные вопросы