Rails连接两次相同的表

时间:2013-11-29 17:36:18

标签: ruby-on-rails performance activerecord

我有一张友谊的桌子。它的构建方式与http://railscasts.com/episodes/163-self-referential-association类似。现在我希望以最有效的方式获得所有关系,这些关系是双方的(正常和反向友谊)。

在MySQL中它应该看起来像:

SELECT * FROM Friendship as f1, Friendship as f2 WHERE f1.user_id = f2.friend_id AND f1.friend_id = f2.user_id

在对railscast的评论中,有人构建了mutual_friends部分的扩展。但这三条线在我的案例中不起作用:

设计用户模型:

class User < ActiveRecord::Base
 has_many :features
 has_and_belongs_to_many :tags
 has_many :friendships
 has_many :friends, through: :friendships
 has_many :inverse_friendships, class_name: 'Friendship', foreign_key: 'friend_id'
 has_many :inverse_friends, through: :inverse_friendships, source: :user
  devise :database_authenticatable, :registerable,
     :recoverable, :rememberable, :trackable, :validatable

  attr_accessible :first_name, :last_name, :email, :password, :date_of_birth, :gender, :password_confirmation

  validates_inclusion_of :gender, :in => ['female', 'male'], message: 'Kein Geschlecht ausgewaehlt'
  validates :first_name, presence: true
  validates :last_name, presence: true
  validates_date :date_of_birth, :on_or_before => lambda { Date.current }

 def mutual_friends
   inverse_friends.where('user_id in (?)', friend_user_ids)
 end
end

设计用户迁移:

class DeviseCreateUsers < ActiveRecord::Migration
  def change
   create_table(:users) do |t|
    t.string :email,              :null => false, :default => ""
    t.string :encrypted_password, :null => false, :default => ""
    t.string   :reset_password_token
    t.datetime :reset_password_sent_at
    t.datetime :remember_created_at
    t.integer  :sign_in_count, :default => 0
    t.datetime :current_sign_in_at
    t.datetime :last_sign_in_at
    t.string   :current_sign_in_ip
    t.string   :last_sign_in_ip
    t.string :first_name
    t.string :last_name
    t.datetime :date_of_birth
    t.string :gender
    t.integer :tag_id
    t.timestamps
   end

   add_index :users, :email,                :unique => true
   add_index :users, :reset_password_token, :unique => true
 end
end

友谊模式

class Friendship < ActiveRecord::Base
 attr_accessible :user_id, :friend_id

 belongs_to :user
 belongs_to :friend, class_name: 'User'
end

友谊迁移

class CreateFriendships < ActiveRecord::Migration
 def self.up
  create_table :friendships do |t|
   t.integer :user_id
   t.integer :friend_id
   t.timestamps
  end
 end

 def self.down
  drop_table :friendships
 end
end

此解决方案中的错误是:

undefined local variable or method `friend_user_ids' for #<User:0x007fcb93e86de8>

Railscast评论中的原始星座是:

朋友模特

 class Friend < ActiveRecord::Base
  belongs_to :profile
  belongs_to :friend, :class_name => 'Profile'
 end

个人资料模型

class Profile < ActiveRecord::Base
   has_many  :friends
   has_many  :friend_profiles, :through => :friends, source: :friend
   has_many  :friended_by, class_name: "Friend"  , :foreign_key => "friend_id"
   has_many  :friended_by_profiles, :through => :friended_by, source: :profile


   def mutual_friends
    friended_by.where('profile_id in (?)', friend_profile_ids)
   end

   def mutual_friend_profiles
    Profile.where('id in (?)', mutual_friends.pluck(:profile_id))
   end

   def requesting_friends
    friended_by.where('profile_id not in (?)', friend_profile_ids)
   end

  def requesting_friend_profiles
    Profile.where('id in (?)', requesting_friends.pluck(:profile_id))
  end

  def pending_friends
   friends.where('friend_id not in (?)', friended_by_profile_ids)
  end

  def pending_friend_profiles
     Profile.where('id in (?)', pending_friends.pluck(:friend_id))
  end
 end

感谢您的帮助!

1 个答案:

答案 0 :(得分:0)

我如何让它工作的解决方案: 删除共同的朋友

def mutual_friends
    inverse_friends.where('user_id in (?)', friend_user_ids)
end

将友谊控制器中的逻辑更改为:

#all mutual friendships with marked true in both relations
@user = current_user
@user_friends = @user.friendships.select{ |friendship| friendship.marked == true }
@user_inverse_friends = @user.inverse_friendships.select{ |friendship| friendship.marked == true }
@friendships = @user_friends.select{ |friend| @user_inverse_friends.map{|inverse_friend| inverse_friend.user_id}.include? friend.friend_id}

#friendships with false mark
@skiped_friendships = current_user.friendships.select { |friendship| friendship.marked != true}

#marked friend: users friendships without the mutual valid friendships
@marked_friends = current_user.friendships - @friendships - @skiped_friendships