getting to know arel

36
Getting to know Arel Active Record's nerdy little brother

Upload: ray-zane

Post on 23-Feb-2017

321 views

Category:

Data & Analytics


0 download

TRANSCRIPT

Page 1: Getting to know Arel

Getting to know ArelActive Record's nerdy little brother

Page 2: Getting to know Arel

What is Arel?Arel is a SQL AST manager for Ruby

Active Record uses Arel to build queries

It adapts to various RDBMSes

It is intended to be a framework framework

Page 3: Getting to know Arel

Active Record is pretty good...

Page 4: Getting to know Arel

Dog.where(name: 'Fido')# WHERE "dogs"."name" = 'Fido'

Page 5: Getting to know Arel

Dog.where(name: nil)# WHERE "dogs"."name" IS NULL

Page 6: Getting to know Arel

Dog.where(name: ['Fido', 'Jeff'])# WHERE "dogs"."name" IN ('Fido', 'Jeff')

Page 7: Getting to know Arel

Dog.where(name: ['Fido', 'Jeff', nil])# WHERE (# "dogs"."name" IN ('Fido', 'Jeff') OR# "dogs"."name" IS NULL# )

Page 8: Getting to know Arel

Active Record is pretty good,until it isn't.

Page 9: Getting to know Arel

It only supports equality

Dog.where('age > ?', 5)# WHERE age > 5

Page 10: Getting to know Arel

No support for OR (until 5.0.0)

Dog.where('age = ? OR name = ?', 5, 'Fido')# WHERE age = 5 OR name = 'Fido'

Page 11: Getting to know Arel

No support for explicit joins

Dog.joins( 'INNER JOIN owners o ON o.id = dogs.owner_id')

Page 12: Getting to know Arel

No outer joins (without loading everything into memory)

Dog.joins( 'LEFT OUTER JOIN owners ON owners.id = dogs.owner_id')

Page 13: Getting to know Arel

Composability goes out the window

class Dog < ActiveRecord::Base scope :old, -> { where('age > ?', 5) } scope :named_fido, -> { where(name: 'Fido') }

def self.named_fido_or_old where('age > ? OR name = ?', 5, 'Fido') endend

Page 14: Getting to know Arel

Not to mention...Not database agnostic

No syntax checking

Question marks can be tough to track down

What ever happened to the 80 characters/line?

Model .joins('LEFT JOIN candidacies as search_candidates on search_candidates.contact_id = contacts.id' .joins('LEFT JOIN searches as contact_searches on search_candidates.search_id = contact_searches.id' .where('(lower(contact_searches.name) like ? AND search_candidates.deleted=?)', "%#{name}%".downcase

Page 15: Getting to know Arel

Arel to the rescue!

Page 16: Getting to know Arel

Arel::Table

Arel::Table.new(:dogs)Arel::Table.new(:dogs)[:name] # an Arel::Attribute

Dog.arel_tableDog.arel_table[:name] # an Arel::Attribute

Page 17: Getting to know Arel

Predications

age = Dog.arel_table[:age]name = Dog.arel_table[:name]

Dog.where age.gt(5)# WHERE "dogs"."age" > 5

Dog.where age.not_eq(5)# WHERE "dogs"."age" != 5

Dog.where name.matches('%ido')# WHERE "dogs"."name" LIKE '%ido'

Page 18: Getting to know Arel

Grouping

id = Dog.arel_table[:id]age = Dog.arel_table[:age]name = Dog.arel_table[:name]

Dog.where id.gt(5).and( name.eq('Ronald').or( age.eq(3) ))# WHERE (# "dogs"."id" > 5 AND (# "dogs"."name" = 'Ronald' OR# "dogs"."age" = 3# )# )

Page 19: Getting to know Arel

Inner Join

dogs = Dog.arel_tableowners = Owner.arel_table

join = dogs.inner_join(owners).on( dogs[:owner_id].eq(owners[:id]))

Dog.joins join.join_sources# SELECT "dogs".* FROM "dogs"# INNER JOIN "owners"# ON "dogs"."owner_id" = "owners"."id"

Page 20: Getting to know Arel

Outer Join

dogs = Dog.arel_tableowners = Owner.arel_table

join = dogs.outer_join(owners).on( dogs[:owner_id].eq(owners[:id]))

Dog.joins join.join_sources# SELECT "dogs".* FROM "dogs"# LEFT OUTER JOIN "owners"# ON "dogs"."owner_id" = "owners"."id"

Page 21: Getting to know Arel

Composability �

class Dog < ActiveRecord::Base scope :old, -> { where(old_arel) } scope :named_fido, -> { where(named_fido_arel) }

def self.old_or_named_fido where named_fido_arel.or(old_arel) end

def self.old_arel arel_table[:age].gt(5) end

def self.named_fido_arel arel_table[:name].eq('Fido') endend

Page 22: Getting to know Arel

Arel can do anything!

Page 23: Getting to know Arel

Aggregates

Dog.select Dog.arel_table[:age].maximum# SELECT MAX("dogs"."age") FROM "dogs"

Page 24: Getting to know Arel

Functions

Dog.select( Arel::Nodes::NamedFunction.new('COALESCE', [ Dog.arel_table[:name], Arel::Nodes.build_quoted('Ronald') ]))# SELECT COALESCE("dogs"."name", 'Ronald')# FROM "dogs"

Page 25: Getting to know Arel

Infix

Dog.select( Arel::Nodes::InfixOperation.new('||', Dog.arel_table[:name], Arel::Nodes.build_quoted('diddly') ))# SELECT "dogs"."name" || 'diddly'# FROM "dogs"

Dog.select("name || 'diddly'")# SELECT.... nevermind...

Page 26: Getting to know Arel

Am I just wasting your time?(this Arel stuff is pretty verbose)

Page 27: Getting to know Arel

Introducing Baby Squeel

Page 28: Getting to know Arel

� Extremely similar to Squeel� Under 500 LOC� No core exts� No monkey patches!� As conservative as possible

Page 29: Getting to know Arel

Predications

Dog.where.has { age > 5 }# WHERE ("dogs"."age" > 5)

Dog.where.has { name != 'Jeff' }# WHERE ("dogs"."name" != 'Jeff')

Dog.where.has { name =~ '%ido' }# WHERE ("name" LIKE '%ido')

Page 30: Getting to know Arel

Grouping

Dog.where.has { (id > 5).and( (name == 'Ronald').or(age == 3) )}# WHERE (# "dogs"."id" > 5 AND (# "dogs"."name" = 'Ronald' OR# "dogs"."age" = 3# )# )

Page 31: Getting to know Arel

Dog.selecting { age.maximum }# SELECT MAX("dogs"."age") FROM "dogs"

Dog.selecting { (id + 100) / 20 }# SELECT ("dogs"."id" + 100) / 20 FROM "dogs"

Dog.selecting { coalesce(name, quoted('Ronald')) }# SELECT coalesce("dogs"."name", 'Ronald') FROM "dogs"

Dog.selecting { name.op('||', quoted('diddly')) }# SELECT "dogs"."name" || 'diddly' FROM "dogs"

Page 32: Getting to know Arel

Explicit Joins

Dog.joining { owner.on(owner_id == owner.id) }# INNER JOIN "owners"# ON "dogs"."owner_id" = "owners"."id"

Dog.joining { owner.outer.on(owner.id == owner_id) }# LEFT OUTER JOIN "owners"# ON "dogs"."owner_id" = "owners"."id"

Page 33: Getting to know Arel

Implicit Joins

class Dog < ActiveRecord::Base belongs_to :ownerend

Dog.joining { owner }# INNER JOIN "owners"# ON "owners"."id" = "dogs"."owner_id"

Dog.joining { owner.outer }# LEFT OUTER JOIN "owners"# ON "owners"."id" = "dogs"."owner_id"

Page 34: Getting to know Arel

Join like a BO$$

Dog.joining { owner.dog.outer.owner.dog }# SELECT "dogs".* FROM "dogs"# INNER JOIN "owners"# ON "owners"."id" = "dogs"."owner_id"# LEFT OUTER JOIN "dogs" "dogs_owners"# ON "dogs_owners"."owner_id" = "owners"."id"# INNER JOIN "owners" "owners_dogs"# ON "owners_dogs"."id" = "dogs_owners"."owner_id"# INNER JOIN "dogs" "dogs_owners_2"# ON "dogs_owners_2"."owner_id" = "owners_dogs"."id"

Page 35: Getting to know Arel

Composability �

class Dog < ActiveRecord::Base sifter(:old) { age > 5 } sifter(:named_fido) { name == 'Fido' }

scope :old, -> { where.has { sift(:old) } } scope :named_fido, -> { where.has { sift(:named_fido) } }

def self.old_or_named_fido where.has { sift(:old) | sift(:named_fido) } endend

Page 36: Getting to know Arel

Thanks.Now, please stop using strings to generate SQL.