Flamboyant loading in many ways in Rails 4
This is a simplified example of what I am trying to achieve.
- Has_many Catalog Collection: via catalog_collections
- Has_many Collection product: via collection_products
I have this circuit:
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
I am looking forward to loading associations using .includes (), this only spawns the queries I need.
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">]>
The problem is I have a problem with an n + 1 request, this somehow spawns more requests.
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">]>
I need to somehow request these functions using the associations we have already loaded, rather than finding everything from scratch that they are currently doing.
+3
source to share
1 answer
Extending the suggestion https://stackoverflow.com/users/419017/damien-roche in his comment
products = cats.first.collections.first.products
product_1 = products.to_a.select { |p| p.name == 'product1' }
Pretty likely to_a
plus is O (n) select
faster than an additional SQL query. If not, then you are already optimal.
+1
source to share