|
3 | 3 | module JSONAPI
|
4 | 4 | module ActiveRelationRetrieval
|
5 | 5 | include ::JSONAPI::RelationRetrieval
|
| 6 | + include ::JSONAPI::ActiveRelationRetrieval::FindRelatedThroughPrimary |
6 | 7 |
|
7 | 8 | def find_related_ids(relationship, options)
|
8 | 9 | self.class.find_related_fragments(self, relationship, options).keys.collect { |rid| rid.id }
|
9 | 10 | end
|
10 | 11 |
|
11 | 12 | module ClassMethods
|
| 13 | + include JSONAPI::ActiveRelationRetrieval::FindRelatedThroughPrimary::ClassMethods |
| 14 | + |
| 15 | + def default_find_related_through(polymorphic = false) |
| 16 | + if polymorphic |
| 17 | + JSONAPI.configuration.default_find_related_through_polymorphic |
| 18 | + else |
| 19 | + JSONAPI.configuration.default_find_related_through |
| 20 | + end |
| 21 | + end |
| 22 | + |
12 | 23 | # Finds Resources using the `filters`. Pagination and sort options are used when provided
|
13 | 24 | #
|
14 | 25 | # @param filters [Hash] the filters hash
|
@@ -119,6 +130,11 @@ def find_fragments(filters, options)
|
119 | 130 | options: options)
|
120 | 131 |
|
121 | 132 | if options[:cache]
|
| 133 | + # When using caching the a two step process is used. First the records ids are retrieved and then the |
| 134 | + # records are retrieved using the ids. Then the ids are used to query the database again to get the |
| 135 | + # cache misses. In the second phase the records are not sorted or paginated and the `records_for_populate` |
| 136 | + # method is used to ensure any dependent includes or custom database fields are calculated. |
| 137 | + |
122 | 138 | # This alias is going to be resolve down to the model's table name and will not actually be an alias
|
123 | 139 | resource_table_alias = resource_klass._table_name
|
124 | 140 |
|
@@ -189,6 +205,10 @@ def find_fragments(filters, options)
|
189 | 205 | warn "Performance issue detected: `#{self.name.to_s}.records` returned non-normalized results in `#{self.name.to_s}.find_fragments`."
|
190 | 206 | end
|
191 | 207 | else
|
| 208 | + # When not using caching resources can be generated after querying. The `records_for_populate` |
| 209 | + # method is merged in to ensure any dependent includes or custom database fields are calculated. |
| 210 | + records = records.merge(records_for_populate(options)) |
| 211 | + |
192 | 212 | linkage_fields = []
|
193 | 213 |
|
194 | 214 | linkage_relationships.each do |linkage_relationship|
|
@@ -260,50 +280,74 @@ def find_fragments(filters, options)
|
260 | 280 | # the ResourceInstances matching the filters, sorting, and pagination rules along with any request
|
261 | 281 | # additional_field values
|
262 | 282 | def find_related_fragments(source_fragment, relationship, options)
|
263 |
| - if relationship.polymorphic? # && relationship.foreign_key_on == :self |
264 |
| - source_resource_klasses = if relationship.foreign_key_on == :self |
265 |
| - relationship.polymorphic_types.collect do |polymorphic_type| |
266 |
| - resource_klass_for(polymorphic_type) |
| 283 | + case relationship.find_related_through |
| 284 | + when :primary |
| 285 | + if relationship.polymorphic? |
| 286 | + find_related_polymorphic_fragments_through_primary([source_fragment], relationship, options, false) |
| 287 | + else |
| 288 | + find_related_monomorphic_fragments_through_primary([source_fragment], relationship, options, false) |
| 289 | + end |
| 290 | + when :inverse |
| 291 | + if relationship.polymorphic? # && relationship.foreign_key_on == :self |
| 292 | + source_resource_klasses = if relationship.foreign_key_on == :self |
| 293 | + relationship.polymorphic_types.collect do |polymorphic_type| |
| 294 | + resource_klass_for(polymorphic_type) |
| 295 | + end |
| 296 | + else |
| 297 | + source.collect { |fragment| fragment.identity.resource_klass }.to_set |
267 | 298 | end
|
268 |
| - else |
269 |
| - source.collect { |fragment| fragment.identity.resource_klass }.to_set |
270 |
| - end |
271 | 299 |
|
272 |
| - fragments = {} |
273 |
| - source_resource_klasses.each do |resource_klass| |
274 |
| - inverse_direct_relationship = _relationship(resource_klass._type.to_s.singularize) |
| 300 | + fragments = {} |
| 301 | + source_resource_klasses.each do |resource_klass| |
| 302 | + inverse_direct_relationship = _relationship(resource_klass._type.to_s.singularize) |
275 | 303 |
|
276 |
| - fragments.merge!(resource_klass.find_related_fragments_from_inverse([source_fragment], inverse_direct_relationship, options, false)) |
| 304 | + fragments.merge!(resource_klass.find_related_fragments_through_inverse([source_fragment], inverse_direct_relationship, options, false)) |
| 305 | + end |
| 306 | + fragments |
| 307 | + else |
| 308 | + relationship.resource_klass.find_related_fragments_through_inverse([source_fragment], relationship, options, false) |
277 | 309 | end
|
278 |
| - fragments |
279 | 310 | else
|
280 |
| - relationship.resource_klass.find_related_fragments_from_inverse([source_fragment], relationship, options, false) |
| 311 | + raise "Unknown find_related_through: #{relationship.find_related_through}" |
| 312 | + {} |
281 | 313 | end
|
282 | 314 | end
|
283 | 315 |
|
284 | 316 | def find_included_fragments(source_fragments, relationship, options)
|
285 |
| - if relationship.polymorphic? # && relationship.foreign_key_on == :self |
286 |
| - source_resource_klasses = if relationship.foreign_key_on == :self |
287 |
| - relationship.polymorphic_types.collect do |polymorphic_type| |
288 |
| - resource_klass_for(polymorphic_type) |
| 317 | + case relationship.find_related_through |
| 318 | + when :primary |
| 319 | + if relationship.polymorphic? |
| 320 | + find_related_polymorphic_fragments_through_primary(source_fragments, relationship, options, true) |
| 321 | + else |
| 322 | + find_related_monomorphic_fragments_through_primary(source_fragments, relationship, options, true) |
| 323 | + end |
| 324 | + when :inverse |
| 325 | + if relationship.polymorphic? # && relationship.foreign_key_on == :self |
| 326 | + source_resource_klasses = if relationship.foreign_key_on == :self |
| 327 | + relationship.polymorphic_types.collect do |polymorphic_type| |
| 328 | + resource_klass_for(polymorphic_type) |
| 329 | + end |
| 330 | + else |
| 331 | + source.collect { |fragment| fragment.identity.resource_klass }.to_set |
289 | 332 | end
|
290 |
| - else |
291 |
| - source_fragments.collect { |fragment| fragment.identity.resource_klass }.to_set |
292 |
| - end |
293 | 333 |
|
294 |
| - fragments = {} |
295 |
| - source_resource_klasses.each do |resource_klass| |
296 |
| - inverse_direct_relationship = _relationship(resource_klass._type.to_s.singularize) |
| 334 | + fragments = {} |
| 335 | + source_resource_klasses.each do |resource_klass| |
| 336 | + inverse_direct_relationship = _relationship(resource_klass._type.to_s.singularize) |
297 | 337 |
|
298 |
| - fragments.merge!(resource_klass.find_related_fragments_from_inverse(source_fragments, inverse_direct_relationship, options, true)) |
| 338 | + fragments.merge!(resource_klass.find_related_fragments_through_inverse(source_fragments, inverse_direct_relationship, options, true)) |
| 339 | + end |
| 340 | + fragments |
| 341 | + else |
| 342 | + relationship.resource_klass.find_related_fragments_through_inverse(source_fragments, relationship, options, true) |
299 | 343 | end
|
300 |
| - fragments |
301 | 344 | else
|
302 |
| - relationship.resource_klass.find_related_fragments_from_inverse(source_fragments, relationship, options, true) |
| 345 | + raise "Unknown find_related_through: #{relationship.options[:find_related_through]}" |
| 346 | + {} |
303 | 347 | end
|
304 | 348 | end
|
305 | 349 |
|
306 |
| - def find_related_fragments_from_inverse(source, source_relationship, options, connect_source_identity) |
| 350 | + def find_related_fragments_through_inverse(source, source_relationship, options, connect_source_identity) |
307 | 351 | inverse_relationship = source_relationship._inverse_relationship
|
308 | 352 | return {} if inverse_relationship.blank?
|
309 | 353 |
|
@@ -499,10 +543,10 @@ def find_related_fragments_from_inverse(source, source_relationship, options, co
|
499 | 543 | # @return [Integer] the count
|
500 | 544 |
|
501 | 545 | def count_related(source, relationship, options)
|
502 |
| - relationship.resource_klass.count_related_from_inverse(source, relationship, options) |
| 546 | + relationship.resource_klass.count_related_through_inverse(source, relationship, options) |
503 | 547 | end
|
504 | 548 |
|
505 |
| - def count_related_from_inverse(source_resource, source_relationship, options) |
| 549 | + def count_related_through_inverse(source_resource, source_relationship, options) |
506 | 550 | inverse_relationship = source_relationship._inverse_relationship
|
507 | 551 | return -1 if inverse_relationship.blank?
|
508 | 552 |
|
|
0 commit comments