
[英]How return true if the array of object doesn't include object with this 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.