ActiveRecord & ARel
Upcoming SlideShare
Loading in...5
×
 

ActiveRecord & ARel

on

  • 7,724 views

 

Statistics

Views

Total Views
7,724
Views on SlideShare
7,678
Embed Views
46

Actions

Likes
10
Downloads
53
Comments
0

3 Embeds 46

https://gitter.im 32
https://twitter.com 11
http://duckduckgo.com 3

Accessibility

Categories

Upload Details

Uploaded via as Microsoft PowerPoint

Usage Rights

CC Attribution-NonCommercial-ShareAlike LicenseCC Attribution-NonCommercial-ShareAlike LicenseCC Attribution-NonCommercial-ShareAlike License

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

ActiveRecord & ARel ActiveRecord & ARel Presentation Transcript

  • ActiveRecord & ARel Awkward friends
  • ARel is not ...
    • A replacement for ActiveRecord!
  • ARel is ...
      • Active Relation
      • Really neat
      • Still under development
      • The safest way to write SQL
      • SQL engine agnostic!
      • An OOP interpretation of Relational Algebra
      • A DSL
      • Docs? Documentation? Uh, he's sick.
      • Use it!
      • All examples assume PSQL
  • How to use ARel
    • ARel can be used independently of ActiveRecord, but I'm not going to focus on that, because it's not my use case.
    • The easiest way to use ARel, with ActiveRecord, is to access the #arel_table of your ActiveRecord models. This method exposes the Arel::Table object associated with your model. Through this object you can do many ARel-y things.
    • There are a few other class level methods, which will be covered in a bit.
  • Accessing attributes
      • Model.arel_table[:attr]
      • Model.arel_table[:attr]. as("alias").to_sql
    • The essential building block for AR & AR interaction. All of the model's attributes are available as keys to the arel_table.
    • "model"."attr" AS alias
    • Notice the result is canonically referenced and properly quoted (very portable)
  • Predicates
      • Model.arel_table[:id]. eq (1).to_sql
      • Model.arel_table[:id]. not_eq (1).to_sql
      • Model.arel_table[:id]. in ([1,2]).to_sql
      • Model.arel_table[:id]. not_in ([1,2]). to_sql
      • Model.arel_table[:name]. matches ("a%"). to_sql
      • Model.arel_table[:name]. does_not_match ("a%").to_sql
      • Model.arel_table[:id]. gt (1).to_sql
      • Model.arel_table[:id]. gteq (1).to_sql
      • Model.arel_table[:id]. lt (1).to_sql
      • Model.arel_table[:id]. lteq (1).to_sql
      • "model"."id" = 1
      • "model"."id" != 1
      • "model"."id" IN (1,2)
      • "model"."id" NOT IN (1,2)
      • "model"."name" ILIKE "a%"
      • "model"."name" NOT ILIKE "a%"
      • "model"."id" > 1
      • "model"."id" >= 1
      • &quot;model&quot;.&quot;id&quot; < 1
      • &quot;model&quot;.&quot;id&quot; <= 1
  • Predicate alternates
    • Each basic predicate supports two additional types, _any and _all . These methods each accept arrays of values, ie
    • Model.arel_table[:id]. eq_any ([1,2]).to_sql
    • => SELECT &quot;models&quot;.* FROM &quot;models&quot; WHERE (&quot;models&quot;.&quot;id&quot; = 1 OR &quot;models&quot;.&quot;id&quot; = 2)
    • Model.arel_table[:id]. in_all ([[1,2],[3,4]]).to_sql
    • => SELECT &quot;models&quot;.* FROM &quot;models&quot; WHERE (&quot;models&quot;.&quot;id&quot; IN (1,2) AND &quot;models&quot;.&quot;id&quot; IN (3,4))
  • Aggregate Functions
      • Model.arel_table[:int]. average .to_sql
      • Model.arel_table[:int]. average .as(&quot;alias&quot;).to_sql
      • Model.arel_table[:int]. count .to_sql
      • Model.arel_table[:int]. count .as(&quot;alias&quot;).to_sql
      • Model.arel_table[:int]. maximum .to_sql
      • Model.arel_table[:int]. minimum .to_sql
      • Model.arel_table[:int]. sum .to_sql
      • AVG (&quot;models&quot;.&quot;int&quot;) AS avg_id
      • AVG (&quot;models&quot;.&quot;int&quot;) AS alias
      • COUNT (&quot;models&quot;.&quot;int&quot;)
      • COUNT (&quot;models&quot;.&quot;int&quot;) AS alias
      • MAX (&quot;models&quot;.&quot;int&quot;) AS max_id
      • MIN (&quot;models&quot;.&quot;int&quot;) AS min_id
      • SUM (&quot;models&quot;.&quot;int&quot;) AS sum_id
  • Infix operators
    • m = Model.arel_table
      • m[:int1] + m[:int2]
      • m[:int1] - m[:int2]
      • m[:int1] * m[:int2]
      • m[:int1] * m[:int2].as &quot;int&quot;
      • m[:int1] / m[:int2]
      • m[:int1] / m[:int2].as &quot;int&quot;
      • Model.select( m[:int1] * m[:int2].     as(&quot;product&quot;)).to_sql
    • No support for bitwise operators or aliasing addition / subtraction
      • (int1 + int2)
      • (int1 - int2)
      • int1 * int2
      • int1 * int2 AS int
      • int1 / int2
      • int1 / int2 AS int
      • SELECT &quot;models&quot;.&quot;int1&quot; * &quot;models&quot;.&quot;int2&quot; AS product FROM &quot;models&quot;
  • Mixing ActiveRecord & ARel Where inconsistencies collide
  • Selecting results
    • m = Model.arel_table
      • Model.select( [ m[:id], m[:name] ). to_sql
      • Model. select( m[:int1].average.as(&quot;int1&quot; )). to_sql
      • Model.select( m[:int1] + m[:int2] ).to_sql
      • SELECT &quot;models&quot;.&quot;id&quot;, &quot;models&quot;.&quot;name&quot; FROM &quot;models&quot;
      • SELECT AVG(&quot;models&quot;.&quot;int1&quot;) AS int1 FROM &quot;models&quot;
      • SELECT (&quot;models&quot;.&quot;int1&quot; + &quot;models&quot;.&quot;int2&quot;) FROM &quot;models&quot;
  • Filtering results (where)
    • m = Model.arel_table
      • Model.where( m[:id].gteq(10) ). to_sql
      • Model.where( m.grouping( m[:id].gt(10).  and(m[:name].matches(&quot;foo%&quot;)). or(m[:int1].in([1,2])) )
      • Model.where( m[:int1] + m[:int2].gteq(100) )
      • SELECT &quot;models&quot;.* FROM &quot;models&quot; WHERE &quot;models&quot;.&quot;id&quot; >= 10
      • SELECT &quot;models&quot;.* FROM &quot;models&quot; WHERE ( (( &quot;models&quot;.&quot;id&quot; > 10 AND &quot;models&quot;.&quot;name&quot; ILIKE &quot;foo%&quot;) OR &quot;models&quot;.&quot;int1&quot; IN (1,2)) )
      • SELECT &quot;models&quot;.* FROM &quot;models&quot; WHERE &quot;models&quot;.&quot;int1&quot; +  &quot;models&quot;.&quot;int2&quot; >= 100
  • Grouping and Having results
      • ARel
        • Model.select( m[:name] ). group( m[:name] ).to_sql
      • ActiveRecord
        • Model.select( :name ). group( :name ).to_sql
    • The ARel methods are important, because ActiveRecord does not canonically reference fields
      • SELECT &quot;models&quot;.&quot;name&quot;   FROM &quot;models&quot;  GROUP BY &quot;models&quot;.&quot;name&quot;
      • SELECT name FROM &quot;models&quot;  GROUP BY name
  • Ordering results
    • m = Model.arel_table
      • m[:id]. asc
      • m[:id]. desc
      • m[:string]. lower
      • ARel
        • Model.order( m[:id].asc ). to_sql
      • ActiveRecord
        • Model.order( :id ).to_sql
    • The ARel methods are important, because ActiveRecord does not canonically reference fields
      • &quot;models&quot;.&quot;id&quot; ASC
      • &quot;models&quot;.&quot;id&quot; DESC
      • LOWER (&quot;models&quot;.&quot;string&quot;)
      • SELECT &quot;models&quot;.* FROM &quot;models&quot; ORDER BY &quot;models&quot;.&quot;id&quot; ASC
      • SELECT &quot;models&quot;.*  FROM &quot;models&quot;  ORDER BY id
  • Limiting results
      • Model. skip (10).to_sql
      • Model. take (100).to_sql
      • Model. skip (10). take (100). to_sql
    • Take must be the last method in the chain, always.
      • SELECT &quot;models&quot;.* FROM &quot;models&quot; OFFSET 10
      • SELECT &quot;models&quot;.* FROM &quot;models&quot; LIMIT 100
      • SELECT &quot;models&quot;.* FROM &quot;models&quot; LIMIT 100 OFFSET 10
  • Sub Selects
    • sub = Models.joins(:others).
    •       as(&quot;alias&quot;)
    • Models.select('*').from(sub).
    • to_sql
    • SELECT * 
    • FROM (
    •   SELECT &quot;models&quot;.* 
    •   FROM &quot;models&quot; 
    •     INNER JOIN &quot;others&quot; ON 
    •       &quot;others&quot;.&quot;model_id&quot; =    
    •       &quot;models&quot;.&quot;id&quot;
    • ) alias
  • Unions
    • m = Model.arel_table
    • o = Other.arel_table
      • Model.select(m[:id]).union( Other.select(o[:id])).to_sql
      • ( SELECT &quot;models&quot;.&quot;id&quot; FROM &quot;models&quot; UNION SELECT &quot;others&quot;.&quot;id&quot; FROM &quot;others&quot; )
  • Complex joins
    • Polymorphic join
    • m = Model.arel_table
    • o = Other.arel_table
    • sql =  m.joins(o).on(
    •     m[:id].eq(o[:able_id]).
    •     and(o[:able_type].eq(&quot;O&quot;))
    •   ).to_sql
    • Model.joins(sql).to_sql
    • SELECT &quot;models&quot;.*
    • FROM &quot;models&quot;
    •   INNER JOIN &quot;others&quot; ON     (&quot;models&quot;.&quot;id&quot; = 
    •         &quot;others&quot;.&quot;able_id&quot;
    •       AND
    •       &quot;others&quot;.&quot;able_type&quot; = 
    •         &quot;O&quot;
    •       )
  • Arbitrary SQL functions
    • m = Model.arel_table
      • Model.select( Arel::Nodes::NamedFunction.new( :coalesce, [m[:id], 0])).to_sql
      • Model.select( Arel::Nodes::NamedFunction.new( :coalesce, [m[:id], 0], &quot;foo&quot;)). to_sql
      • SELECT coalesce(&quot;models&quot;.&quot;id&quot;, 0) FROM &quot;models&quot;
      • SELECT coalesce(&quot;models&quot;.&quot;id&quot;, 0)   AS foo FROM &quot;models&quot;
  • Easier Arbitrary SQL Functions
    • config/initializers/arel_extensions.rb
    • Arel::Table.class_eval do |base|
    •   def function(name, expr, aliaz = nil)
    •     Arel::Nodes::NamedFunction.new(name, expr, aliaz)
    •   end
    • end
    • m = Model.arel_table
    • Model.select(m.function(:coalesce, [m[:id], 0], &quot;foo&quot;))
  • Peeking and poking
    • m = Model.arel_table
    • query = Model.   joins(:others).
    •   merge(Other.where(id: 1)).   order(m[:id].desc).
    •   skip(10).
    •   take(100)
      • query. join_sql
      • query. where_sql
      • query. to_sql
      • INNER JOIN &quot;others&quot; ON &quot;models&quot;.&quot;id&quot; = &quot;others&quot;.&quot;model_id&quot;
      • WHERE (&quot;others&quot;.&quot;id&quot; = 1)
      • SELECT &quot;models&quot;.* FROM &quot;model&quot;   INNER JOIN &quot;others&quot; ON   &quot;models&quot;.&quot;id&quot; =   &quot;others&quot;.&quot;model_id&quot; WHERE (&quot;others&quot;.&quot;id&quot; = 1) ORDER BY &quot;models&quot;.&quot;id&quot; DESC OFFSET 10 LIMIT 100
  • Links
      • http://magicscalingsprinkles.wordpress.com/2010/01/28/why-i-wrote-arel/
      • https://github.com/rails/arel
      • http://www.railsdispatch.com/posts/activerelation
      • http://m.onkey.org/active-record-query-interface
      • http://www.bigbinary.com/videos/5-how-arel-works
      • https://github.com/stevecj/arel-presentation-slides/blob/master/one/01_slide.md
      • http://railscasts.com/episodes/215-advanced-queries-in-rails-3