Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

belongs_to causes unnecessary db hits #1100

Closed
wants to merge 1 commit into from

Conversation

ssendev
Copy link
Contributor

@ssendev ssendev commented Aug 29, 2015

failing test for #762

also fails with Adapter::Json but i'm not sure what the expected hash would look like (embed ids appears to have no effect with json adapter blog name still appears in the resulting hash (was this changed in 0.10?)).

@@ -145,6 +145,24 @@ def test_associations_custom_keys
assert expected_association_keys.include? :writer
assert expected_association_keys.include? :site
end

def test_belongs_to_dosnt_load_record
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo: doesnt

@bf4
Copy link
Member

bf4 commented Aug 30, 2015

👍 Nice

@ssendev ssendev force-pushed the extra-belongs-to-db-hit branch from 78adc34 to 76722f2 Compare August 30, 2015 08:27
end
end

post_serializer = Class.new(ActiveModel::Serializer) do
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would call the class PostSerializer instead of post_serializer (although I know you took the code from somewhere else, but I think this is more 'standard').
Also, I would write it like that:

class PostSerializer < ActiveModel::Serializer
  belongs_to :blog, embed: :ids
end

as I find it less confusing, but here people might disagree.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tried changing it but it seems like this notation is not accepted in a method.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very true indeed, I overlooked that part.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could

def test_something
  post_serializer = Class.new(ActiveModel::Serializer)
  Object.const_set(:PostSerializer, post_serializer)
  PostSerializer.whatever
ensure
    Object.send(:remove_const, :PostSerializer)
end

e.g. see

def test_get_adapter_from_environment_registers_adapter
ActiveModel::Serializer::Adapter.const_set(:AdapterFromEnvironment, Class.new)
klass = ::ActiveModel::Serializer::Adapter::AdapterFromEnvironment
name = "adapter_from_environment".freeze
assert_equal ActiveModel::Serializer::Adapter.get(name), klass
assert ActiveModel::Serializer::Adapter.adapters.include?(name)
ensure
ActiveModel::Serializer::Adapter::ADAPTER_MAP.delete(name)
ActiveModel::Serializer::Adapter.send(:remove_const, :AdapterFromEnvironment)
end
def test_get_adapter_for_unknown_name
assert_raises UnknownAdapterError do
ActiveModel::Serializer::Adapter.get(:json_simple)
end
end
def test_adapter
assert_equal ActiveModel::Serializer.config.adapter, :flatten_json
assert_equal ActiveModel::Serializer.adapter, ActiveModel::Serializer::Adapter::FlattenJson
end
def test_register_adapter
new_adapter_name = :foo
new_adapter_klass = Class.new
ActiveModel::Serializer::Adapter.register(new_adapter_name, new_adapter_klass)
assert ActiveModel::Serializer::Adapter.adapters.include?("foo".freeze)
assert ActiveModel::Serializer::Adapter.get(:foo), new_adapter_klass
ensure
ActiveModel::Serializer::Adapter::ADAPTER_MAP.delete(new_adapter_name.to_s)
end
def test_inherited_adapter_hooks_register_adapter
Object.const_set(:MyAdapter, Class.new)
my_adapter = MyAdapter
ActiveModel::Serializer::Adapter.inherited(my_adapter)
assert_equal ActiveModel::Serializer::Adapter.get(:my_adapter), my_adapter
ensure
ActiveModel::Serializer::Adapter::ADAPTER_MAP.delete("my_adapter".freeze)
Object.send(:remove_const, :MyAdapter)
end
def test_inherited_adapter_hooks_register_demodulized_adapter
Object.const_set(:MyNamespace, Module.new)
MyNamespace.const_set(:MyAdapter, Class.new)
my_adapter = MyNamespace::MyAdapter
ActiveModel::Serializer::Adapter.inherited(my_adapter)
assert_equal ActiveModel::Serializer::Adapter.get(:my_adapter), my_adapter
ensure
ActiveModel::Serializer::Adapter::ADAPTER_MAP.delete("my_adapter".freeze)
MyNamespace.send(:remove_const, :MyAdapter)
Object.send(:remove_const, :MyNamespace)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

very interesting. but in the referenced test it's required since that's what's being tested. here it seems like it would just make the test more complex for "no" benefit. especially considering there already is a PostSerializer which would have to be restored.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm okay with it.j

@ssendev ssendev force-pushed the extra-belongs-to-db-hit branch from 76722f2 to 1141f4b Compare August 30, 2015 22:47
@beauby
Copy link
Contributor

beauby commented Aug 30, 2015

After giving it a thought, I am not sure we can avoid this "unnecessary db hit". What happens if we have belongs_to :author in the serializer, and belongs_to :author, class_name: "User" in the model? Then I fear AMS wouldn't know how to set the type property of the Resource Identifier.

@ssendev
Copy link
Contributor Author

ssendev commented Aug 30, 2015

belongs_to :author, type: "users" comes to mind. i mean for attributes there's key

@beauby
Copy link
Contributor

beauby commented Aug 31, 2015

key for attributes is something different: it exposes an existing attribute under a different name (in the serialized document).
I have a seemless fix for that extra db hit, I just need #1103 to be merged first.

996
end
def blog
fail "should use blog_id"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

haha nice trick! 👍

@joaomdmoura
Copy link
Member

Thank you @ssendev this is indeed something that we should address, we should not do unnecessary queries or anything that might slow down the apps performance.

As @beauby this might be a little bit tricky because of the example:
belongs_to :author, class_name: "User"

What you suggest might be a way to make it work belongs_to :author, type: "users" but I think we can try to discuss and come up with a more elegant solution. @beauby can you open a [WIP] PR with your solution, meanwhile I'll check #1103

@ssendev
Copy link
Contributor Author

ssendev commented Sep 5, 2015

an alternative would be to specify a serializer and require json_api_type to be defined on it, or guess it from the serializers name . both solutions wouldn't be able to handle polymorphic relations but then there is probably an author_type column which could be consulted.

it would probably also be possible to to inspect the relationship on the model object.association(:author).reflection.class_name

@sandstrom
Copy link

This would be a godsend. Currently using AMS and need manual work-arounds to avoid these costly DB-lookups.

@ssendev has a great point, simply use reflection to inspect the relationships of the model. Specifying a type manually would be rare and only needed for polymorphic relationships.

I don't use ActiveRecord, so not 100% sure but I think Author.reflect_on_association(:books).klass works[1]. Mongoid, another popular ORM, also has good reflection support.

[1] https://github.com/rails/rails/blob/master/activerecord/lib/active_record/reflection.rb

@bf4
Copy link
Member

bf4 commented Jan 29, 2016

So sorry we lost track of this!

@NullVoxPopuli
Copy link
Contributor

needs rebase, I'd like to get this merged soonish

@ssendev ssendev force-pushed the extra-belongs-to-db-hit branch 4 times, most recently from 20bcc8f to b4c49cd Compare May 25, 2016 10:20
@ssendev ssendev force-pushed the extra-belongs-to-db-hit branch from b4c49cd to b021388 Compare May 25, 2016 10:48
@ssendev
Copy link
Contributor Author

ssendev commented May 25, 2016

this fails with

-{:data=>{:id=>"post", :type=>"posts", :relationships=>{:blog=>{:data=>{:id=>"blog", :type=>"blogs"}}}}}
+{:data=>{:id=>"post", :type=>"posts", :relationships=>{:blog=>{:data=>nil}}}}

is this expected or am i missing something?

@NullVoxPopuli
Copy link
Contributor

@ssendev is that a conflict in the rebase? or is that the result of a test? (I guess I don't know what context that is in)

@ssendev
Copy link
Contributor Author

ssendev commented May 25, 2016

@NullVoxPopuli
Copy link
Contributor

it is yeah.

my initial hunch is that something is off, because specifying a relationship with a data of nil seems weird. I'm going to check the json api spec, and see what is says about unspecified relationships.

It could be that we want to just omit that relationship entry.

@ssendev
Copy link
Contributor Author

ssendev commented May 25, 2016

but the relationship is specified it just doesn't get serialized

@NullVoxPopuli
Copy link
Contributor

ah, yes, I was looking at the wrong test.

@NullVoxPopuli
Copy link
Contributor

I think I was confused earlier, and thought this was a fix for the problem. ha. I should not be on github when I'm tired.

@NullVoxPopuli
Copy link
Contributor

anywho, I'm looking at fixing this

@NullVoxPopuli
Copy link
Contributor

Closing in favor of #1750, as I added some notes in the code about what's happening where.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants