Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Advanced Arel: When ActiveRecord Just Isn't Enough

18,949 views

Published on

Talk given at RailsConf 2014 about using Arel to build database queries of arbitrary complexity.

  • Hello! Get Your Professional Job-Winning Resume Here - Check our website! https://vk.cc/818RFv
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • Hello everybody, I don't just know the reason why some people is finding it difficult to believe that there is a cure for herpes, I have been suffering from herpes since last three years with my boyfriend but today I am happy that am cure from it with the herbal medicine of Dr Aba the great healer,I was browsing the internet searching for help when I came across a testimony shared by someone on how Dr Aba cure his herpes I was so much in need of getting his treatment but after all Dr Aba brought a smile to my face with his herbal medicine. I am so much happy today that we have someone like this great healer out there, so my people out there kindly contact this great healer on his email address: dr.abaherbalhome@gmail.com please sir keep your good work cause there are people out there who is in need of your healing medicine.once more contact him now: dr.abaherbalhome@gmail.com you can call him or whatsApp his number +2348107155060
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • Do anyone still doubt natural herbs? I've seen the great importance of natural herbs and the wonderful work they have done in people's lives. I wonder why people still spend their money on surgery, injections and drugs each time they are sick. Natural herbs can cure all kinds of illness including herpes, diabetics, asthma, HIV, hepatitis, etc. I've seen it with my own eyes. I was cured of HIV and my aunt and her husband were cured of herpes by Dr. Baba Ogu who uses natural herbs to cure different kind of illness. Even Dr. Sebi prove to the whole world that natural herbs can cure all diseases and he cured countless of people using natural herbs. I know is hard to believe but am a living testimony. There is no harm in trying herbs. He is also a spell caster, he can cast a spell to restore your marriage back to normal, a good luck spell to prosper in life and excel in whatever you do in life. Contact or email Dr. Baba Ogu on: drbabaogu@gmail.com (phone/whatsap no): +2348137291215 facebook.com/drbabaogucure/
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • Good day to my viewers out there.. please just read this, some times we just need to give a try of herbal Medicine to see for our self. I am very happy today that i do not listen to what people say if not i would have been a dead man by now. I was infected with HERPES SIMPLEX VIRUS in 2014, i went to many hospitals for cure but there was no solution, so I was thinking how can I get a solution out so that my body can be okay. One day I was in the river side thinking where I can go to get solution. so at that moment i was doing some research about remedy for herpes on my cell phone, so i came across an articles about a lady testifying on how she get cured by the help of a doctor called Odoma, so i have to write down the email contact of the doctor that she recommended which is ( odomaspelltemple@outlook.com ) on a very good day i decide to write to this doctor and explain how this virus is killing me slowing, after introducing myself to this doctor after some time i got a reply back from him, and he told me the important of his herbal medicine also how he has cure HERPES SIMPLEX VIRUS and all kinds of virus, He told me all the things I need to do and also give me instructions to take, which I followed properly. Before I knew what gradually my body was getting recovery and i was feeling so different than ever after two weeks the HERPES SIMPLEX VIRUS that was in my body got vanished. so if you are also heart broken and also need a help, you can also contact him Via Email (odomaspelltemple@outlook.com) and give a try i promise if you can stay in touch with Him through his contact email address stated here correctly you wont miss out okay here is the Email once again ( odomaspelltemple@outlook.com ) and you will give a testimony too wish you Good luck guys...
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • Hello everybody, I don't just know the reason why some people is finding it difficult to believe that there is a cure for herpes, I have been suffering from herpes since last three years with my boyfriend but today I am happy that am cure from it with the herbal medicine of Dr Aba the great healer,I was browsing the internet searching for help when I came across a testimony shared by someone on how Dr Aba cure his herpes I was so much in need of getting his treatment but after all Dr Aba brought a smile to my face with his herbal medicine. I am so much happy today that we have someone like this great healer out there, so my people out there kindly contact this great healer on his email address: dr.abaherbalhome@gmail.com please sir keep your good work cause there are people out there who is in need of your healing medicine.once more contact him now: dr.abaherbalhome@gmail.com you can call him or whatsApp his number +2348107155060
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here

Advanced Arel: When ActiveRecord Just Isn't Enough

  1. 1. ADVANCED AREL WHEN ACTIVERECORD JUST ISN’T ENOUGH Author.where( name: "Cameron Dutro", username: "@camertron" )
  2. 2. `WHOAMI` CAMERON DUTRO INTERNATIONAL MAN OF MYSTERY US GOVERNMENT @CAMERTRON GITHUB.COM/CAMERTRON
  3. 3. `WHOAMI` CAMERON DUTRO INTERNATIONAL MAN OF MYSTERY US GOVERNMENT @CAMERTRON GITHUB.COM/CAMERTRON
  4. 4. `WHOAMI` CAMERON DUTRO INTERNATIONAL ENGINEERING TWITTER, INC @CAMERTRON GITHUB.COM/CAMERTRON
  5. 5. RAISE YOUR HAND IF... Posts.find(:all, joins: [ "JOIN comments ON comments.post_id = posts.id", "JOIN authors ON authors.id = comments.author_id" ], conditions: [ "authors.name = ? AND posts.active = ?", "Barack Obama", true ] ) 2
  6. 6. RAISE YOUR HAND IF... Posts .joins( "JOIN comments ON comments.post_id = posts.id", "JOIN authors ON authors.id = comments.author_id") .where( "authors.name = ? AND posts.active = ?", "Barack Obama", true ) 3/4
  7. 7. RAISE YOUR HAND IF... Posts .joins( "JOIN comments ON comments.post_id = posts.id", "JOIN authors ON authors.id = comments.author_id") .where( "authors.name = ? AND posts.active = ?", "Barack Obama", true ) 3/4
  8. 8. HMM, LET’S SIMPLIFY? Posts .joins(:comments) .joins( "JOIN authors ON authors.id = comments.author_id" ) .where( "authors.name = ? AND posts.active = ?", "Barack Obama", true ) 3/4
  9. 9. HMM, LET’S SIMPLIFY? Posts .joins(:comments) .joins(:comments => :author) .where( "authors.name = ? AND posts.active = ?", "Barack Obama", true ) 3/4
  10. 10. PROBLEMS 3/4 Posts .joins(:comments) .joins( "JOIN authors ON authors.id = comments.author_id" ) .where( "authors.name = ? AND posts.active = ?", "Barack Obama", true )
  11. 11. PROBLEMS .joins( "JOIN authors ON authors.id = comments.author_id" )
  12. 12. PROBLEMS .joins( "JOIN authors ON authors.id = comments.author_id" ) HAVE TO WRITE “JOIN” AND “ON”
  13. 13. PROBLEMS .joins( "JOIN authors ON authors.id = comments.author_id" ) HAVE TO WRITE “JOIN” AND “ON” HAVE TO KNOW MYSQL SYNTAX
  14. 14. PROBLEMS .joins( "JOIN authors ON authors.id = comments.author_id" ) HAVE TO WRITE “JOIN” AND “ON” HAVE TO KNOW MYSQL SYNTAX NO SYNTAX CHECKING
  15. 15. PROBLEMS 3/4 Posts .joins(:comments) .joins( "JOIN authors ON authors.id = comments.author_id" ) .where( "authors.name = ? AND posts.active = ?", "Barack Obama", true )
  16. 16. PROBLEMS .where( "authors.name = ? AND posts.active = ?", "Barack Obama", true )
  17. 17. PROBLEMS .where( "authors.name = ? AND posts.active = ?", "Barack Obama", true ) HAVE TO KNOW MYSQL SYNTAX
  18. 18. PROBLEMS .where( "authors.name = ? AND posts.active = ?", "Barack Obama", true ) HAVE TO KNOW MYSQL SYNTAX CONFUSING TO MATCH ARGUMENTS WITH QUESTION MARKS
  19. 19. PROBLEMS .where( "authors.name = ? AND posts.active = ?", "Barack Obama", true ) HAVE TO KNOW MYSQL SYNTAX CONFUSING TO MATCH ARGUMENTS WITH QUESTION MARKS NOT OBJECT-ORIENTED
  20. 20. PROBLEMS .where( "authors.name = ? AND posts.active = ?", "Barack Obama", true ) HAVE TO KNOW MYSQL SYNTAX CONFUSING TO MATCH ARGUMENTS WITH QUESTION MARKS NOT OBJECT-ORIENTED NO SYNTAX CHECKING
  21. 21. STACKOVERFLOW
  22. 22. STACKOVERFLOW RAILSCASTS #202
  23. 23. STACKOVERFLOW RAILSCASTS #202 BLOGS
  24. 24. STACKOVERFLOW RAILSCASTS #202 BLOGS COWORKERS
  25. 25. STACKOVERFLOW RAILSCASTS #202 BLOGS COWORKERS FRIENDS
  26. 26. STACKOVERFLOW RAILSCASTS #202 BLOGS COWORKERS FRIENDS FAMILY MEMBERS
  27. 27. STACKOVERFLOW RAILSCASTS #202 BLOGS COWORKERS FRIENDS FAMILY MEMBERS YOUR CAT
  28. 28. KEEP CALM AND AVOID LITERAL STRINGS IN YOUR QUERIES
  29. 29. A BETTER WAY Post .joins(:comments) .joins(Comment.joins(:author).join_sources) .where( Author[:name].eq("Barack Obama") .and(Post[:active].eq(true)) )
  30. 30. A BETTER WAY Post .joins(:comments) .joins(Comment.joins(:author).join_sources) .where( Author[:name].eq("Barack Obama") .and(Post[:active].eq(true)) ) DON’T HAVE TO KNOW SQL SYNTAX
  31. 31. A BETTER WAY Post .joins(:comments) .joins(Comment.joins(:author).join_sources) .where( Author[:name].eq("Barack Obama") .and(Post[:active].eq(true)) ) DON’T HAVE TO KNOW SQL SYNTAX RUBY SYNTAX CHECKING
  32. 32. A BETTER WAY Post .joins(:comments) .joins(Comment.joins(:author).join_sources) .where( Author[:name].eq("Barack Obama") .and(Post[:active].eq(true)) ) DON’T HAVE TO KNOW SQL SYNTAX RUBY SYNTAX CHECKING OBJECT-ORIENTED (CHAINABLE)
  33. 33. A BETTER WAY Post .joins(:comments) .joins(Comment.joins(:author).join_sources) .where( Author[:name].eq("Barack Obama") .and(Post[:active].eq(true)) ) DON’T HAVE TO KNOW SQL SYNTAX RUBY SYNTAX CHECKING OBJECT-ORIENTED (CHAINABLE) NO QUESTION MARKS
  34. 34. A BETTER WAY Post .joins(:comments) .joins(Comment.joins(:author).join_sources) .where( Author[:name].eq("Barack Obama") .and(Post[:active].eq(true)) ) DON’T HAVE TO KNOW SQL SYNTAX RUBY SYNTAX CHECKING OBJECT-ORIENTED (CHAINABLE) NO QUESTION MARKS EASY TO READ - IT’S JUST RUBY!
  35. 35. WHAT WE’LL COVER ACTIVERECORD VS AREL TABLES, COLUMNS TERMINAL METHODS SELECT, WHERE, JOIN, JOIN ASSOCIATION, ORDER AND, OR , GREATER/LESS THAN, NOT EQUALS, ETC MATCH, IN
  36. 36. ACTIVERECORD DATABASE ABSTRACTION NO NEED TO SPEAK A DIALECT OF SQL PERSISTENCE DATABASE ROWS AS RUBY OBJECTS DOMAIN LOGIC MODELS CONTAIN APPLICATION LOGIC, VALIDATIONS, ETC MODELS DEFINE ASSOCIATIONS
  37. 37. AREL “RELATIONAL ALGEBRA” FOR RUBY BUILDS SQL QUERIES, GENERATES ASTS APPLIES QUERY OPTIMIZATIONS ENABLES CHAINING “VEXINGLY UNDOCUMENTED”
  38. 38. AREL KNOWS NOTHING ABOUT YOUR MODELS KNOWS VERY LITTLE ABOUT YOUR DATABASE DOES NOT RETRIEVE OR STORE DATA
  39. 39. AREL KNOWS NOTHING ABOUT YOUR MODELS KNOWS VERY LITTLE ABOUT YOUR DATABASE DOES NOT RETRIEVE OR STORE DATA ACTIVERECORD’S RESPONSIBILITY
  40. 40. AREL CONSTRUCTS QUERIES
  41. 41. ACTIVERECORD DOES EVERYTHING ELSE
  42. 42. HIERARCHY activerecord arel database
  43. 43. activerecord arel database CONSTRUCT QUERY EXECUTE QUERY HIERARCHY
  44. 44. WHAT’S AN AST? 7 4 9 1 5
  45. 45. WHAT’S AN AST? 7 4 9 1 5 LEFT CHILD
  46. 46. WHAT’S AN AST? 7 4 9 1 5 LEFT CHILD RIGHT CHILD
  47. 47. WHAT’S AN AST? 5 * (6 + 3) * 5 + 6 3
  48. 48. WHAT’S AN AST? SELECT id, text FROM posts SELECT id text <query> FROM posts
  49. 49. LET’S GET TO SOME CODE
  50. 50. AREL-HELPERS GEM gem install arel-helpers gem “arel-helpers”, “~> 1.1.0”
  51. 51. TABLES AND COLUMNS class Post < ActiveRecord::Base has_many :comments end
  52. 52. TABLES AND COLUMNS class Post < ActiveRecord::Base has_many :comments end Post.arel_table[:id]
  53. 53. TABLES AND COLUMNS class Post < ActiveRecord::Base has_many :comments end Post.arel_table[:id] Post.arel_table[:text]
  54. 54. TABLES AND COLUMNS class Post < ActiveRecord::Base has_many :comments end Post.arel_table[:id] Post.arel_table[:text] => #<struct Arel::Attributes::Attribute ... >
  55. 55. TABLES AND COLUMNS class Post < ActiveRecord::Base include ArelHelpers::ArelTable has_many :comments end Post.arel_table[:id] Post.arel_table[:text] => #<struct Arel::Attributes::Attribute ... >
  56. 56. TABLES AND COLUMNS class Post < ActiveRecord::Base include ArelHelpers::ArelTable has_many :comments end Post[:id] Post[:text] => #<struct Arel::Attributes::Attribute ... >
  57. 57. RELATIONS POP QUIZ! WHAT DOES THIS STATEMENT RETURN? Post.select(:title)
  58. 58. RELATIONS POP QUIZ! WHAT DOES THIS STATEMENT RETURN? Post.select(:title) A. [“Rails is Cool” ... ]
  59. 59. RELATIONS POP QUIZ! WHAT DOES THIS STATEMENT RETURN? Post.select(:title) A. [“Rails is Cool” ... ] B. [#<Post title=”Rails is Cool”>, ... ]
  60. 60. RELATIONS POP QUIZ! WHAT DOES THIS STATEMENT RETURN? Post.select(:title) A. [“Rails is Cool” ... ] C. <ActiveRecord::Relation ... > B. [#<Post title=”Rails is Cool”>, ... ]
  61. 61. RELATIONS POP QUIZ! WHAT DOES THIS STATEMENT RETURN? Post.select(:title) A. [“Rails is Cool” ... ] C. <ActiveRecord::Relation ... > B. [#<Post title=”Rails is Cool”>, ... ]
  62. 62. RELATIONS query = Post.select(:title)
  63. 63. RELATIONS query = Post.select(:title) query = query.select(:id)
  64. 64. RELATIONS query = Post.select(:title) query.to_sql query = query.select(:id)
  65. 65. RELATIONS query = Post.select(:title) query.to_sql => SELECT title, id FROM `posts` query = query.select(:id)
  66. 66. RELATIONS query = Post.select(:title) query.to_sql => SELECT title, id FROM `posts` query = query.select(:id) RELATIONS CAN BE CHAINED!
  67. 67. THE SERENDIPITY OF “SELECT”
  68. 68. SELECT Post.select([:id, :text]).to_sql => SELECT id, text FROM `posts`
  69. 69. SELECT Post.select([:id, :text]).to_sql => SELECT id, text FROM `posts` Post.select(:id).count.to_sql => NoMethodError: undefined method `to_sql' for 26:Fixnum
  70. 70. SELECT Post.select([:id, :text]).to_sql => SELECT id, text FROM `posts` Post.select(:id).count.to_sql => NoMethodError: undefined method `to_sql' for 26:Fixnum WHAT HAPPENED??
  71. 71. SELECT Post.select([:id, :text]).to_sql => SELECT id, text FROM `posts` Post.select(:id).count.to_sql => NoMethodError: undefined method `to_sql' for 26:Fixnum WHAT HAPPENED?? .count IS A TERMINAL METHOD
  72. 72. SELECT Post.select([Post[:id].count, :text]).to_sql => SELECT COUNT(`posts`.`id`), text FROM `posts`
  73. 73. TERMINAL METHODS EXECUTE IMMEDIATELY DO NOT RETURN AN ActiveRecord::Relation count first, last to_a pluck each, map, ETC
  74. 74. TERMINAL METHODS Post.where(title: "Arel is Cool").each do |post| puts post.text end
  75. 75. TERMINAL METHODS Post.where(title: "Arel is Cool").each do |post| puts post.text end Post.where(title: "Arel is Cool").each_slice(3)
  76. 76. TERMINAL METHODS Post.where(title: "Arel is Cool").each do |post| puts post.text end Post.where(title: "Arel is Cool").each_slice(3) BOTH EXECUTE THE QUERY IMMEDIATELY
  77. 77. SELECT Post.select(Post[:visitors].sum).to_sql => SELECT SUM(`posts`.`visitors`) AS sum_id FROM `posts`
  78. 78. SELECT Post.select(Post[:visitors].sum.as("visitor_total")).to_sql => SELECT SUM(`posts`.`views`) AS visitor_total FROM `posts`
  79. 79. SELECT Post.select(Post[:visitors].sum).to_sql => SELECT SUM(`posts`.`visitors`) AS sum_id FROM `posts` Post.select(Post[:visitors].maximum).to_sql => SELECT MAX(`posts`.`visitors`) AS max_id FROM `posts`
  80. 80. SELECT Post.select(Post[:visitors].sum).to_sql => SELECT SUM(`posts`.`visitors`) AS sum_id FROM `posts` Post.select(Post[:visitors].maximum).to_sql => SELECT MAX(`posts`.`visitors`) AS max_id FROM `posts` Post.select(Post[:visitors].minimum).to_sql => SELECT MIN(`posts`.`visitors`) AS min_id FROM `posts`
  81. 81. SELECT Post.select( Arel::Nodes::NamedFunction.new( "LENGTH", [Post[:text]] ).as("length") ).to_sql => SELECT LENGTH(`posts`.`text`) AS length FROM `posts`
  82. 82. SELECT Post.select( Arel::Nodes::NamedFunction.new( "LENGTH", [Post[:text]] ).as("length") ).to_sql => SELECT LENGTH(`posts`.`text`) AS length FROM `posts`
  83. 83. SELECT Post.select( Arel::Nodes::NamedFunction.new( "LENGTH", [Post[:text]] ).as("length") ).to_sql => SELECT LENGTH(`posts`.`text`) AS length FROM `posts` include Arel::Nodes
  84. 84. SELECT Post.select( NamedFunction.new( "LENGTH", [Post[:text]] ).as("length") ).to_sql => SELECT LENGTH(`posts`.`text`) AS length FROM `posts` include Arel::Nodes
  85. 85. SELECT Post.select(Arel.star).to_sql => SELECT * FROM `posts`
  86. 86. SELECT FROM Post.select(:id).from(Post.select([:id, :text]).ast).to_sql => SELECT id FROM SELECT id, text FROM `posts`
  87. 87. THE WONDER OF “WHERE”
  88. 88. WHERE Post.where(title: "Arel is Cool").to_sql => SELECT `users`.* FROM `users` WHERE `users`.`title` = 'Arel is Cool' WITH ACTIVERECORD SUGAR
  89. 89. WHERE Post.where(title: "Arel is Cool").to_sql => SELECT `users`.* FROM `users` WHERE `users`.`title` = 'Arel is Cool' WITH ACTIVERECORD SUGAR Post.where(Post[:title].eq("Arel is Cool")).to_sql => SELECT `users`.* FROM `users` WHERE `users`.`title` = 'Arel is Cool' WITH AREL
  90. 90. WHERE Post.where(Post[:title].not_eq("Arel is Cool")).to_sql => SELECT `posts`.* FROM `posts` WHERE (`posts`.`title` != 'Arel is Cool')
  91. 91. WHERE Post.where(Post[:title].not_eq("Arel is Cool")).to_sql => SELECT `posts`.* FROM `posts` WHERE (`posts`.`title` != 'Arel is Cool') Post.where(Post[:title].not_eq(nil)).to_sql => SELECT `posts`.* FROM `posts` WHERE (`posts`.`title` IS NOT NULL)
  92. 92. WHERE Post.where(Post[:visitors].gt(250)).to_sql => SELECT `posts`.* FROM `posts` WHERE (`posts`.`visitors` > 250)
  93. 93. WHERE Post.where(Post[:visitors].gt(250)).to_sql => SELECT `posts`.* FROM `posts` WHERE (`posts`.`visitors` > 250) Post.where(Post[:visitors].lt(250)).to_sql => SELECT `posts`.* FROM `posts` WHERE (`posts`.`visitors` < 250)
  94. 94. WHERE Post.where(Post[:visitors].gteq(250)).to_sql => SELECT `posts`.* FROM `posts` WHERE (`posts`.`visitors` >= 250) Post.where(Post[:visitors].lteq(250)).to_sql => SELECT `posts`.* FROM `posts` WHERE (`posts`.`visitors` <= 250)
  95. 95. WHERE Post.where( Post[:title].eq("Arel is Cool").and( Post[:id].eq(22).or( Post[:id].eq(23) ) ) ).to_sql => SELECT `posts`.* FROM `posts` WHERE ( `posts`.`title` = 'Arel is Cool' AND (`posts`.`id` = 22 OR `posts`.`id` = 23) )
  96. 96. WHERE Post.where( Post[:title].eq("Arel is Cool").and( Post[:id].in(22, 23) ) ).to_sql => SELECT `posts`.* FROM `posts` WHERE ( `posts`.`title` = 'Arel is Cool' AND `posts`.`id` IN (22, 23) )
  97. 97. WHERE Post.where( Post[:title].eq("Arel is Cool").and( NamedFunction.new("LENGTH", [Post[:slug]]).gt(10) ) ).to_sql => SELECT `posts`.* FROM `posts` WHERE ( `posts`.`title` = 'Arel is Cool' AND LENGTH(`posts`.`slug`) > 10 )
  98. 98. THE JOY OF “JOIN”
  99. 99. JOIN class Post < ActiveRecord::Base has_many :comments end class Comment < ActiveRecord::Base belongs_to :post has_one :author end class Author < ActiveRecord::Base belongs_to :comment end
  100. 100. JOIN Author.joins(:comment).where(id: 42).to_sql => SELECT `authors`.* FROM `authors` INNER JOIN `comments` ON `comments`.`id` = `authors`.`comment_id` WHERE `authors`.`id` = 42
  101. 101. JOIN Author .joins(:comment, :comment => :post) .where(Post[:id].eq(42)) .to_sql => SELECT `authors`.* FROM `authors` INNER JOIN `comments` ON `comments`.`id` = `authors`.`comment_id` INNER JOIN `posts` ON `posts`.`id` = `comments`.`post_id` WHERE `posts`.`id` = 42
  102. 102. WAIT, WHAT ABOUT OUTER JOINS?
  103. 103. JOIN Author .joins(:comment, :comment => :post) .where(Post[:id].eq(42)) .to_sql => SELECT `authors`.* FROM `authors` INNER JOIN `comments` ON `comments`.`id` = `authors`.`comment_id` INNER JOIN `posts` ON `posts`.`id` = `comments`.`post_id` WHERE `posts`.`id` = 42
  104. 104. JOIN Author .joins(:comment) .joins(Comment.joins(:post).join_sources) .where(Post[:id].eq(42)) .to_sql => SELECT `authors`.* FROM `authors` INNER JOIN `comments` ON `comments`.`id` = `authors`.`comment_id` INNER JOIN `posts` ON `posts`.`id` = `comments`.`post_id` WHERE `posts`.`id` = 42
  105. 105. JOIN Author .joins(Author.joins(:comment).join_sources) .joins(Comment.joins(:post).join_sources) .where(Post[:id].eq(42)) .to_sql => SELECT `authors`.* FROM `authors` INNER JOIN `comments` ON `comments`.`id` = `authors`.`comment_id` INNER JOIN `posts` ON `posts`.`id` = `comments`.`post_id` WHERE `posts`.`id` = 42
  106. 106. JOIN Author .joins( Author.arel_table.join(Comment.arel_table) .on(Comment[:id].eq(Author[:comment_id])) .join_sources ) .joins( Comment.arel_table.join(Post.arel_table) .on(Post[:id].eq(Comment[:post_id])) .join_sources ) .where(Post[:id].eq(42)) .to_sql
  107. 107. JOIN Author .joins( Author.arel_table.join(Comment.arel_table, Arel::OuterJoin) .on(Comment[:id].eq(Author[:comment_id])) .join_sources ) .joins( Comment.arel_table.join(Post.arel_table, Arel::OuterJoin) .on(Post[:id].eq(Comment[:post_id])) .join_sources ) .where(Post[:id].eq(42)) .to_sql
  108. 108. JOIN => SELECT `authors`.* FROM `authors` LEFT OUTER JOIN `comments` ON `comments`.`id` = `authors`.`comment_id` LEFT OUTER JOIN `posts` ON `posts`.`id` = `comments`.`post_id` WHERE `posts`.`id` = 42
  109. 109. JOIN Author .joins( Author.arel_table.join(Comment.arel_table, Arel::OuterJoin) .on(Comment[:id].eq(Author[:comment_id])) .join_sources ) .joins( Comment.arel_table.join(Post.arel_table, Arel::OuterJoin) .on(Post[:id].eq(Comment[:post_id])) .join_sources ) .where(Post[:id].eq(42)) .to_sql
  110. 110. JOIN Author .joins( Author.arel_table.join(Comment.arel_table, Arel::OuterJoin) .on(Comment[:id].eq(Author[:comment_id])) .join_sources ) .joins( Comment.arel_table.join(Post.arel_table, Arel::OuterJoin) .on(Post[:id].eq(Comment[:post_id])) .join_sources ) .where(Post[:id].eq(42)) .to_sql
  111. 111. JOIN Author .joins( Author.arel_table.join(Comment.arel_table, Arel::OuterJoin) .on(Comment[:id].eq(Author[:comment_id])) .join_sources ) .joins( Comment.arel_table.join(Post.arel_table, Arel::OuterJoin) .on(Post[:id].eq(Comment[:post_id])) .join_sources ) .where(Post[:id].eq(42)) .to_sql include ArelHelpers::JoinAssociation
  112. 112. JOIN Author .joins(join_association(Author, :comment, Arel::OuterJoin)) .joins( Comment.arel_table.join(Post.arel_table, Arel::OuterJoin) .on(Post[:id].eq(Comment[:post_id])) .join_sources ) .where(Post[:id].eq(42)) .to_sql include ArelHelpers::JoinAssociation
  113. 113. JOIN Author .joins(join_association(Author, :comment, Arel::OuterJoin)) .joins(join_association(Comment, :post, Arel::OuterJoin)) .where(Post[:id].eq(42)) .to_sql include ArelHelpers::JoinAssociation
  114. 114. JOIN Author .joins( join_association(Author, :comment) do |assoc_name, join_conds| join_conds.and(Comment[:created_at].lteq(Date.yesterday)) end ) .joins(join_association(Comment, :post, Arel::OuterJoin)) .where(Post[:id].eq(42)) .to_sql include ArelHelpers::JoinAssociation
  115. 115. JOIN => SELECT `authors`.* FROM `authors` INNER JOIN `comments` ON `comments`.`id` = `authors`.`comment_id` AND `comments`.`created_at` <= '2014-04-15' LEFT OUTER JOIN `posts` ON `posts`.`id` = `comments`.`post_id` WHERE `posts`.`id` = 42
  116. 116. JOIN TABLES class Course < ActiveRecord::Base has_and_belongs_to_many :teachers end class Teacher < ActiveRecord::Base has_and_belongs_to_many :courses end
  117. 117. JOIN TABLES class Course < ActiveRecord::Base has_and_belongs_to_many :teachers end class Teacher < ActiveRecord::Base has_and_belongs_to_many :courses end courses teachers courses_teachers
  118. 118. JOIN TABLES
  119. 119. JOIN TABLES course TABLE
  120. 120. JOIN TABLES Course.arel_table course TABLE
  121. 121. JOIN TABLES Course.arel_table course TABLE teacher TABLE
  122. 122. JOIN TABLES Course.arel_table Teacher.arel_table course TABLE teacher TABLE
  123. 123. JOIN TABLES Course.arel_table Teacher.arel_table course TABLE teacher TABLE courses_teachers TABLE
  124. 124. JOIN TABLES Course.arel_table Teacher.arel_table course TABLE teacher TABLE ???? (no model class) courses_teachers TABLE
  125. 125. JOIN TABLES
  126. 126. JOIN TABLES ct = Arel::Table.new(:courses_teachers)
  127. 127. JOIN TABLES ct = Arel::Table.new(:courses_teachers) Course .joins( Course.arel_table.join(Teacher.arel_table) .on(Course[:id].eq(ct[:course_id])) .and(Teacher[:id].eq(ct[:teacher_id])) .join_sources ) .to_sql
  128. 128. THE OPULENCE OF “ORDER”
  129. 129. ORDER Post.order(:visitors).to_sql => SELECT `posts`.* FROM `posts` ORDER BY visitors
  130. 130. ORDER Post.order(:visitors).to_sql => SELECT `posts`.* FROM `posts` ORDER BY visitors Post.order(:views).reverse_order.to_sql => SELECT `posts`.* FROM `posts` ORDER BY visitors DESC
  131. 131. ORDER Post.order(:visitors).to_sql => SELECT `posts`.* FROM `posts` ORDER BY visitors Post.order(:views).reverse_order.to_sql => SELECT `posts`.* FROM `posts` ORDER BY visitors DESC Post.order(Post[:views].desc).to_sql => SELECT `posts`.* FROM `posts` ORDER BY visitors DESC
  132. 132. SUBQUERIES WITH “IN”
  133. 133. IN Post.where( Post.arel_table[:title].in( Post.select(:title).where(id: 5).ast ) ).to_sql => SELECT `phrases`.* FROM `phrases` WHERE `phrases`.`title` IN ( SELECT title FROM `phrases` WHERE `phrases`.`id` = 5 )
  134. 134. LIKE QUERIES WITH “MATCHES”
  135. 135. MATCHES Post.where(Post[:title].matches("%arel%")).to_sql => SELECT `phrases`.* FROM `phrases` WHERE (`phrases`.`key` LIKE x'256172656c25')
  136. 136. QUERY BUILDERS
  137. 137. class QueryBuilder extend Forwardable attr_reader :query def_delegators :@query, :to_a, :to_sql, :each def initialize(query) @query = query end protected def reflect(query) self.class.new(query) end end
  138. 138. class PostQueryBuilder < QueryBuilder def initialize(query = nil) super(query || post.unscoped) end def with_title_matching(title) reflect( query.where(post[:title].matches("%#{title}%")) ) end def with_comments_by(usernames) reflect( query .joins(:comments => :author) .where(Author[:username].in(usernames)) ) end def since_yesterday reflect( query.where(post[:created_at].gteq(Date.yesterday)) ) end private def author Author end def post Post end end
  139. 139. class PostQueryBuilder < QueryBuilder def initialize(query = nil) super(query || post.unscoped) end def with_title_matching(title) reflect( query.where(post[:title].matches("%#{title}%")) ) end def with_comments_by(usernames) reflect( query .joins(:comments => :author) .where(Author[:username].in(usernames)) ) end def since_yesterday reflect( query.where(post[:created_at].gteq(Date.yesterday)) ) end private def author Author end def post Post end end
  140. 140. def with_title_matching(title) reflect( query.where(post[:title].matches("%#{title}%")) ) end
  141. 141. class PostQueryBuilder < QueryBuilder def initialize(query = nil) super(query || post.unscoped) end def with_title_matching(title) reflect( query.where(post[:title].matches("%#{title}%")) ) end def with_comments_by(usernames) reflect( query .joins(:comments => :author) .where(Author[:username].in(usernames)) ) end def since_yesterday reflect( query.where(post[:created_at].gteq(Date.yesterday)) ) end private def author Author end def post Post end end
  142. 142. class PostQueryBuilder < QueryBuilder def initialize(query = nil) super(query || post.unscoped) end def with_title_matching(title) reflect( query.where(post[:title].matches("%#{title}%")) ) end def with_comments_by(usernames) reflect( query .joins(:comments => :author) .where(Author[:username].in(usernames)) ) end def since_yesterday reflect( query.where(post[:created_at].gteq(Date.yesterday)) ) end private def author Author end def post Post end end
  143. 143. def with_comments_by(usernames) reflect( query .joins(:comments => :author) .where(Author[:username].in(usernames)) ) end
  144. 144. class PostQueryBuilder < QueryBuilder def initialize(query = nil) super(query || post.unscoped) end def with_title_matching(title) reflect( query.where(post[:title].matches("%#{title}%")) ) end def with_comments_by(usernames) reflect( query .joins(:comments => :author) .where(Author[:username].in(usernames)) ) end def since_yesterday reflect( query.where(post[:created_at].gteq(Date.yesterday)) ) end private def author Author end def post Post end end
  145. 145. class PostQueryBuilder < QueryBuilder def initialize(query = nil) super(query || post.unscoped) end def with_title_matching(title) reflect( query.where(post[:title].matches("%#{title}%")) ) end def with_comments_by(usernames) reflect( query .joins(:comments => :author) .where(Author[:username].in(usernames)) ) end def since_yesterday reflect( query.where(post[:created_at].gteq(Date.yesterday)) ) end private def author Author end def post Post end end
  146. 146. def since_yesterday reflect( query.where( post[:created_at].gteq(Date.yesterday) ) ) end
  147. 147. EXAMPLES PostQueryBuilder.new .with_comments_by(['camertron', 'catwithtail']) .to_sql => SELECT `posts`.* FROM `posts` INNER JOIN `comments` ON `comments`.`post_id` = `posts`.`id` INNER JOIN `authors` ON `authors`.`comment_id` = `comments`.`id` WHERE `authors`.`username` IN ( 'camertron', 'catwithtail' )
  148. 148. EXAMPLES PostQueryBuilder.new .with_comments_by(['camertron', 'catwithtail']) .with_title_matching("arel").to_sql => SELECT `posts`.* FROM `posts` INNER JOIN `comments` ON `comments`.`post_id` = `posts`.`id` INNER JOIN `authors` ON `authors`.`comment_id` = `comments`.`id` WHERE `authors`.`username` IN ( 'camertron', 'catwithtail' ) AND (`posts`.`title` LIKE '%arel%')
  149. 149. EXAMPLES PostQueryBuilder.new .with_comments_by(['camertron', 'catwithtail']) .with_title_matching(`arel`).since_yesterday.to_sql => SELECT `posts`.* FROM `posts` INNER JOIN `comments` ON `comments`.`post_id` = `posts`.`id` INNER JOIN `authors` ON `authors`.`comment_id` = `comments`.`id` WHERE `authors`.`username` IN ( 'camertron', 'catwithtail' ) AND (`posts`.`title` LIKE '%arel%') AND (`posts`.`created_at` >= '2014-04-20')
  150. 150. Sigh... this is all so complicated. Surely there’s a tool out there...“ ”
  151. 151. SCUTTLE www.scuttle.io
  152. 152. SCUTTLE sql-parser (java) http://github.com/camertron/sql-parser scuttle-rb (jRuby gem) http://github.com/camertron/scuttle-rb scuttle-server (sinatra jRuby) http://github.com/camertron/scuttle-server
  153. 153. THANK YOU!

×