-
Notifications
You must be signed in to change notification settings - Fork 21.4k
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
Relation.where with no args can be chained with not, like, and not_like #8332
Conversation
# User.where.not(name: %(Ko1 Nobu)) | ||
# # SELECT * FROM users WHERE name NOT IN ('Ko1', 'Nobu') | ||
def not(opts, *rest) | ||
extend(NotBuilder).where(opts, *rest).dup |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it necessary to dup
, since where
should already be a cloned relation?
Looks awesome 👍❤️, thanks @amatsuda. We'll probably need a changelog entry and some guides updates for that, let me know if you need help with anything. |
Oh, this feature is amazing. Thanks! One question, couldn't we implement it not using mixins? Something like:
The downside of using mixins is that |
@carlosantoniodasilva Alright. I will work on more documentations :) @josevalim I just wanted to always keep the returned Relation as an absolute Relation, for example responds to |
@amatsuda ah, I see what you mean. Yes, you are correct, my proposal is not going to work as expected. |
Wait, actually, my proposal is still going to return a relation, except when you call In this case, I would indeed prefer to go with the simpler implementation since it also won't permanently pollute the relation. /cc @jonleighton @jeremy what do you think? |
Lovely ❤️ Agree with treating |
This looks good to me. Agree about using a builder object. |
@amatsuda I have edited your code following the suggestion by @josevalim so WhereChain is now a class with a builder, rather than a mixin. I have also added documentation and edited the CHANGELOG. It's all in this new pull request: #8365 @josevalim I think that, after my commit, it becomes clearer how to chain those three new methods. If you agree, I would like to make it even clearer by explicitly raising an
Right now, the previous line would just raise a generic error saying that the method Finally, I'd like to draw your attention to this required change in the test: claudiob@be71e1a#L2L302. |
Seems good to me. @amatsuda we will need changelog entry and updates on the guides. @josevalim @jonleighton anything else? |
@@ -271,7 +271,7 @@ def test_finding_array_compatibility | |||
end | |||
|
|||
def test_find_with_blank_conditions | |||
[[], {}, nil, ""].each do |blank| | |||
[[], {}, ""].each do |blank| |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you want to prevent that to happen - changing the meaning of where(nil)
- you can add a special name to the opts
argument, lets say :chain
, or even use false
as a check instead of nil
. This was done previously with exists?
359592b. Just an idea :)
examples: Model.where.not field: nil #=> "SELECT * FROM models WHERE field IS NOT NULL Model.where.like name: 'Jeremy%' #=> "SELECT * FROM models WHERE name LIKE 'Jeremy%' this feature was originally suggested by Jeremy Kemper rails#5950 (comment) Closes rails#5950
Just @claudiob Your version would not work with varargs, e.g.: |
Thanks @amatsuda 👍 |
@amatsuda I thought about that, but then again, why would
then shouldn't you also be able to write the following?
In other words, should I also think it wouldn't hurt to restore this test: claudiob@be71e1a#L4R89 or at least something that makes clear what happens if you call @carlosantoniodasilva I understand your suggestion, but before delving into coding that I would like to know what is the most desired behavior. What makes most sense to you? That
so personally I would keep the old behavior in this case (having |
@josevalim @rafaelfranca any opinion on the comment above? |
@claudiob answering your question, to me, About raising another/specific error, I think a |
Relation.where with no args can be chained with not, like, and not_like Conflicts: activerecord/CHANGELOG.md activerecord/lib/active_record/relation/query_methods.rb
Consider this scenario: if params[:foo] conditions = { foo: true } end foos = Foo.where(conditions).order(:id) When params[:foo] is nil, this would call: foos = Foo.where(nil).order(:id) In this scenario, we want Foo.where(conditions) to be the same as calling Foo.all, otherwise we'd get a "NoMethodError order for WhereChain". Related to rails#8332.
@amatsuda thank you 💚💛❤️💜💙 @claudiob please feel free to improve the test coverage with other scenarios as you suggest, would be more than welcome. I've reverted back the (apparently github decided to point the linked commits only to my fork since I pushed there first, dunno why ;) |
@carlosantoniodasilva Using the :chain symbol as a default looks great! I just added a new pull request to have the RDOC match your change: #8445 |
@carlosantoniodasilva I have one more doubt on this commit. Because of the code at carlosantoniodasilva@23b9cc8#L1R32 ,
My doubt (also for @amatsuda) is the following: why do we allow I think that we should keep coherence among the WhereChain methods: they should all accept the same types of parameters. Therefore, I see two options:
@carlosantoniodasilva and @amatsuda what is your opinion on this? Personally, I'd vote for option 1) since I don't see a big advantage in writing code like:
or:
rather than:
Implementing this support would also be cumbersome, since we would have to transform the equal sign in |
I'm not sure about removing the other types from |
I think it is fine as it is. |
guys, taking the opportunity, i guess it's time to add support to Arel::Nodes::GreaterThan, Arel::Nodes::GreaterThanOrEqual, Arel::Nodes::LessThan and Arel::Nodes::LessThanOrEqual using the amazing WhereChain class. |
@rafaelfranca and @carlosantoniodasilva If you think that it's fine leaving things as they are, I suggest we document this discrepancy in the formats of conditions accepted by I have created a pull request with this goal: #8452 |
doesn't really make sense. OTOH
and
are not the same. For more example, think about the difference between these two queries: Post.where.not(title: 'hello', name: 'jon') Post.where.not("title = 'hello' and name = 'jon'") So,
this is false. These are different, and we need both. |
Following comments by @jweslley and @pwightman I have added inequalities to WhereChain in this pull request: #8453 Regarding this commit, I have a couple of questions for @rafaelfranca and @carlosantoniodasilva :
|
@claudiob I don't know yet if it's going to be accepted, but if it is, I think it's fine for the methods to be aliased. I also don't think there's need for refactoring through metaprogramming yet, even though there seems to be a bit of repetition, it's very clear what each piece of code does, they're very small, and we can add as much docs as we want this way :). Thanks! |
@amatsuda Oh, you are right! I didn't see that, because all the examples that pass conditions as a hash only have one key/value pair! Maybe we can add some documentation or test where a WhereChain methods is called with more than one key/value pair, like in your example?
|
Just for reference in this pull request, support for .like and .not_like was later removed in 8d02afe |
Wouldn’t it be cleaner API-wise to just have: Post.where(title: 'Foo').first
Post.not(title: 'Foo').first Since |
This commit stems from rails#8332 (comment) Since the formats in which conditions can be passed to `not` differ from the formats in which conditions can be passed to `like` and `not_like`, then I think it's worth adding rdoc and tests to show this behavior
Finally implemented @jeremy's suggestion here #5950 (comment)
This patch adds a chainable mode to
AR::Relation
for new ARnot
,like
, andnot_like
query methods.examples: