-
Notifications
You must be signed in to change notification settings - Fork 0
/
demo.rb
executable file
·134 lines (107 loc) · 3.19 KB
/
demo.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
#!/usr/bin/env ruby
# frozen_string_literal: true
require 'active_record'
require 'logger'
require 'sqlite3'
require 'query_count'
require_relative 'setup'
# Something to note: ActiveRecord is typically used to
# retrieve whole objects, not just attributes. This is
# because it is an ORM (Object Relational Mapper).
# This is not always efficient.
# Define post model
class Post < ApplicationRecord
has_many :comments
# Page 5, the `.last` executes the query,
# so it's not efficient. This doesn't matter
# whether there is ain `includes` to the left,
# it still produces n+1 queries.
def latest_comment
comments.order(:created_at).last
end
end
# Define comment model
class Comment < ApplicationRecord
belongs_to :post
end
# Seeding Data
def seed_one_post
post = Post.create(title: 'Example Post 1')
id = post.id
post.comments.create(body: 'This is a comment')
post.comments.create(body: 'This is another comment')
(1..10).each do |i|
post.comments.create(body: "Comment #{i}")
end
id
end
def seed_two_posts
count = 1
post = Post.create(title: 'Example Post 1')
# id = post.id
# post.acomments.create(body: 'This is a comment')
# post.comments.create(body: 'This is another comment')
(0..count).each do |i|
post.comments.create(body: "Comment #{i}")
end
post = Post.create(title: 'Example Post 2')
(0..count).each do |i|
post.comments.create(body: "Comment #{i}")
end
end
def seed(post_count:, comment_count:)
(1..post_count).each do |i|
post = Post.create(title: "Example Post #{i}")
(1..comment_count).each do |j|
post.comments.create(body: "Comment #{j}")
end
end
end
def with_includes
# Querying Data
post_id = post.id # Use the ID of the post you want to retrieve
# This is 2 database calls.
# queried_post = Post.includes(:comments).find(post_id)
queried_post = Post.eager_load(:comments).find(post_id)
# binding.irb
# This doesn't work because the comments are not preloaded.
# queried_post = Post.find(post_id).includes(:comments)
puts "Post: #{queried_post.title}"
queried_post.comments.each do |comment|
puts " - Comment: #{comment.body}"
end
rescue ActiveRecord::RecordNotFound
puts "Post with id #{post_id} not found."
end
def without_includes(post_id)
queried_post = Post.find(post_id)
# binding.irb
# This doesn't work because the comments are not preloaded.
# queried_post = Post.find(post_id)
puts "Post: #{queried_post.title}"
queried_post.comments.each do |comment|
puts " - Comment: #{comment.body}"
end
rescue ActiveRecord::RecordNotFound
puts "Post with id #{post_id} not found."
end
# This does not do n+1
# post_id = seed_one_post
# without_includes(post_id)
# post_id = seed_two_posts
# Run this ./demo > tmp/out.txt
# Check with grep "SELECT" tmp/out.txt | wc -l
# Post.limit(1000).find_each do |post|
# post.comments.each do |comment|
# puts comment.body
# end
# end
# Page 4.
seed(post_count: 1, comment_count: 1)
ActiveRecord::Base.logger = Logger.new($stdout)
# The `includes` is ignored here.
Post.includes(:comments).find_each do |post|
# But here rails will "ignore" your includes and run a query
# for each post (n+1 queries)
puts post.latest_comment
end