Skip to content

Commit b8672ea

Browse files
authored
Merge pull request #5331 from rmosolgo/source-has-many
Support has_many in ActiveRecordAssociationSource
2 parents 63193d8 + 30fa82c commit b8672ea

File tree

3 files changed

+66
-7
lines changed

3 files changed

+66
-7
lines changed

lib/graphql/dataloader/active_record_association_source.rb

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,14 @@ def initialize(association, scope = nil)
1212
@scope = scope
1313
end
1414

15+
def self.batch_key_for(association, scope = nil)
16+
if scope
17+
[association, scope.to_sql]
18+
else
19+
[association]
20+
end
21+
end
22+
1523
def load(record)
1624
if (assoc = record.association(@association)).loaded?
1725
assoc.target
@@ -41,17 +49,17 @@ def fetch(records)
4149
::ActiveRecord::Associations::Preloader.new(records: records, associations: @association, available_records: available_records, scope: @scope).call
4250

4351
loaded_associated_records = records.map { |r| r.public_send(@association) }
44-
records_by_model = {}
45-
loaded_associated_records.each do |record|
46-
if record
47-
updates = records_by_model[record.class] ||= {}
48-
updates[record.id] = record
49-
end
50-
end
5152

5253
if @scope.nil?
5354
# Don't cache records loaded via scope because they might have reduced `SELECT`s
5455
# Could check .select_values here?
56+
records_by_model = {}
57+
loaded_associated_records.flatten.each do |record|
58+
if record
59+
updates = records_by_model[record.class] ||= {}
60+
updates[record.id] = record
61+
end
62+
end
5563
records_by_model.each do |model_class, updates|
5664
dataloader.with(RECORD_SOURCE_CLASS, model_class).merge(updates)
5765
end

spec/graphql/dataloader/active_record_association_source_spec.rb

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,56 @@
106106
assert_equal ::Band.find(1), vulfpeck
107107
end
108108

109+
it_dataloads "works with collection associations" do |d|
110+
wilco = ::Band.find(4)
111+
chon = ::Band.find(3)
112+
albums_by_band = nil
113+
log = with_active_record_log(colorize: false) do
114+
albums_by_band = d.with(GraphQL::Dataloader::ActiveRecordAssociationSource, :albums).load_all([wilco, chon])
115+
end
116+
117+
assert_equal [[6], [4, 5]], albums_by_band.map { |al| al.map(&:id) }
118+
assert_includes log, 'SELECT "albums".* FROM "albums" WHERE "albums"."band_id" IN (?, ?) [["band_id", 4], ["band_id", 3]]'
119+
120+
albums = nil
121+
log = with_active_record_log(colorize: false) do
122+
albums = d.with(GraphQL::Dataloader::ActiveRecordSource, Album).load_all([3,4,5,6])
123+
end
124+
125+
assert_equal [3,4,5,6], albums.map(&:id)
126+
assert_includes log, 'WHERE "albums"."id" = ? [["id", 3]]'
127+
end
128+
129+
it_dataloads "works with collection associations with scope" do |d|
130+
wilco = ::Band.find(4)
131+
chon = ::Band.find(3)
132+
albums_by_band = nil
133+
one_month_ago = nil
134+
log = with_active_record_log(colorize: false) do
135+
one_month_ago = 1.month.ago.end_of_day
136+
albums_by_band_1 = d.with(GraphQL::Dataloader::ActiveRecordAssociationSource, :albums, Album.where("created_at >= ?", one_month_ago)).request(wilco)
137+
albums_by_band_2 = d.with(GraphQL::Dataloader::ActiveRecordAssociationSource, :albums, Album.where("created_at >= ?", one_month_ago)).request(chon)
138+
albums_by_band = [albums_by_band_1.load, albums_by_band_2.load]
139+
end
140+
141+
assert_equal [[6], [4, 5]], albums_by_band.map { |al| al.map(&:id) }
142+
expected_log = if Rails::VERSION::STRING > "8"
143+
'SELECT "albums".* FROM "albums" WHERE (created_at >= ?) AND "albums"."band_id" IN (?, ?)'
144+
else
145+
'SELECT "albums".* FROM "albums" WHERE (created_at >= ' + one_month_ago.utc.strftime("'%Y-%m-%d %H:%M:%S.%6N'") + ') AND "albums"."band_id" IN (?, ?)'
146+
end
147+
148+
assert_includes log, expected_log
149+
150+
albums = nil
151+
log = with_active_record_log(colorize: false) do
152+
albums = d.with(GraphQL::Dataloader::ActiveRecordSource, Album).load_all([3,4,5,6])
153+
end
154+
155+
assert_equal [3,4,5,6], albums.map(&:id)
156+
assert_includes log, 'WHERE "albums"."id" IN (?, ?, ?, ?) [["id", 3], ["id", 4], ["id", 5], ["id", 6]]'
157+
end
158+
109159
if Rails::VERSION::STRING > "7.1" # not supported in <7.1
110160
it_dataloads "loads with composite primary keys and warms the cache" do |d|
111161
my_first_car = ::Album.find(2)

spec/support/active_record_setup.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@
7070
t.integer :band_id
7171
t.string :band_name
7272
t.integer :band_genre
73+
t.timestamps
7374
end
7475

7576
create_table :books do |t|

0 commit comments

Comments
 (0)