diff --git a/jsonapi-resources.gemspec b/jsonapi-resources.gemspec index 65372c9f..f6b2f8af 100644 --- a/jsonapi-resources.gemspec +++ b/jsonapi-resources.gemspec @@ -28,6 +28,7 @@ Gem::Specification.new do |spec| spec.add_development_dependency 'concurrent-ruby-ext' spec.add_development_dependency 'database_cleaner' spec.add_development_dependency 'hashie' + spec.add_development_dependency 'sorted_set' spec.add_dependency 'activerecord', '>= 5.1' spec.add_dependency 'railties', '>= 5.1' spec.add_dependency 'concurrent-ruby' diff --git a/lib/jsonapi/active_relation_retrieval.rb b/lib/jsonapi/active_relation_retrieval.rb index 1e1a72fb..f331bd59 100644 --- a/lib/jsonapi/active_relation_retrieval.rb +++ b/lib/jsonapi/active_relation_retrieval.rb @@ -271,7 +271,7 @@ def find_related_fragments(source_fragment, relationship, options = {}) source_resource_klasses.each do |resource_klass| inverse_direct_relationship = _relationship(resource_klass._type.to_s.singularize) - fragments.merge!(resource_klass.find_related_fragments_from_inverse([source_fragment], inverse_direct_relationship, options, true)) + fragments.merge!(resource_klass.find_related_fragments_from_inverse([source_fragment], inverse_direct_relationship, options, false)) end fragments else @@ -317,9 +317,16 @@ def find_related_fragments_from_inverse(source, source_relationship, options, co linkage_relationships = to_one_relationships_for_linkage(include_directives[:include_related]) sort_criteria = [] - options[:sort_criteria].try(:each) do |sort| - field = sort[:field].to_s == 'id' ? _primary_key : sort[:field] - sort_criteria << { field: field, direction: sort[:direction] } + + # Do not sort the related_fragments. This can be keyed off `connect_source_identity` to indicate whether this + # is a related resource primary step vs. an include step. + sort_related_fragments = !connect_source_identity + + if sort_related_fragments + options[:sort_criteria].try(:each) do |sort| + field = sort[:field].to_s == 'id' ? _primary_key : sort[:field] + sort_criteria << { field: field, direction: sort[:direction] } + end end join_manager = ActiveRelation::JoinManager.new(resource_klass: self, diff --git a/lib/jsonapi/configuration.rb b/lib/jsonapi/configuration.rb index 1d2c235e..b9f4b772 100644 --- a/lib/jsonapi/configuration.rb +++ b/lib/jsonapi/configuration.rb @@ -43,7 +43,8 @@ class Configuration :resource_cache_usage_report_function, :default_exclude_links, :default_resource_retrieval_strategy, - :use_related_resource_records_for_joins + :use_related_resource_records_for_joins, + :related_identities_set def initialize #:underscored_key, :camelized_key, :dasherized_key, or custom @@ -182,6 +183,13 @@ def initialize # This setting allows included resources to account for permission scopes. It can be overridden explicitly per # relationship. Furthermore, specifying a `relation_name` on a relationship will cause this setting to be ignored. self.use_related_resource_records_for_joins = true + + # Collect the include keys into a Set or a SortedSet. SortedSet carries a small performance cost in the rails app + # but produces consistent and more human navigable result sets. + # To use SortedSet be sure to add `sorted_set` to your Gemfile and the following two lines to your JR initializer: + # require 'sorted_set' + # config.related_identities_set = SortedSet + self.related_identities_set = Set end def cache_formatters=(bool) @@ -327,6 +335,8 @@ def allow_include=(allow_include) attr_writer :default_resource_retrieval_strategy attr_writer :use_related_resource_records_for_joins + + attr_writer :related_identities_set end class << self diff --git a/lib/jsonapi/resource_fragment.rb b/lib/jsonapi/resource_fragment.rb index c354f9aa..0149f1ee 100644 --- a/lib/jsonapi/resource_fragment.rb +++ b/lib/jsonapi/resource_fragment.rb @@ -25,7 +25,7 @@ def initialize(identity, resource: nil, cache: nil, primary: false) @primary = primary @related = {} - @related_from = Set.new + @related_from = JSONAPI.configuration.related_identities_set.new end def initialize_related(relationship_name) diff --git a/lib/jsonapi/resource_set.rb b/lib/jsonapi/resource_set.rb index f4d6186f..e5846994 100644 --- a/lib/jsonapi/resource_set.rb +++ b/lib/jsonapi/resource_set.rb @@ -180,7 +180,7 @@ def flatten_resource_tree(resource_tree, flattened_tree = {}) flattened_tree[resource_klass][id][:resource] ||= fragment.resource if fragment.resource fragment.related.try(:each_pair) do |relationship_name, related_rids| - flattened_tree[resource_klass][id][:relationships][relationship_name] ||= Set.new + flattened_tree[resource_klass][id][:relationships][relationship_name] ||= JSONAPI.configuration.related_identities_set.new flattened_tree[resource_klass][id][:relationships][relationship_name].merge(related_rids) end end diff --git a/test/test_helper.rb b/test/test_helper.rb index 834c8577..b92d8428 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -39,6 +39,9 @@ JSONAPI.configure do |config| config.json_key_format = :camelized_key + + require 'sorted_set' + config.related_identities_set = SortedSet end ActiveSupport::Deprecation.silenced = true