I''m a bit unclear what''s going on with relations. I''m hoping someone can help explain it to me. Lets say we have a model with orders and line_items such that an order has many line_items. Thus: class Order has_may :line_items end class LineItem belongs_to :order end When I have an order and call line_items what''s going on? e.g. Order order = Order.first order.line_items.each {|li| puts li } I thought that was basically an alias for: Order order = Order.first LineItem.where(:order_id => order.id).each {|li| puts li } but, this isn''t consistent with batching. So if we do Order order = Order.first # this doesn''t appear to work, it''s not batching, just finding all of the line items and iterating order.line_items.find_each {|li| puts li } # this seems to work as expected (i.e. it batches the retrieval from the db) LineItem.where(:order_id => order.id).find_each {|li| puts li } Can anyone explain to me why these to are different? -- You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To view this discussion on the web visit https://groups.google.com/d/msg/rubyonrails-talk/-/kb5hRwQtHvwJ. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en.
John H. wrote in post #1063205:> I''m a bit unclear what''s going on with relations. I''m hoping someone can > help explain it to me. > > Lets say we have a model with orders and line_items such that an order > has > many line_items. Thus: > > class Order > has_may :line_items > end > class LineItem > belongs_to :order > end > > When I have an order and call line_items what''s going on? e.g. > > Order order = Order.first > order.line_items.each {|li| puts li } > > I thought that was basically an alias for: > > Order order = Order.first > LineItem.where(:order_id => order.id).each {|li| puts li }This is an over-simplification. Let me illustrate by example: 1.9.3p194 :010 > order = Order.first Order Load (0.3ms) SELECT "orders".* FROM "orders" LIMIT 1 => #<Order id: 1, name: "First item", description: "First one", price: nil, created_at: "2012-06-06 01:20:03", updated_at: "2012-06-06 01:20:03"> Notice first that selecting the order does not touch the line_items table at all. At this point the order.line_items method is represented by an ActiveRecord::Relation object. Accessing this relation object will load (fire) the relation and populate the array. Now let''s take look at the line_items: 1.9.3p194 :002 > order.line_items LineItem Load (0.2ms) SELECT "line_items".* FROM "line_items" WHERE "line_items"."order_id" = 1 => [#<LineItem id: 1, order_id: 1, name: "Line 1", quantity: 5, created_at: "2012-06-06 01:21:24", updated_at: "2012-06-06 01:21:24">] 1.9.3p194 :003 > order.line_items => [#<LineItem id: 1, order_id: 1, name: "Line 1", quantity: 5, created_at: "2012-06-06 01:21:24", updated_at: "2012-06-06 01:21:24">] Notice the first time we access line_items ActiveRecord will load the association by running a generated SQL statement. Subsequent calls do not reissue the SQL statement. Let''s take a look at what is reported as the class for order.line_items: 1.9.3p194 :003 > puts order.line_items.class Array This appears to be a typical Array object, but it''s really more complicated than that. What you''re seeing is not a simple Array object, but rather an Array that has been extended with some Rails magic. Let''s take a look that the ancestry of that resulting "Array" object: 1.9.3p194 :004 > puts order.line_items.class.ancestors Array JSON::Ext::Generator::GeneratorMethods::Array Enumerable Object PP::ObjectMixin JSON::Ext::Generator::GeneratorMethods::Object ActiveSupport::Dependencies::Loadable Kernel BasicObject Here''s the ancestry of a plain old Ruby Array: # Ruby (No Rails) $ irb 1.9.3p194 :001 > Array.ancestors => [Array, Enumerable, Object, Kernel, BasicObject] In fact all Array objects in rails have the additional magic added to them: # Ruby on Rails $ rails console Loading development environment (Rails 3.2.3) 1.9.3p194 :001 > puts Array.ancestors Array JSON::Ext::Generator::GeneratorMethods::Array Enumerable Object PP::ObjectMixin JSON::Ext::Generator::GeneratorMethods::Object ActiveSupport::Dependencies::Loadable Kernel BasicObject> but, this isn''t consistent with batching. > > So if we do > > Order order = Order.first > > # this doesn''t appear to work, it''s not batching, just finding all of > the > line items and iterating > order.line_items.find_each {|li| puts li }Hopefully what I explained above will clear up what''s happening here. That very first access of order.line_items is causing that relation object to fire and load the objects. What you probably want is the find_in_batches method provided by ActiveRecord: http://api.rubyonrails.org/classes/ActiveRecord/Batches.html#method-i-find_in_batches -- Posted via http://www.ruby-forum.com/. -- You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en.
Thanks. (the find_each I referenced in my question is (basically) the same as find_in_batches) The question is really why: order.line_items != LineItem.where(:order_id => order.id) So, I think what you''re saying is that order.line_items returns an array, not a relation. On Tuesday, June 5, 2012 7:02:25 PM UTC-7, Ruby-Forum.com User wrote:> > John H. wrote in post #1063205: > > I''m a bit unclear what''s going on with relations. I''m hoping someone can > > help explain it to me. > > > > Lets say we have a model with orders and line_items such that an order > > has > > many line_items. Thus: > > > > class Order > > has_may :line_items > > end > > class LineItem > > belongs_to :order > > end > > > > When I have an order and call line_items what''s going on? e.g. > > > > Order order = Order.first > > order.line_items.each {|li| puts li } > > > > I thought that was basically an alias for: > > > > Order order = Order.first > > LineItem.where(:order_id => order.id).each {|li| puts li } > > This is an over-simplification. Let me illustrate by example: > > 1.9.3p194 :010 > order = Order.first > Order Load (0.3ms) SELECT "orders".* FROM "orders" LIMIT 1 > => #<Order id: 1, name: "First item", description: "First one", price: > nil, created_at: "2012-06-06 01:20:03", updated_at: "2012-06-06 > 01:20:03"> > > Notice first that selecting the order does not touch the line_items > table at all. At this point the order.line_items method is represented > by an ActiveRecord::Relation object. Accessing this relation object will > load (fire) the relation and populate the array. > > Now let''s take look at the line_items: > > 1.9.3p194 :002 > order.line_items > LineItem Load (0.2ms) SELECT "line_items".* FROM "line_items" WHERE > "line_items"."order_id" = 1 > => [#<LineItem id: 1, order_id: 1, name: "Line 1", quantity: 5, > created_at: "2012-06-06 01:21:24", updated_at: "2012-06-06 01:21:24">] > 1.9.3p194 :003 > order.line_items > => [#<LineItem id: 1, order_id: 1, name: "Line 1", quantity: 5, > created_at: "2012-06-06 01:21:24", updated_at: "2012-06-06 01:21:24">] > > Notice the first time we access line_items ActiveRecord will load the > association by running a generated SQL statement. Subsequent calls do > not reissue the SQL statement. > > Let''s take a look at what is reported as the class for order.line_items: > > 1.9.3p194 :003 > puts order.line_items.class > Array > > This appears to be a typical Array object, but it''s really more > complicated than that. What you''re seeing is not a simple Array object, > but rather an Array that has been extended with some Rails magic. Let''s > take a look that the ancestry of that resulting "Array" object: > > 1.9.3p194 :004 > puts order.line_items.class.ancestors > Array > JSON::Ext::Generator::GeneratorMethods::Array > Enumerable > Object > PP::ObjectMixin > JSON::Ext::Generator::GeneratorMethods::Object > ActiveSupport::Dependencies::Loadable > Kernel > BasicObject > > Here''s the ancestry of a plain old Ruby Array: > > # Ruby (No Rails) > $ irb > 1.9.3p194 :001 > Array.ancestors > => [Array, Enumerable, Object, Kernel, BasicObject] > > In fact all Array objects in rails have the additional magic added to > them: > > # Ruby on Rails > $ rails console > Loading development environment (Rails 3.2.3) > 1.9.3p194 :001 > puts Array.ancestors > Array > JSON::Ext::Generator::GeneratorMethods::Array > Enumerable > Object > PP::ObjectMixin > JSON::Ext::Generator::GeneratorMethods::Object > ActiveSupport::Dependencies::Loadable > Kernel > BasicObject > > > but, this isn''t consistent with batching. > > > > So if we do > > > > Order order = Order.first > > > > # this doesn''t appear to work, it''s not batching, just finding all of > > the > > line items and iterating > > order.line_items.find_each {|li| puts li } > > Hopefully what I explained above will clear up what''s happening here. > That very first access of order.line_items is causing that relation > object to fire and load the objects. > > What you probably want is the find_in_batches method provided by > ActiveRecord: > > > http://api.rubyonrails.org/classes/ActiveRecord/Batches.html#method-i-find_in_batches > > -- > Posted via http://www.ruby-forum.com/. >-- You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To view this discussion on the web visit https://groups.google.com/d/msg/rubyonrails-talk/-/Q2azabP6S_kJ. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en.
> order.line_items != LineItem.where(:order_id => order.id)Let me rephrase Robert''s words. The key difference here is, that LineItem.where(:order_id => order.id) doesn''t fire the sql and returns ActiveRecord::Relation object. While the order.line_items runs SQL and returns the Array of records. The purpose of it is that u can change the resulting SQL and do the method chaining. Try next: line_items_by_order_id = LineItem.where(:order_id => order.id) # => object of ActiveRecord::Relation class ordered_line_items_by_order_id = LineItem.where(:order_id => order.id).order(:updated_at) # => object of ActiveRecord::Relation class ordered_line_items_by_order_id_with_positive_price LineItem.where(:order_id => order.id).order(:updated_at).where("line_items.price > ?", 20) # => object of ActiveRecord::Relation class # ... and so on # where order.line_items # returns [LineItem, LineItem ...] array -- You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en.
On 8 June 2012 16:44, Max Reznichenko <max.reznichenko-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:>> order.line_items != LineItem.where(:order_id => order.id) > > Let me rephrase Robert''s words. > The key difference here is, that LineItem.where(:order_id => order.id) > doesn''t fire the sql and returns ActiveRecord::Relation object. > While the order.line_items runs SQL and returns the Array of records. > The purpose of it is that u can change the resulting SQL and do the > method chaining. > > Try next: > line_items_by_order_id = LineItem.where(:order_id => order.id) # => > object of ActiveRecord::Relation class > ordered_line_items_by_order_id = LineItem.where(:order_id => > order.id).order(:updated_at) # => object of ActiveRecord::Relation > class > ordered_line_items_by_order_id_with_positive_price > LineItem.where(:order_id => > order.id).order(:updated_at).where("line_items.price > ?", 20) # => > object of ActiveRecord::Relation class > # ... and so on > # where > order.line_items # returns [LineItem, LineItem ...] arrayIt is noteworthy that one can still do order.line_items.where( some condition ) even though order.line_items appears to be an Array. Colin -- You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en.
I think that''s what''s always tripped me up. Most ActiveRecord commands still work normally on the array. I''ve always chained them and thought I was building a relation, not getting the array. It wasn''t until I had a big set of records and discovered I couldn''t use the association in a find_each that I tripped over this. Thanks for the help. On Friday, June 8, 2012 9:02:45 AM UTC-7, Colin Law wrote:> > On 8 June 2012 16:44, Max Reznichenko > >> order.line_items != LineItem.where(:order_id => order.id) > > > > Let me rephrase Robert''s words. > > The key difference here is, that LineItem.where(:order_id => order.id) > > doesn''t fire the sql and returns ActiveRecord::Relation object. > > While the order.line_items runs SQL and returns the Array of records. > > The purpose of it is that u can change the resulting SQL and do the > > method chaining. > > > > Try next: > > line_items_by_order_id = LineItem.where(:order_id => order.id) # => > > object of ActiveRecord::Relation class > > ordered_line_items_by_order_id = LineItem.where(:order_id => > > order.id).order(:updated_at) # => object of ActiveRecord::Relation > > class > > ordered_line_items_by_order_id_with_positive_price = > > LineItem.where(:order_id => > > order.id).order(:updated_at).where("line_items.price > ?", 20) # => > > object of ActiveRecord::Relation class > > # ... and so on > > # where > > order.line_items # returns [LineItem, LineItem ...] array > > It is noteworthy that one can still do > order.line_items.where( some condition ) > even though order.line_items appears to be an Array. > > Colin >On Friday, June 8, 2012 9:02:45 AM UTC-7, Colin Law wrote:> > On 8 June 2012 16:44, Max Reznichenko > >> order.line_items != LineItem.where(:order_id => order.id) > > > > Let me rephrase Robert''s words. > > The key difference here is, that LineItem.where(:order_id => order.id) > > doesn''t fire the sql and returns ActiveRecord::Relation object. > > While the order.line_items runs SQL and returns the Array of records. > > The purpose of it is that u can change the resulting SQL and do the > > method chaining. > > > > Try next: > > line_items_by_order_id = LineItem.where(:order_id => order.id) # => > > object of ActiveRecord::Relation class > > ordered_line_items_by_order_id = LineItem.where(:order_id => > > order.id).order(:updated_at) # => object of ActiveRecord::Relation > > class > > ordered_line_items_by_order_id_with_positive_price = > > LineItem.where(:order_id => > > order.id).order(:updated_at).where("line_items.price > ?", 20) # => > > object of ActiveRecord::Relation class > > # ... and so on > > # where > > order.line_items # returns [LineItem, LineItem ...] array > > It is noteworthy that one can still do > order.line_items.where( some condition ) > even though order.line_items appears to be an Array. > > Colin >-- You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To view this discussion on the web visit https://groups.google.com/d/msg/rubyonrails-talk/-/aIxiclDbJN8J. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en.