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

merge parent joins for through associations #24682

Closed

Conversation

sevaorlov
Copy link
Contributor

@sevaorlov sevaorlov commented Apr 21, 2016

Summary

Fixes #22538
Merges parent association joins for through associations into a scope. These joins are not taken into account at all right now.

For example direct_appointments join joins(:role) will not be merged for direct_users association and a call for direct_users will fail because direct column will not be found in users table.

class User < ActiveRecord::Base
end

class Role < ActiveRecord::Base
  scope :direct, ->{ where(direct: true) }
end

class Institution < ActiveRecord::Base
  has_many :direct_appointments, ->{ joins(:role).merge(Role.direct) }, 
class_name: 'Appointment'
  has_many :direct_users, through: :direct_appointments, source: :user
end

class Appointment < ActiveRecord::Base
  belongs_to :role
  belongs_to :institution
  belongs_to :user
end

My request adds these joins merging in association scope.

Other Information

I open request to master, but that issue is also present in Rails 4+, do I need to open requests to branches for 4+ versions?

Update

Also fixes #12933

@rails-bot
Copy link

Thanks for the pull request, and welcome! The Rails team is excited to review your changes, and you should hear from @chancancode (or someone else) soon.

If any changes to this PR are deemed necessary, please add them as extra commits. This ensures that the reviewer can see what has changed since they last reviewed the code. Due to the way GitHub handles out-of-date commits, this should also make it reasonably obvious what issues have or haven't been addressed. Large or tricky changes may require several passes of review and changes.

Please see the contribution instructions for more information.

@@ -134,13 +134,13 @@ def add_constraints(scope, owner, association_klass, refl, chain_head, chain_tai
scope = next_chain_scope(scope, table, reflection, association_klass, foreign_table, next_reflection)
end

# Exclude the scope of the association itself, because that
# was already merged in the #scope method.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

outdated comment

@sevaorlov sevaorlov force-pushed the fix/joins-in-association-conditions branch from b420e1d to c7c1453 Compare April 23, 2016 21:02
@@ -158,6 +158,20 @@ def add_constraints(scope, owner, association_klass, refl, chain_head, chain_tai
scope
end

def merge_joins(scope, other)
joins_dependency, rest = other.joins_values.partition do |join|
Copy link
Contributor Author

@sevaorlov sevaorlov Apr 23, 2016

Choose a reason for hiding this comment

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

the same code we have here https://github.com/rails/rails/blob/master/activerecord/lib/active_record/relation/merger.rb#L113
I think it will be good to extract it somewhere

@sevaorlov
Copy link
Contributor Author

sevaorlov commented Apr 23, 2016

Will be good to add tests for pure sql joins and maybe also extract the logic to some sort of a merger class, but I would like to here some feedback first. Am I moving in right direction?

@sgrif
Copy link
Contributor

sgrif commented Apr 24, 2016

Can you please give more context in your commit message about what the issue is, why it occurs, and why this fixes it? "Fix joins in association scope" isn't much information for future readers.

@sevaorlov sevaorlov force-pushed the fix/joins-in-association-conditions branch from c7c1453 to 291bcf1 Compare April 25, 2016 18:45
@sevaorlov
Copy link
Contributor Author

@sgrif, sorry for that, I've updated commit and pull request descriptions

@sevaorlov sevaorlov changed the title fix Joins in association scope merge parent joins for through associations Apr 25, 2016
end

join_dependency = ActiveRecord::Associations::JoinDependency.new(other.klass, joins_dependency, rest)
scope.joins! join_dependency.join_constraints([], Arel::Nodes::InnerJoin).map(&:joins)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I also need to merge outer joins

@sevaorlov sevaorlov force-pushed the fix/joins-in-association-conditions branch from 291bcf1 to 9e9e484 Compare May 7, 2016 18:05
Merges parent associations joins for through associations into a scope. Previously joins in association scope were taken
into account only for root associations with that scope.
@sevaorlov sevaorlov force-pushed the fix/joins-in-association-conditions branch from 9e9e484 to 2767ea5 Compare May 7, 2016 18:07
@sevaorlov
Copy link
Contributor Author

sevaorlov commented May 7, 2016

Rails converts inner joins to outer joins when merging #24281
Instead of using Relation::Merger I've created my own custom method that doesn't convert inner joins to outer joins so that expected behaviour is met. Behaviour that is described in this PR description.

That maybe strange and counterintuitive that joins with merge in through associations scope doesn't convert joins to outer, but in scope it does, like here #22021.

If #24281 is bug that needs to be fixed, than I'll be able to use Relation::Merger here.

@oyeanuj
Copy link

oyeanuj commented Jul 10, 2016

@sgrif Any chance this can be merged soon?

@mattgibson
Copy link

Just ran into this. It breaks quite a few includes calls for us. Any chance of a merge?


if scope_chain_item == refl.scope
scope.merge! item.except(:where, :includes)
elsif item.joins_values.any?
merge_joins(scope, item)
Copy link
Contributor

Choose a reason for hiding this comment

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

How is this different than the merge that will occur in the call to scope.merge!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

scope.merge! merges joins with Relation::Merger, which converts inner joins to outer when merge_joins doesn't do that

Copy link
Contributor

Choose a reason for hiding this comment

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

That doesn't appear to be the case. The code is almost exactly identical to what you have here. https://github.com/rails/rails/blob/master/activerecord/lib/active_record/relation/merger.rb#L107-L129

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, it looks pretty much the same, but there are differences at the end. Instead of passing ActiveRecord::Associations::JoinDependency instance to relation.joins!, I pass an array of Arel::Nodes::InnerJoin objects, which are built as inner joins instead of outer for JoinDependency here
https://github.com/rails/rails/blob/master/activerecord/lib/active_record/relation/query_methods.rb#L1015
https://github.com/rails/rails/blob/master/activerecord/lib/active_record/relation/query_methods.rb#L1030
https://github.com/rails/rails/blob/master/activerecord/lib/active_record/relation/query_methods.rb#L1042
https://github.com/rails/rails/blob/master/activerecord/lib/active_record/associations/join_dependency.rb#L122

@at-hatran
Copy link

Is anyone working on this pr?

@sevaorlov
Copy link
Contributor Author

@at-hatran, at the time there is nothing to work on in this pr. I am waiting for any comments.

@at-hatran
Copy link

@sevaorlov I've just discovered this issue and my project use a lot of associations like this. Do you have any workaround for this?

@sevaorlov
Copy link
Contributor Author

@at-hatran, just don't user merges in conditions. For example, you could create a separate Query class for that particular query.

@sevaorlov sevaorlov closed this Jul 5, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
8 participants