Rails5、重构,多条件最优查询
Rails 5, refactoring, optimal query for multiple conditions
我有一个 rails 应用程序,我最近更新到 Rails 5. 我有一个看起来像这样的数据模型(简化):Users
可以有很多 Apps
,Users
也可以是多个Team
的一个Member
,每个Team
也可以有多个App
。在我的 App
s 索引视图/控制器中,我想列出他/她创建的所有用户应用程序,我还想列出属于 Team
s 的所有应用程序,Users
是一个Member
的。
我感觉有一种比我当前的实现更好、更高效的方法(可能是 Rails 5 中的新内容)。这是我当前的实现方式:
apps = []
# First get all the team apps where the user is a member, but haven't created the app.
current_or_guest_user.teams.each do |team|
team.apps.each do |app|
unless app.user.eql?(current_or_guest_user)
apps << app
end
end
end
# ... then get all the apps that the user have created.
current_or_guest_user.apps.each do |app|
unless apps.include?(app)
apps << app
end
end
# Return the apps.
@apps = apps
那么,有没有一种更简洁、更优化的方式来完成我正在做的事情?那看起来怎么样?
编辑
这是我的活动模型关联的样子:
# App.rb
belongs_to :user
belongs_to :team
# User.rb
has_many :apps, dependent: :destroy
has_many :teams
has_many :teams, through: :members
# Team.rb
has_many :apps, dependent: :destroy
编辑 2
我想知道 Rails 5 方法 #or
(https://github.com/rails/rails/pull/16052) 是否可以用于此用例,例如:
current_user.apps.or([...]])
# [...] = In not exactly sure what to put here in that case.
我认为下面的代码应该更简洁地完成这个:
# using a shorter variable name
user = current_or_guest_user
# does the same thing as your first loop over teams
set1 = user.teams.includes(:apps).where("apps.user_id = ?", user.id).map(&:apps)
# does the same thing as the second loop
# (no need to check for duplicates here)
set2 = user.apps
# combine the two queries without adding duplicates
return set1 | set2
抱歉,如果这不能开箱即用,我还没有测试过。
这里有几个概念:
includes
将通过关联 "preload" 记录。这通过单个查询获取所有关联记录,而不是触发单独的 SQL 查询来获取每个记录。
where("apps.user_id = ?", user.id)
根据关联记录的 user_id 过滤查询。这里的?
是一个被user.id
代替的变量。
我有一个 rails 应用程序,我最近更新到 Rails 5. 我有一个看起来像这样的数据模型(简化):Users
可以有很多 Apps
,Users
也可以是多个Team
的一个Member
,每个Team
也可以有多个App
。在我的 App
s 索引视图/控制器中,我想列出他/她创建的所有用户应用程序,我还想列出属于 Team
s 的所有应用程序,Users
是一个Member
的。
我感觉有一种比我当前的实现更好、更高效的方法(可能是 Rails 5 中的新内容)。这是我当前的实现方式:
apps = []
# First get all the team apps where the user is a member, but haven't created the app.
current_or_guest_user.teams.each do |team|
team.apps.each do |app|
unless app.user.eql?(current_or_guest_user)
apps << app
end
end
end
# ... then get all the apps that the user have created.
current_or_guest_user.apps.each do |app|
unless apps.include?(app)
apps << app
end
end
# Return the apps.
@apps = apps
那么,有没有一种更简洁、更优化的方式来完成我正在做的事情?那看起来怎么样?
编辑
这是我的活动模型关联的样子:
# App.rb
belongs_to :user
belongs_to :team
# User.rb
has_many :apps, dependent: :destroy
has_many :teams
has_many :teams, through: :members
# Team.rb
has_many :apps, dependent: :destroy
编辑 2
我想知道 Rails 5 方法 #or
(https://github.com/rails/rails/pull/16052) 是否可以用于此用例,例如:
current_user.apps.or([...]])
# [...] = In not exactly sure what to put here in that case.
我认为下面的代码应该更简洁地完成这个:
# using a shorter variable name
user = current_or_guest_user
# does the same thing as your first loop over teams
set1 = user.teams.includes(:apps).where("apps.user_id = ?", user.id).map(&:apps)
# does the same thing as the second loop
# (no need to check for duplicates here)
set2 = user.apps
# combine the two queries without adding duplicates
return set1 | set2
抱歉,如果这不能开箱即用,我还没有测试过。
这里有几个概念:
includes
将通过关联 "preload" 记录。这通过单个查询获取所有关联记录,而不是触发单独的 SQL 查询来获取每个记录。where("apps.user_id = ?", user.id)
根据关联记录的 user_id 过滤查询。这里的?
是一个被user.id
代替的变量。