繁体   English   中英

导轨 - 包括? 带有 object 或 object id

[英]Rails - include? with object or object id

我有一个名为 Company 的 object,它有一个名为 owner 的方法,它返回一个 User 对象数组。 我想通过检查当前用户是否在所有者的数组中来过滤这些公司。 首先我做了这样的事情(有效):

Company.select { |c| c.owners.include?(current_user) }

但是,我认为更有效的检查是只比较 id 而不是整个对象:

Company.select { |c| c.owners.map(&:id)include?(current_user.id) }

谁能帮我理解这两个选项之间是否有区别?

两者之间真的没有区别。 如果您的 Company 表很大,两者都非常低效并且将无法工作。 他们将所有公司记录加载到 memory 中,并进一步调用c.owners对每条记录触发附加查询。 这称为 N+1 查询。 您可以使用Company.all.includes(:owners)解决 N+1 部分,但您仍然会遇到将所有内容加载到 memory 的问题。

由于您尚未共享 model 定义(特别是如何定义Company#owners关联),因此很难为您提供准确的代码。 但我假设您有一个 CompanysOwners 连接表。 在这种情况下,我会推荐以下代码:

companies = CompanysOwners.where(owner: current_user).includes(:company).map(&:company)

这只会触发一个查询,并且不会将更多的记录加载到 memory 中。 你可以去掉includes(:company)部分,它仍然可以工作,但速度会慢一些。

如果您的Company#owners关联的定义与此不同,请随时发表评论,我可以向您展示如何根据您的需要进行修改。

顺便说一句,为了更直接地解决您的原始问题......在幕后, c.owners.include?(current_user)使用==来比较记录。 ActiveRecord::Core#==正在比较引擎盖下的 ID。 您可以在此处查看源代码以证明这一点: https://api.rubyonrails.org/v6.1.3.1/classes/ActiveRecord/Core.html#method-i-3D-3D 所以他们真的在做同样的事情。

我有一个名为 Company 的 object,它有一个名为 owner 的方法,它返回一个 User 对象数组。

那么这就是你的问题。

这应该通过设置间接关联在 model 层上真正处理:

class User < ApplicationRecord
  has_many :company_ownerships, 
     foreign_key: :owner_id,
     inverse_of: :owner
  has_many :companies, through: :company_ownerships
end

class CompanyOwnership < ApplicationRecord
  belongs_to :owner, class_name: 'User'
  belongs_to :company
end

class Company < ApplicationRecord
  has_many :company_ownerships
  has_many :owners, through: :company_ownerships
end

这将让您通过简单地调用来获得用户拥有的公司:

current_user.companies

这大大提高了效率,因为它执行单个查询来获取关联的记录,而不是将整个表加载到 memory 中。 这将返回ActiveRecord::Relation object 而不是允许您在需要时添加其他范围的数组:

current_user.companies.where(bankrupt: false)

从代码设计的角度来看,它也是优越的,因为业务逻辑被封装在您的模型中,而不是到处泄露实现细节。

它还可以让您include / preload / eager_load以避免 n+1 查询:

@users = User.include(:companies)
             .all

@users.each do |user|
  # this loads all the companies in one single query instead 
  # of one query per user
  user.companies.each do |company|
    puts company.name  
  end
end

如果您出于某种原因需要检查两条记录是否相关,您需要使用 join 和 where 子句:

@companies = Company.joins(:owners)
                    .where(users: { id: current_user.id })

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM