Module: GraphQL::Analysis::AST::QueryComplexity::ComplexityMergeFunctions
- Defined in:
- lib/graphql/analysis/ast/query_complexity.rb
Overview
These functions use ScopedTypeComplexity
objects,
especially their scoped_children
, to traverse down the tree
and find the max complexity for any possible runtime type.
Yowza.
Class Method Summary collapse
-
.applies_to?(query, left_scope, right_scope) ⇒ Boolean
When looking at two selection scopes, figure out whether the selections on
right_scope
should be applied when analyzingleft_scope
. -
.merged_max_complexity(query, children_for_scope) ⇒ Integer
Complexity value for all these selections in the current scope.
-
.merged_max_complexity_for_scopes(query, scoped_children_hashes) ⇒ Object
Class Method Details
.applies_to?(query, left_scope, right_scope) ⇒ Boolean
When looking at two selection scopes, figure out whether the selections on
right_scope
should be applied when analyzing left_scope
.
This is like the Typecast.subtype?
, except it’s using query-specific type filtering.
111 112 113 114 115 116 117 118 119 120 121 |
# File 'lib/graphql/analysis/ast/query_complexity.rb', line 111 def applies_to?(query, left_scope, right_scope) if left_scope == right_scope # This can happen when several branches are being analyzed together true else # Check if these two scopes have _any_ types in common. possible_right_types = query.possible_types(right_scope) possible_left_types = query.possible_types(left_scope) !(possible_right_types & possible_left_types).empty? end end |
.merged_max_complexity(query, children_for_scope) ⇒ Integer
Returns Complexity value for all these selections in the current scope.
181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 |
# File 'lib/graphql/analysis/ast/query_complexity.rb', line 181 def merged_max_complexity(query, children_for_scope) all_keys = [] children_for_scope.each do |c| all_keys.concat(c.keys) end all_keys.uniq! complexity_for_keys = {} all_keys.each do |child_key| scoped_children_for_key = nil complexity_for_key = nil children_for_scope.each do |children_hash| if children_hash.key?(child_key) complexity_for_key = children_hash[child_key] if complexity_for_key.terminal? # Assume that all terminals would return the same complexity # Since it's a terminal, its child complexity is zero. complexity_for_key = complexity_for_key.own_complexity(0) complexity_for_keys[child_key] = complexity_for_key else scoped_children_for_key ||= [] scoped_children_for_key << complexity_for_key.scoped_children end end end if scoped_children_for_key child_complexity = merged_max_complexity_for_scopes(query, scoped_children_for_key) # This is the _last_ one we visited; assume it's representative. max_complexity = complexity_for_key.own_complexity(child_complexity) complexity_for_keys[child_key] = max_complexity end end # Calculate the child complexity by summing the complexity of all selections complexity_for_keys.each_value.inject(0, &:+) end |
.merged_max_complexity_for_scopes(query, scoped_children_hashes) ⇒ Object
123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 |
# File 'lib/graphql/analysis/ast/query_complexity.rb', line 123 def merged_max_complexity_for_scopes(query, scoped_children_hashes) # Figure out what scopes are possible here. # Use a hash, but ignore the values; it's just a fast way to work with the keys. all_scopes = {} scoped_children_hashes.each do |h| all_scopes.merge!(h) end # If an abstract scope is present, but _all_ of its concrete types # are also in the list, remove it from the list of scopes to check, # because every possible type is covered by a concrete type. # (That is, there are no remainder types to check.) # # TODO redocument prev_keys = all_scopes.keys prev_keys.each do |scope| if scope.kind.abstract? missing_concrete_types = query.possible_types(scope).select { |t| !all_scopes.key?(t) } # This concrete type is possible _only_ as a member of the abstract type. # So, attribute to it the complexity which belongs to the abstract type. missing_concrete_types.each do |concrete_scope| all_scopes[concrete_scope] = all_scopes[scope] end all_scopes.delete(scope) end end # This will hold `{ type => int }` pairs, one for each possible branch complexity_by_scope = {} # For each scope, # find the lexical selections that might apply to it, # and gather them together into an array. # Then, treat the set of selection hashes # as a set and calculate the complexity for them as a unit all_scopes.each do |scope, _| # These will be the selections on `scope` children_for_scope = [] scoped_children_hashes.each do |sc_h| sc_h.each do |inner_scope, children_hash| if applies_to?(query, scope, inner_scope) children_for_scope << children_hash end end end # Calculate the complexity for `scope`, merging all # possible lexical branches. complexity_value = merged_max_complexity(query, children_for_scope) complexity_by_scope[scope] = complexity_value end # Return the max complexity among all scopes complexity_by_scope.each_value.max end |