我有一个
User
has_many :friends, through: :friendships
has_many :blocked_friends, through: blocked_users
通过网络方法,我试图让当前用户分为两个级别的朋友。 问题是第一次运行此方法会为每个第一级朋友查找提供个人查询。在我第二次运行它之后它应该像它应该的那样(给我6个查询的常量)。这有什么问题?
def network
User.includes(friends: [:friends]).find(self.id)
all_friends = []
all_friends << self # add current user
all_friends += self.friends # add user's friends
self.friends.each {|i| all_friends += i.friends} # add user's friend friends
all_friends.uniq - self.blocked_friends # take away blocked friends
end
答案 0 :(得分:0)
您的问题是您对数据库进行缓存,而不是在Rails应用程序上缓存。
让我们来看看您的network
方法:
def network
User.includes(friends: [:friends]).find(self.id)
# Will do some queries like your User selection,
# its friend selection, and sub-friends selection.
# SQL queries are done, but the result is sent to nowhere.
all_friends = []
all_friends << self # add current user
all_friends += self.friends # add user's friends
# Now, you request AGAIN the friends of the user, but the
# variable is not the same ! self is not equal to
# User.find(self.id). It represents the same object, but
# not exactly the same object_id in your Rails application.
# In other words,
# self == User.find(self.id) => true
# self === User.find(self.id) => false !
# [ ... ]
end
为了提高效率,你应该这样做:
def network
self_with_eager_loading = User.includes(:friends => :friends).find(self.id)
all_friends = []
all_friends << self_with_eager_loading
all_friends += self_with_eager_loading.friends
self_with_eager_loading.friends.each {|i| all_friends += i.friends}
[ ... ]
end
在此示例中,未使用变量self
(不包含预先加载)。仅使用self_with_eager_loading
。
只是一点点说明:你不应该这样做。在实例类方法中使用User.find(self).id
很糟糕。为了漂亮,您的代码应如下所示:
def network
all_friends = []
all_friends << self
all_friends += self.friends.includes(:friends)
self.friends.each {|i| all_friends += i.friends}
[ ... ]
end
当您执行此操作并且退出此方法时,您的变量self
始终包含friends
和子friends
,您可以在任何地方使用它。