Class: GraphQL::Models::RelationLoader
- Inherits:
-
Batch::Loader
- Object
- Batch::Loader
- GraphQL::Models::RelationLoader
- Defined in:
- lib/graphql/models/relation_loader.rb
Instance Attribute Summary collapse
-
#model_class ⇒ Object
readonly
Returns the value of attribute model_class.
Instance Method Summary collapse
- #fulfill_request(request, result) ⇒ Object
-
#initialize(model_class) ⇒ RelationLoader
constructor
A new instance of RelationLoader.
- #perform(load_requests) ⇒ Object
Constructor Details
#initialize(model_class) ⇒ RelationLoader
Returns a new instance of RelationLoader.
7 8 9 |
# File 'lib/graphql/models/relation_loader.rb', line 7 def initialize(model_class) @model_class = model_class end |
Instance Attribute Details
#model_class ⇒ Object (readonly)
Returns the value of attribute model_class.
5 6 7 |
# File 'lib/graphql/models/relation_loader.rb', line 5 def model_class @model_class end |
Instance Method Details
#fulfill_request(request, result) ⇒ Object
97 98 99 100 101 |
# File 'lib/graphql/models/relation_loader.rb', line 97 def fulfill_request(request, result) result = request.ensure_cardinality(result) request.fulfilled(result) fulfill(request, result) end |
#perform(load_requests) ⇒ Object
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 |
# File 'lib/graphql/models/relation_loader.rb', line 11 def perform(load_requests) # Group the requests to load by id into a single relation, and we'll fan it back out after # we have the results relations = [] id_requests = load_requests.select { |r| r.load_type == :id } id_relation = model_class.where(id: id_requests.map(&:load_target)) relations.push(id_relation) if id_requests.any? relations_to_requests = {} # Gather up all of the requests to load a relation, and map the relations back to their requests load_requests.select { |r| r.load_type == :relation }.each do |request| relation = request.load_target relations.push(relation) unless relations.detect { |r| r.object_id == relation.object_id } relations_to_requests[relation.object_id] ||= [] relations_to_requests[relation.object_id].push(request) end # We need to build a query that will return all of the rows that match any of the relations. # But in addition, we also need to know how that relation sorted them. So we pull any ordering # information by adding RANK() columns to the query, and we determine whether that row belonged # to the query by adding CASE columns. # Map each relation to a SQL query that selects only the ID column for the rows that match it selection_clauses = relations.map do |relation| relation.unscope(:select).select(model_class.primary_key).to_sql end # Generate a CASE column that will tell us whether the row matches this particular relation slicing_columns = relations.each_with_index.map do |_relation, index| %{ CASE WHEN "#{model_class.table_name}"."#{model_class.primary_key}" IN (#{selection_clauses[index]}) THEN 1 ELSE 0 END AS "in_relation_#{index}" } end # For relations that have sorting applied, generate a RANK() column that tells us how the rows are # sorted within that relation sorting_columns = relations.each_with_index.map do |relation, index| arel = relation.arel next nil unless arel.orders.any? order_by = arel.orders.map do |expr| if expr.is_a?(Arel::Nodes::SqlLiteral) expr.to_s else expr.to_sql end end %{ RANK() OVER (ORDER BY #{order_by.join(', ')}) AS "sort_relation_#{index}" } end sorting_columns.compact! # Build the query that will select any of the rows that match the selection clauses main_relation = model_class .where("id in ((#{selection_clauses.join(") UNION (")}))") .select(%( "#{model_class.table_name}".* )) main_relation = slicing_columns.reduce(main_relation) { |relation, memo| relation.select(memo) } main_relation = sorting_columns.reduce(main_relation) { |relation, memo| relation.select(memo) } # Run the query result = main_relation.to_a # Now multiplex the results out to all of the relations that had asked for values relations.each_with_index do |relation, index| slice_col = "in_relation_#{index}" sort_col = "sort_relation_#{index}" matching_rows = result.select { |r| r[slice_col] == 1 }.sort_by { |r| r[sort_col] } if relation.object_id == id_relation.object_id pk = relation.klass.primary_key id_requests.each do |request| row = matching_rows.detect { |r| r[pk] == request.load_target } fulfill(request, row) end else relations_to_requests[relation.object_id].each do |request| fulfill_request(request, matching_rows) end end end end |