【问题标题】:Eager Loading in Many to Many relationship in Rails 4Rails 4中多对多关系的急切加载
【发布时间】:2014-08-24 06:05:58
【问题描述】:

这是我要达到的目标的简化示例。

  • 目录 has_many 集合:通过 catalog_collections
  • 产品 has_many 收藏:通过 collection_products

我有这个架构:

class Product < ActiveRecord::Base
   has_many :collection_products, :foreign_key => :product_id, :dependent => :destroy, :inverse_of => :product
   has_many :collections, :through => :collection_products, :foreign_key => :collection_id, :inverse_of => :products
end

class Catalog < ActiveRecord::Base
  has_many :catalog_collections, :foreign_key => :catalog_id, :dependent => :destroy, :inverse_of => :catalog
  has_many :collections, :through => :catalog_collections, :foreign_key => :collection_id, :inverse_of => :catalogs
end

class Collection < ActiveRecord::Base
  has_many :collection_products, :foreign_key => :collection_id, :dependent => :destroy, :inverse_of => :collection
  has_many :products, :through => :collection_products, :foreign_key => :product_id, :inverse_of => :collections

  has_many :catalog_collections, :foreign_key => :collection_id, :dependent => :destroy, :inverse_of => :collection
  has_many :catalogs, :through => :catalog_collections, :inverse_of => :collections
end

class CollectionProduct < ActiveRecord::Base
  belongs_to :collection, inverse_of: :collection_products
  belongs_to :product, inverse_of: :collection_products
end

class CatalogCollection < ActiveRecord::Base
  belongs_to :catalog, inverse_of: :catalog_collections
  belongs_to :collection, inverse_of: :catalog_collections
end

我渴望使用 .includes() 加载关联,这只会生成我需要的查询。

cats = Catalog.includes(collections: :products)
  Catalog Load (0.6ms)  SELECT "catalogs".* FROM "catalogs"
  CatalogCollection Load (0.4ms)  SELECT "catalog_collections".* FROM "catalog_collections"  WHERE "catalog_collections"."catalog_id" IN (1)
  Collection Load (0.5ms)  SELECT "collections".* FROM "collections"  WHERE "collections"."id" IN (1)
  CollectionProduct Load (0.3ms)  SELECT "collection_products".* FROM "collection_products"  WHERE "collection_products"."collection_id" IN (1)
  Product Load (0.3ms)  SELECT "products".* FROM "products"  WHERE "products"."id" IN (1)
 => #<ActiveRecord::Relation [#<Catalog id: 1, name: "catalog1", created_at: "2014-08-20 08:59:05", updated_at: "2014-08-20 08:59:05">]>

cats.first.collections.first.products
 => #<ActiveRecord::Associations::CollectionProxy [#<Product id: 1, name: "product1", created_at: "2014-08-20 08:58:46", updated_at: "2014-08-20 08:58:46">]>

问题是我有 n+1 个查询问题,这不知何故会产生更多查询。

cats.first.collections.first.products.where(name: 'product1')
  Product Load (0.4ms)  SELECT "products".* FROM "products" INNER JOIN "collection_products" ON "products"."id" = "collection_products"."product_id" WHERE "collection_products"."collection_id" = ? AND "products"."name" = 'product1'  [["collection_id", 1]]
 => #<ActiveRecord::AssociationRelation [#<Product id: 1, name: "product1", created_at: "2014-08-20 08:58:46", updated_at: "2014-08-20 08:58:46">]>

我需要以某种方式让这些函数使用我们已经加载的关联进行查询,而不是从头开始查找它们当前正在执行的所有内容。

【问题讨论】:

  • 您的最后一个查询正在执行另一个查询,因为您在products 上有一个where 子句,但是当您第一次执行includes(collections: :products) 时,products 查询已经在没有它的情况下运行。将您的 products 视为一个数组,并改用 Ruby select 方法。

标签: ruby-on-rails ruby activerecord


【解决方案1】:

在他的评论中扩展https://stackoverflow.com/users/419017/damien-roche的建议

products = cats.first.collections.first.products
product_1 = products.to_a.select { |p| p.name == 'product1' }

很可能to_a 加上 O(n) select 比额外的 SQL 查询更快。如果不是,那么你已经是最佳状态了。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-05-09
    • 2021-12-01
    • 1970-01-01
    • 2014-06-21
    • 2016-04-25
    • 2016-06-06
    • 1970-01-01
    • 2013-10-07
    相关资源
    最近更新 更多