Ror lab. season 2
    - the 10th round -


   Active Record
Query Interface (1)

     November 17th, 2012

       Hyoseong Choi
ActiveRecord
• No more SQL statements      MySQL
                              PostgreSQL
  : select * from tables      SQLite
                              ...
                                   ORM
Action Sequences
                                  Active
    1. SQL query                  Record

    2. Result set
                                  Finder
    3. Ruby object               Methods
    4. (after_find callback)
Finder Methods of
  ActiveRecord
1.where
2.select
3.group
4.order
5.reorder
6.reverse_order
7.limit
8.offset
9.joins
10.includes
11.lock
12.readonly
13.from
14.having

 ActiveRecord::Relation
Retrieving
    A Single Object
• find :ActiveRecord::RecordNotFound
• first :nil
• last :nil
• first! : ActiveRecord::RecordNotFound
• last! : ActiveRecord::RecordNotFound
Retrieving
    A Single Object
                            - find -


  # Find the client with primary key (id) 10.

  client = Client.find(10)




SELECT * FROM clients WHERE (clients.id = 10)

ActiveRecord::RecordNotFound exception
Retrieving
    A Single Object
                            - first -


  client = Client.first

  # => #<Client id: 1, first_name: "Lifo">



SELECT * FROM clients LIMIT 1


nil if no matching record is found
Retrieving
    A Single Object
                             - last -


  client = Client.last

  # => #<Client id: 221, first_name: "Russel">



SELECT * FROM clients ORDER BY clients.id DESC LIMIT 1


nil if no matching record is found
Retrieving
    A Single Object
                          - first! -


  client = Client.first!

  # => #<Client id: 1, first_name: "Lifo">



SELECT * FROM clients LIMIT 1


RecordNotFound if no matching record
Retrieving
    A Single Object
                           - last! -


  client = Client.last!

  # => #<Client id: 221, first_name: "Russel">



SELECT * FROM clients ORDER BY clients.id DESC LIMIT 1


RecordNotFound if no matching record
Retrieving
  Multiple Objects
• Using multiple primary keys
• In batches
 • find_each :batch_size, :start,
 • find_in_batches :batch_size, :start
    + find options (except for :order, :limit)
Retrieving
  Multiple Objects
          - Using multiple primary keys -

  # Find the clients with primary keys 1 and 10.
  client = Client.find([1, 10])
  # Or even Client.find(1, 10)
  # => [#<Client id: 1, first_name: "Lifo">, #<Client id: 10,




SELECT * FROM clients WHERE (clients.id IN (1,10))

ActiveRecord::RecordNotFound exception
Retrieving
       Multiple Objects
                            - in Batches -
                  to iterate over a large set of records

• find_each : each record to the block individually as a model
• find_in_batches : the entire batch to the block as an array of models
       # This is very inefficient when the users table has thousands
       of rows.
       User.all.each do |user|
         NewsLetter.weekly_deliver(user)

                                                OK to 1,000
Retrieving
Multiple Objects
             - in Batches : find_each -

User.find_each do |user|
  NewsLetter.weekly_deliver(user)
end

User.find_each(:batch_size => 5000) do |user|
  NewsLetter.weekly_deliver(user)
end

User.find_each(:start => 2000, :batch_size => 5000) do |
user|
  NewsLetter.weekly_deliver(user)
Retrieving
   Multiple Objects
                - in Batches : find_each -

  Article.find_each { |a| ... } # => iterate over all articles, in
  chunks of 1000 (the default)

  Article.find_each(:conditions => { :published =>
  true }, :batch_size => 100 ) { |a| ... }
    # iterate over published articles in chunks of 100




http://archives.ryandaigle.com/articles/2009/2/23/what-s-
new-in-edge-rails-batched-find
Retrieving
     Multiple Objects
              - in Batches : find_in_batches -

    # Give add_invoices an array of 1000 invoices at a time
    Invoice.find_in_batches(:include => :invoice_lines) do |
    invoices|
      export.add_invoices(invoices)
    end



options :
  • :batch_size & :start
  • options of find method (except :order and :limit)
Retrieving
   Multiple Objects
              - in Batches : find_in_batches -

Article.find_in_batches { |articles| articles.each { |a| ... } } # => articles
is array of size 1000
Article.find_in_batches(:batch_size => 100 ) { |articles| articles.each { |
a| ... } }
  # iterate over all articles in chunks of 100

class Article < ActiveRecord::Base
 scope :published, :conditions => { :published => true }
end

Article.published.find_in_batches(:batch_size => 100 ) { |articles| ... }
 # iterate over published articles in chunks of 100
Conditions
               - where -


• String conditions
• Array conditions
• Hash conditions
String Conditions


 Client.where("orders_count = ‘2’")
Array Conditions
Client.where("orders_count = ?", params[:orders])


Client.where("orders_count = ? AND locked = ?",
        params[:orders], false)


Client.where("orders_count = #{params[:orders]}")
Array Conditions
Client.where("orders_count = ?", params[:orders])


Client.where("orders_count = ? AND locked = ?",
        params[:orders], false)



                                           X
Client.where("orders_count = #{params[:orders]}")


                          hacking!!! by SQL injection
 http://guides.rubyonrails.org/security.html#sql-injection
Array Conditions
 - Placeholder conditions using symbols -

Client.where("created_at >= :start_date AND created_at
<= :end_date", {:start_date =>
params[:start_date], :end_date => params[:end_date]})
Array Conditions
                 - Range conditions -


  Client.where(:created_at => (params[:start_date].to_date)..
  (params[:end_date].to_date))




SELECT "clients".* FROM "clients" WHERE
("clients"."created_at" BETWEEN '2010-09-29' AND
'2010-11-30')
Hash Conditions
              - Equality conditions -


Client.where(:locked => true)
Hash Conditions
                 - Range conditions -


  Client.where(:created_at => (Time.now.midnight -
  1.day)..Time.now.midnight)




SELECT * FROM clients WHERE (clients.created_at BETWEEN
'2008-12-21 00:00:00' AND '2008-12-22 00:00:00')
Hash Conditions
                  - Subset conditions -


  Client.where(:orders_count => [1,3,5])




SELECT * FROM clients WHERE (clients.orders_count IN (1,3,5))
Ordering

Client.order("created_at")

Client.order("created_at DESC")
# OR
Client.order("created_at ASC")
Selecting
                                                                         ?
If the select method is used, all the returning objects will be read only.



     Client.select("viewable_by, locked")
       SELECT viewable_by, locked FROM clients
     ActiveModel::MissingAttributeError: missing attribute: <attribute>



     Client.select(:name).uniq
       SELECT DISTINCT name FROM clients


     query = Client.select(:name).uniq
     # => Returns unique names
     query.uniq(false)
     # => Returns all names, even if there are duplicates
Limit & Offset
Client.limit(5)

   SELECT * FROM clients LIMIT 5

Client.limit(5).offset(30)

   SELECT * FROM clients LIMIT 5 OFFSET 30
Group
  Order.select(
  "date(created_at) as ordered_date, sum(price) as total_price")
  .group("date(created_at)")




SQL
  SELECT date(created_at) as ordered_date, sum(price) as
  total_price FROM orders GROUP BY date(created_at)
Having
  Order.select(
  "date(created_at) as ordered_date, sum(price) as total_price")
  .group("date(created_at)")
  .having("sum(price) > ?", 100)




SQL
  SELECT date(created_at) as ordered_date, sum(price) as
  total_price FROM orders GROUP BY date(created_at) HAVING
  sum(price) > 100
Overriding
       Conditions
• except
• only
• reorder
• reverse_order
Overriding
                  Conditions
                                 - except -

Post.where('id > 10').limit(20).order('id asc').except(:order)



             SELECT * FROM posts WHERE id > 10 LIMIT 20
Overriding
                       Conditions
                                        - only -

Post.where('id > 10').limit(20).order('id desc').only(:order, :where)



             SELECT * FROM posts WHERE id > 10 ORDER BY id DESC
Overriding
          Conditions
                      - reorder -
class Post < ActiveRecord::Base
  ..
  ..
  has_many :comments, :order => 'posted_at DESC'
end
 



SELECT * FROM posts WHERE id = 10 ORDER BY posted_at DESC

SELECT * FROM posts WHERE id = 10 ORDER BY name
Overriding
                 Conditions
                          - reverse_order -

  Client.where("orders_count > 10").order(:name).reverse_order


SELECT * FROM clients WHERE orders_count > 10 ORDER BY name DESC


  Client.where("orders_count > 10").reverse_order



SELECT * FROM clients WHERE orders_count > 10 ORDER BY clients.id DESC
Readonly Objects
client = Client.readonly.first
client.visits += 1
client.save




     ActiveRecord::ReadOnlyRecord exception
Locking Records
    for Update
• To prevent “race conditions”
• To ensure “atomic updates”
• Two locking mechanisms
  ‣ Optimistic Locking : version control
  ‣ Pessimistic Locking : DB lock
Optimistic Locking
• “lock_version” in DB table (default to 0)
• set_locking_column to change column name
  c1 = Client.find(1)
  c2 = Client.find(1)
   
  c1.first_name = "Michael"
  c1.save # increments the lock_version column
   
  c2.name = "should fail"
Optimistic Locking
• To turn off,
  ActiveRecord::Base.lock_optimistically = false

• To override the name of the lock_version
  column

  class Client < ActiveRecord::Base
    set_locking_column :lock_client_column
Pessimistic Locking

• A locking mechanism by DB
• An exclusive lock on the selected rows
• Usually wrapped inside a transaction
• Two types of Lock
 ‣ FOR UPDATE (default, an exclusive lock)
 ‣ LOCK IN SHARE MODE
Pessimistic Locking
  Item.transaction do
    i = Item.lock.first
    i.name = 'Jones'
    i.save




 SQL (0.2ms)   BEGIN
 Item Load (0.3ms)   SELECT * FROM `items` LIMIT 1 FOR UPDATE
 Item Update (0.4ms)   UPDATE `items` SET `updated_at` = '2009-02-07
 18:05:56', `name` = 'Jones' WHERE `id` = 1
 SQL (0.8ms)   COMMIT
Pessimistic Locking
 Item.transaction do
   i = Item.lock("LOCK IN SHARE MODE").find(1)
   i.increment!(:views)
 end




 item = Item.first
 item.with_lock do
   # This block is called within a transaction,
   # item is already locked.
   item.increment!(:views)
Joining Tables
        - Using a String SQL Fragment -


 Client.joins('LEFT OUTER JOIN addresses ON addresses.client_id




SELECT clients.*
FROM clients
LEFT OUTER JOIN addresses
ON addresses.client_id = clients.id
Joining Tables
- Using Array/Hash of Named Associations -

              only with INNER JOIN


• a Single Association
• Multiple Associations
• Nested Associations(Single Level)
• Nested Associations(Multiple Level)
Joining Tables
           - Using Array/Hash of Named Associations -

                                        only with INNER JOIN
class Category < ActiveRecord::Base
  has_many :posts
end
 
class Post < ActiveRecord::Base       • a Single Association
  belongs_to :category
  has_many :comments
  has_many :tags
end                                       Category.joins(:posts)
 
class Comment < ActiveRecord::Base
  belongs_to :post
  has_one :guest
end                                     SELECT categories.*
                                          FROM categories
class Guest < ActiveRecord::Base          INNER JOIN posts
  belongs_to :comment
end                                       ON posts.category_id = categories.id
 
class Tag < ActiveRecord::Base
  belongs_to :post                      “return a Category object for all categories with posts”
end
Joining Tables
           - Using Array/Hash of Named Associations -

                                        only with INNER JOIN
class Category < ActiveRecord::Base
  has_many :posts
end
 
class Post < ActiveRecord::Base       • Multiple Associations
  belongs_to :category
  has_many :comments
  has_many :tags
end                                       Post.joins(:category, :comments)
 
class Comment < ActiveRecord::Base
  belongs_to :post                                                          and
  has_one :guest                        SELECT posts.* FROM posts
end
 
                                          INNER JOIN categories
class Guest < ActiveRecord::Base           ON posts.category_id = categories.id
  belongs_to :comment                     INNER JOIN comments
end                                        ON comments.post_id = posts.id
 
class Tag < ActiveRecord::Base          “return all posts that have a category and at least one comment”
  belongs_to :post
end
Joining Tables
           - Using Array/Hash of Named Associations -

                                        only with INNER JOIN
class Category < ActiveRecord::Base
  has_many :posts
end
 
class Post < ActiveRecord::Base       • Nested Associations(Single Level)
  belongs_to :category
  has_many :comments
  has_many :tags
end                                       Post.joins(:comments => :guest)
 
class Comment < ActiveRecord::Base
  belongs_to :post
                                                                            nested
  has_one :guest                        SELECT posts.* FROM posts
end
 
                                          INNER JOIN comments
class Guest < ActiveRecord::Base           ON comments.post_id = posts.id
  belongs_to :comment                     INNER JOIN guests
end                                        ON guests.comment_id = comments.id
 
class Tag < ActiveRecord::Base
  belongs_to :post
                                        “return all posts that have a comment made by a guest”
end
Joining Tables
           - Using Array/Hash of Named Associations -

                                        only with INNER JOIN
class Category < ActiveRecord::Base
  has_many :posts
end
 
class Post < ActiveRecord::Base       • Nested Associations(Multiple Level)
  belongs_to :category
  has_many :comments
  has_many :tags
end                                       Category.joins(:posts => [{:comments
 
class Comment < ActiveRecord::Base
  belongs_to :post
  has_one :guest
end
                                        SELECT categories.* FROM categories
class Guest < ActiveRecord::Base          INNER JOIN posts ON posts.category_id = categories.id
  belongs_to :comment
                                          INNER JOIN comments ON comments.post_id = posts.id
end
                                          INNER JOIN guests ON guests.id = comments.quest_id
class Tag < ActiveRecord::Base            INNER JOIN tags ON tags.post_id = posts.id
  belongs_to :post
end
Joining Tables
  - Specifying Conditions on the Joined Tables -

: using Array and String Conditions

 time_range = (Time.now.midnight - 1.day)..Time.now.midnight
 Client.joins(:orders)




: using nested Hash Conditions

 time_range = (Time.now.midnight - 1.day)..Time.now.midnight
 Client.joins(:orders)
감사합니다.

Active Record Query Interface (1), Season 2

  • 1.
    Ror lab. season2 - the 10th round - Active Record Query Interface (1) November 17th, 2012 Hyoseong Choi
  • 2.
    ActiveRecord • No moreSQL statements MySQL PostgreSQL : select * from tables SQLite ... ORM Action Sequences Active 1. SQL query Record 2. Result set Finder 3. Ruby object Methods 4. (after_find callback)
  • 3.
    Finder Methods of ActiveRecord 1.where 2.select 3.group 4.order 5.reorder 6.reverse_order 7.limit 8.offset 9.joins 10.includes 11.lock 12.readonly 13.from 14.having ActiveRecord::Relation
  • 4.
    Retrieving A Single Object • find :ActiveRecord::RecordNotFound • first :nil • last :nil • first! : ActiveRecord::RecordNotFound • last! : ActiveRecord::RecordNotFound
  • 5.
    Retrieving A Single Object - find - # Find the client with primary key (id) 10. client = Client.find(10) SELECT * FROM clients WHERE (clients.id = 10) ActiveRecord::RecordNotFound exception
  • 6.
    Retrieving A Single Object - first - client = Client.first # => #<Client id: 1, first_name: "Lifo"> SELECT * FROM clients LIMIT 1 nil if no matching record is found
  • 7.
    Retrieving A Single Object - last - client = Client.last # => #<Client id: 221, first_name: "Russel"> SELECT * FROM clients ORDER BY clients.id DESC LIMIT 1 nil if no matching record is found
  • 8.
    Retrieving A Single Object - first! - client = Client.first! # => #<Client id: 1, first_name: "Lifo"> SELECT * FROM clients LIMIT 1 RecordNotFound if no matching record
  • 9.
    Retrieving A Single Object - last! - client = Client.last! # => #<Client id: 221, first_name: "Russel"> SELECT * FROM clients ORDER BY clients.id DESC LIMIT 1 RecordNotFound if no matching record
  • 10.
    Retrieving MultipleObjects • Using multiple primary keys • In batches • find_each :batch_size, :start, • find_in_batches :batch_size, :start + find options (except for :order, :limit)
  • 11.
    Retrieving MultipleObjects - Using multiple primary keys - # Find the clients with primary keys 1 and 10. client = Client.find([1, 10]) # Or even Client.find(1, 10) # => [#<Client id: 1, first_name: "Lifo">, #<Client id: 10, SELECT * FROM clients WHERE (clients.id IN (1,10)) ActiveRecord::RecordNotFound exception
  • 12.
    Retrieving Multiple Objects - in Batches - to iterate over a large set of records • find_each : each record to the block individually as a model • find_in_batches : the entire batch to the block as an array of models # This is very inefficient when the users table has thousands of rows. User.all.each do |user|   NewsLetter.weekly_deliver(user) OK to 1,000
  • 13.
    Retrieving Multiple Objects - in Batches : find_each - User.find_each do |user|   NewsLetter.weekly_deliver(user) end User.find_each(:batch_size => 5000) do |user|   NewsLetter.weekly_deliver(user) end User.find_each(:start => 2000, :batch_size => 5000) do | user|   NewsLetter.weekly_deliver(user)
  • 14.
    Retrieving Multiple Objects - in Batches : find_each - Article.find_each { |a| ... } # => iterate over all articles, in chunks of 1000 (the default) Article.find_each(:conditions => { :published => true }, :batch_size => 100 ) { |a| ... } # iterate over published articles in chunks of 100 http://archives.ryandaigle.com/articles/2009/2/23/what-s- new-in-edge-rails-batched-find
  • 15.
    Retrieving Multiple Objects - in Batches : find_in_batches - # Give add_invoices an array of 1000 invoices at a time Invoice.find_in_batches(:include => :invoice_lines) do | invoices|   export.add_invoices(invoices) end options : • :batch_size & :start • options of find method (except :order and :limit)
  • 16.
    Retrieving Multiple Objects - in Batches : find_in_batches - Article.find_in_batches { |articles| articles.each { |a| ... } } # => articles is array of size 1000 Article.find_in_batches(:batch_size => 100 ) { |articles| articles.each { | a| ... } } # iterate over all articles in chunks of 100 class Article < ActiveRecord::Base scope :published, :conditions => { :published => true } end Article.published.find_in_batches(:batch_size => 100 ) { |articles| ... } # iterate over published articles in chunks of 100
  • 17.
    Conditions - where - • String conditions • Array conditions • Hash conditions
  • 18.
  • 19.
    Array Conditions Client.where("orders_count =?", params[:orders]) Client.where("orders_count = ? AND locked = ?", params[:orders], false) Client.where("orders_count = #{params[:orders]}")
  • 20.
    Array Conditions Client.where("orders_count =?", params[:orders]) Client.where("orders_count = ? AND locked = ?", params[:orders], false) X Client.where("orders_count = #{params[:orders]}") hacking!!! by SQL injection http://guides.rubyonrails.org/security.html#sql-injection
  • 21.
    Array Conditions -Placeholder conditions using symbols - Client.where("created_at >= :start_date AND created_at <= :end_date", {:start_date => params[:start_date], :end_date => params[:end_date]})
  • 22.
    Array Conditions - Range conditions - Client.where(:created_at => (params[:start_date].to_date).. (params[:end_date].to_date)) SELECT "clients".* FROM "clients" WHERE ("clients"."created_at" BETWEEN '2010-09-29' AND '2010-11-30')
  • 23.
    Hash Conditions - Equality conditions - Client.where(:locked => true)
  • 24.
    Hash Conditions - Range conditions - Client.where(:created_at => (Time.now.midnight - 1.day)..Time.now.midnight) SELECT * FROM clients WHERE (clients.created_at BETWEEN '2008-12-21 00:00:00' AND '2008-12-22 00:00:00')
  • 25.
    Hash Conditions - Subset conditions - Client.where(:orders_count => [1,3,5]) SELECT * FROM clients WHERE (clients.orders_count IN (1,3,5))
  • 26.
  • 27.
    Selecting ? If the select method is used, all the returning objects will be read only. Client.select("viewable_by, locked") SELECT viewable_by, locked FROM clients ActiveModel::MissingAttributeError: missing attribute: <attribute> Client.select(:name).uniq SELECT DISTINCT name FROM clients query = Client.select(:name).uniq # => Returns unique names query.uniq(false) # => Returns all names, even if there are duplicates
  • 28.
    Limit & Offset Client.limit(5) SELECT * FROM clients LIMIT 5 Client.limit(5).offset(30) SELECT * FROM clients LIMIT 5 OFFSET 30
  • 29.
    Group Order.select( "date(created_at) as ordered_date, sum(price) as total_price") .group("date(created_at)") SQL SELECT date(created_at) as ordered_date, sum(price) as total_price FROM orders GROUP BY date(created_at)
  • 30.
    Having Order.select( "date(created_at) as ordered_date, sum(price) as total_price") .group("date(created_at)") .having("sum(price) > ?", 100) SQL SELECT date(created_at) as ordered_date, sum(price) as total_price FROM orders GROUP BY date(created_at) HAVING sum(price) > 100
  • 31.
    Overriding Conditions • except • only • reorder • reverse_order
  • 32.
    Overriding Conditions - except - Post.where('id > 10').limit(20).order('id asc').except(:order) SELECT * FROM posts WHERE id > 10 LIMIT 20
  • 33.
    Overriding Conditions - only - Post.where('id > 10').limit(20).order('id desc').only(:order, :where) SELECT * FROM posts WHERE id > 10 ORDER BY id DESC
  • 34.
    Overriding Conditions - reorder - class Post < ActiveRecord::Base   ..   ..   has_many :comments, :order => 'posted_at DESC' end   SELECT * FROM posts WHERE id = 10 ORDER BY posted_at DESC SELECT * FROM posts WHERE id = 10 ORDER BY name
  • 35.
    Overriding Conditions - reverse_order - Client.where("orders_count > 10").order(:name).reverse_order SELECT * FROM clients WHERE orders_count > 10 ORDER BY name DESC Client.where("orders_count > 10").reverse_order SELECT * FROM clients WHERE orders_count > 10 ORDER BY clients.id DESC
  • 36.
    Readonly Objects client =Client.readonly.first client.visits += 1 client.save ActiveRecord::ReadOnlyRecord exception
  • 37.
    Locking Records for Update • To prevent “race conditions” • To ensure “atomic updates” • Two locking mechanisms ‣ Optimistic Locking : version control ‣ Pessimistic Locking : DB lock
  • 38.
    Optimistic Locking • “lock_version”in DB table (default to 0) • set_locking_column to change column name c1 = Client.find(1) c2 = Client.find(1)   c1.first_name = "Michael" c1.save # increments the lock_version column   c2.name = "should fail"
  • 39.
    Optimistic Locking • Toturn off, ActiveRecord::Base.lock_optimistically = false • To override the name of the lock_version column class Client < ActiveRecord::Base   set_locking_column :lock_client_column
  • 40.
    Pessimistic Locking • Alocking mechanism by DB • An exclusive lock on the selected rows • Usually wrapped inside a transaction • Two types of Lock ‣ FOR UPDATE (default, an exclusive lock) ‣ LOCK IN SHARE MODE
  • 41.
    Pessimistic Locking Item.transaction do   i = Item.lock.first   i.name = 'Jones'   i.save SQL (0.2ms)   BEGIN Item Load (0.3ms)   SELECT * FROM `items` LIMIT 1 FOR UPDATE Item Update (0.4ms)   UPDATE `items` SET `updated_at` = '2009-02-07 18:05:56', `name` = 'Jones' WHERE `id` = 1 SQL (0.8ms)   COMMIT
  • 42.
    Pessimistic Locking Item.transactiondo   i = Item.lock("LOCK IN SHARE MODE").find(1)   i.increment!(:views) end item = Item.first item.with_lock do   # This block is called within a transaction,   # item is already locked.   item.increment!(:views)
  • 43.
    Joining Tables - Using a String SQL Fragment - Client.joins('LEFT OUTER JOIN addresses ON addresses.client_id SELECT clients.* FROM clients LEFT OUTER JOIN addresses ON addresses.client_id = clients.id
  • 44.
    Joining Tables - UsingArray/Hash of Named Associations - only with INNER JOIN • a Single Association • Multiple Associations • Nested Associations(Single Level) • Nested Associations(Multiple Level)
  • 45.
    Joining Tables - Using Array/Hash of Named Associations - only with INNER JOIN class Category < ActiveRecord::Base   has_many :posts end   class Post < ActiveRecord::Base • a Single Association   belongs_to :category   has_many :comments   has_many :tags end Category.joins(:posts)   class Comment < ActiveRecord::Base   belongs_to :post   has_one :guest end SELECT categories.*   FROM categories class Guest < ActiveRecord::Base   INNER JOIN posts   belongs_to :comment end ON posts.category_id = categories.id   class Tag < ActiveRecord::Base   belongs_to :post “return a Category object for all categories with posts” end
  • 46.
    Joining Tables - Using Array/Hash of Named Associations - only with INNER JOIN class Category < ActiveRecord::Base   has_many :posts end   class Post < ActiveRecord::Base • Multiple Associations   belongs_to :category   has_many :comments   has_many :tags end Post.joins(:category, :comments)   class Comment < ActiveRecord::Base   belongs_to :post and   has_one :guest SELECT posts.* FROM posts end     INNER JOIN categories class Guest < ActiveRecord::Base ON posts.category_id = categories.id   belongs_to :comment   INNER JOIN comments end ON comments.post_id = posts.id   class Tag < ActiveRecord::Base “return all posts that have a category and at least one comment”   belongs_to :post end
  • 47.
    Joining Tables - Using Array/Hash of Named Associations - only with INNER JOIN class Category < ActiveRecord::Base   has_many :posts end   class Post < ActiveRecord::Base • Nested Associations(Single Level)   belongs_to :category   has_many :comments   has_many :tags end Post.joins(:comments => :guest)   class Comment < ActiveRecord::Base   belongs_to :post nested   has_one :guest SELECT posts.* FROM posts end     INNER JOIN comments class Guest < ActiveRecord::Base ON comments.post_id = posts.id   belongs_to :comment   INNER JOIN guests end ON guests.comment_id = comments.id   class Tag < ActiveRecord::Base   belongs_to :post “return all posts that have a comment made by a guest” end
  • 48.
    Joining Tables - Using Array/Hash of Named Associations - only with INNER JOIN class Category < ActiveRecord::Base   has_many :posts end   class Post < ActiveRecord::Base • Nested Associations(Multiple Level)   belongs_to :category   has_many :comments   has_many :tags end Category.joins(:posts => [{:comments   class Comment < ActiveRecord::Base   belongs_to :post   has_one :guest end   SELECT categories.* FROM categories class Guest < ActiveRecord::Base   INNER JOIN posts ON posts.category_id = categories.id   belongs_to :comment INNER JOIN comments ON comments.post_id = posts.id end   INNER JOIN guests ON guests.id = comments.quest_id class Tag < ActiveRecord::Base INNER JOIN tags ON tags.post_id = posts.id   belongs_to :post end
  • 49.
    Joining Tables - Specifying Conditions on the Joined Tables - : using Array and String Conditions time_range = (Time.now.midnight - 1.day)..Time.now.midnight Client.joins(:orders) : using nested Hash Conditions time_range = (Time.now.midnight - 1.day)..Time.now.midnight Client.joins(:orders)
  • 50.
  • 51.
      ROR Lab.