-
Notifications
You must be signed in to change notification settings - Fork 21.7k
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
Added #or to ActiveRecord::Relation #16052
Conversation
Post.where('id = 1').or(Post.where('id = 2')) # => SELECT * FROM posts WHERE (id = 1) OR (id = 2) [Matthew Draper & Gael Muller]
API-wise, this seems reasonable to me. Others can have a look at the implementation. |
end | ||
|
||
def structurally_compatible?(other, allowed_to_vary) | ||
Relation::SINGLE_VALUE_METHODS.all? do |name| |
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.
I'd extract these conditionals to smaller private methods. I know what they are doing but it would be better if they were easily to nay contributor understand what they are doing.
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.
Yeah, this ended up a bit uglier than I'd originally anticipated, and I didn't go back and clean it up. Will do.
|
@egilburg they only work with relations so they can't be combined with They can be chained but with other relation so User.where('something').or(User.where('something')).or(User.where('something')) But I believe something like this is still valid: User.where('something').or(User.where('something')).where(bar: true) |
I love this solution, thanks. I don't see in tests anything specific to handling Something like : Post.where.not('id = 1').or(Post.where.not('id = 2')) Not that it would be very good code (it's quite hard to understand), |
Sorry, instead of In below example what would be evaluation order be?:
(A && B) || C or A && (B || C) And in below example:
Is it: (A || B) && C or A || (B && C) |
Obviously I think this is needed, but I don't see what is wrong with my I personnaly find this notation heavier. Gael Muller |
(A && B) || C
(A || B) && C |
Ok, so it's always left-to-right. In this case, what ambiguity is solved by using the heavier:
As opposed to lighter:
|
and to get the other interpretations:
A && (B || C)
A || (B && C) In general, I anticipate this being used mostly to combine named scopes, likely often inside another named scope (like this): if you're already dealing with direct |
User.where(a).or(User.where(b).where(c)) On Jul 4, 2014, at 11:05 AM, Eugene Gilburg notifications@github.com wrote:
|
I think the point is precisely to avoid such order consideration and
and
are both : Edit : or maybe not, given Matthew answer. This ordering thing was the problem with your implementation. |
The main ambiguity in that syntax, I think, is:
That will presumably resolve as |
In latter case you'd use:
So |
|
||
common = left_values & right_values | ||
mine = left_values - common | ||
theirs = right_values - common |
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 there a particular reason for this? Shouldn't
WHERE a = 1 AND (b = 2 OR c = 3)
be equivalent to
WHERE ((a = 1 AND b = 2) OR (a = 1 AND c = 3))
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.
- legibility / length of the generated query
- keeps the query the same "shape" as the set of relation calls that created it
So, yes.. but it seemed like a nice thing to do, and not unreasonably expensive.
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.
in your example @sgrif if a != 1
wouldn't the first fail after one check whereas the second would still make two checks?
This seems to avoid some of the issues from the previous # works
base_rel = Post.where(id: 1)
base_rel.where("title = 'bar'").or(base_rel.where("title = 'baz'")).to_a
# fails with "ArgumentError: Relation passed to #or must be structurally compatible"
base_rel = Post.where(id: 1)
base_rel.where(title: 'bar').or(base_rel.where(title: 'baz')).to_a This appears to be caused by the two relations being |
Maybe you can use a code block as argument to make it simpeler in use: Post.where(id: 1).or({ where(id: 2) }) |
Btw @matthewd , by far the biggest problem I had with I've toyed for a time with the idea of using UNION instead of OR for I've hit two limitations making this a hard task :
In case it helps. |
It looks like this will break on queries which use bound parameters, we should probably merge the bind_values and add a test for hash style where, as well. |
Nice one! 👍 |
👍 |
Great! 👍 🎉 |
🎉 |
👍 |
✨ 👍 |
Rails 5.0.
|
Awesome ! 👏 |
❤️ |
Bravo! ❤️ |
Great..!!! 👍 |
Fanastic! |
It's wonderful but I feel like I didn't see an example above that allows for the following example: @person = Person.where("(first_name = :forename OR middle_name = :forename OR nickname = :forename) AND last_name = :last_name AND gender = :gender AND birthday = :birthday", forename: first_name, last_name: last_name, gender: gender, birthday: birthday).first So I would reuse the first name passed in to try an prepare for the chance that someone could have used the persons middle or nickname instead of their full name. Stupid example but on contracts I am David (first name), to the general public I am Mitchell (middle) and to friends I am Mitch (nickname) and I have been searched for on all of them. Looking forward to getting the or relation! |
send("#{name}_value") == other.send("#{name}_value") | ||
end && | ||
(Relation::MULTI_VALUE_METHODS - [allowed_to_vary, :extending]).all? do |name| | ||
send("#{name}_values") == other.send("#{name}_values") |
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.
Could you create a method like this?
def validate_strutucture_for(attr)
lambda { |name| send("#{name}_#{attr}") == other.send("#{name}_#{attr}") }
end
And with that method, you can write
Relation::SINGLE_VALUE_METHODS.all? &validate_strutucture_for("value")
and
(Relation::MULTI_VALUE_METHODS - [allowed_to_vary, :extending]).all? &validate_strutucture_for("values")
What do you think?
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.
I think that's a bit harder to read in Ruby than what we have now (though it could be broken up)
FWIW, I've written up a backport of |
To complement the default and behaviour while building relations, allow two very similar relations to be combined with an or.
Unlike previous attempts, we avoid any ambiguity about "how much" we're ORing by exclusively accepting a second relation -- thus, we're ORing whatever conditions differ between the two.
This is a much stricter variant of @gaelmuller's #9052; see also @oelmekki's #10891.
/cc @dhh @jeremy @tenderlove @rafaelfranca