我有一些模型,如Service
,Payment
和Manager
class Service < ApplicationRecord
validates :name, presence: true
has_and_belongs_to_many :tickets
has_many :payments
end
class Payment < ApplicationRecord
belongs_to :manager
belongs_to :service
validates :value, :manager_id, :service_id, presence: true
end
class Manager < ApplicationRecord
validates :name, presence: true
end
Service
可以有多个Payment
s,每个Payment
有单个Manager
。
我希望使用所有嵌套关联作为哈希来获取整个数据,我可以重新格式化(或映射)并发送到客户端。我坚持查询:
Service.includes(payments: :manager).references(:payments, :managers)
因为它使用延迟加载,我需要做类似的事情:
services.first.payments.first.manager
获取数据,这是非最佳的。
是否可以获得所有嵌套关联的所有数据?
我做了这样的计算:
services = Service.includes(payments: :manager)
.references(:payments, :managers)
result = []
services.each do |service|
service.payments.each do |payment|
manager_name = payment[:manager][:name]
value = payment[:value]
service[manager_name] = value
end
result.push(service)
end
并在NoMethodError (undefined method '[]' for nil:NilClass):
线上得到一个错误manager_name = payment[:manager][:name]
。
您可以定义一个额外的has_many通过关系,它可以为您提供所需的内容。
has_many:经理,通过::付款
class Service < ApplicationRecord
validates :name, presence: true
has_and_belongs_to_many :tickets
has_many :payments
has_many :managers, through: :payments
end
class Payment < ApplicationRecord
belongs_to :manager
belongs_to :service
validates :value, :manager_id, :service_id, presence: true
end
class Manager < ApplicationRecord
validates :name, presence: true
end
我不明白你的需要%100但是我想出来并且想法如下。请检讨一下。
payments = Payment.joins(:service).select(:service_id, :manager_id, :value).
group_by(&:service_id)
managers = Manager.where('id IN(SELECT manager_id FROM payments)').
select(:name,:id)
group_by(&:id).transform_values{ |value| value.first }
result = []
ResultClass = Struct.new(:service, :manager, :payment_value)
Service.all.each do |service|
payment = payments[service.id]
manager = managers[payment.manager_id]
result << ResultClass.new(service, manager, payment.value)
end
有了这段代码,我们实际上正在尝试在引擎盖下完成rails includes
机制。我们在这里做了什么?
ResultClass
创建了Struct
来存储像service
和manager
这样的对象的引用payments
和members
作为哈希,因此快速查找并且没有时间复杂性。总体而言,此迭代的时间复杂度为O(n)如果您有任何疑问,请随时发表评论。
经过多次尝试,我终于找到了一些我做过的拼写错误并且我想要使用这样的代码
services = Service.eager_load(payments: :manager)
services.reduce([]) do |acc, service|
service_rec = {
id: service[:id],
name: service[:name],
surgery: service[:surgery]
}
service.payments.each do |payment|
manager_name = payment.manager[:name]
value = payment[:value]
service_rec[manager_name] = value
end
acc.push(service_rec)
end
和它产生的SQL查询
SQL (0.2ms) SELECT "services"."id" AS t0_r0, "services"."code" AS t0_r1, "services"."name" AS t0_r2, "services"."surgery" AS t0_r3, "services"."enabled" AS t0_r4, "services"."created_at" AS t0_r5, "services"."updated_at" AS t0_r6, "payments"."id" AS t1_r0, "payments"."service_id" AS t1_r1, "payments"."manager_id" AS t1_r2, "payments"."value" AS t1_r3, "payments"."created_at" AS t1_r4, "payments"."updated_at" AS t1_r5, "managers"."id" AS t2_r0, "managers"."name" AS t2_r1, "managers"."enabled" AS t2_r2, "managers"."created_at" AS t2_r3, "managers"."updated_at" AS t2_r4 FROM "services" LEFT OUTER JOIN "payments" ON "payments"."service_id" = "services"."id" LEFT OUTER JOIN "managers" ON "managers"."id" = "payments"."manager_id"
有趣的是,使用includes
而不是eager_load
会产生三个查询
Service Load (0.1ms) SELECT "services".* FROM "services"
↳ app/services/payments_service.rb:6
Payment Load (0.1ms) SELECT "payments".* FROM "payments" WHERE "payments"."service_id" IN (?, ?, ?, ?, ?, ?, ?, ?) [["service_id", 1], ["service_id", 2], ["service_id", 3], ["service_id", 4], ["service_id", 5], ["service_id", 6], ["service_id", 7], ["service_id", 8]]
↳ app/services/payments_service.rb:6
Manager Load (0.1ms) SELECT "managers".* FROM "managers" WHERE "managers"."id" IN (?, ?, ?, ?) [["id", 1], ["id", 2], ["id", 3], ["id", 4]]
我们也可以使用Service.includes(payments: :manager).references(:payments, :managers)
并获得与eager_load
相同的查询但是输入的时间更长))
谢谢大家的参与!有没有人对eager_load
或代码优化提案有其他意见?