Join GitHub today
GitHub is home to over 31 million developers working together to host and review code, manage projects, and build software together.
Sign upAdded #or to ActiveRecord::Relation #9052
Conversation
mdespuits
reviewed
Jan 24, 2013
View changes
| @@ -452,6 +468,60 @@ def where!(opts = :chain, *rest) # :nodoc: | |||
| end | |||
| end | |||
|
|
|||
| # Returns a new relation, which is the result of filtering the current relation | |||
| # according to the conditions in the arguments, joining WHERE clauses with OR | |||
| # operand, contraty to the default behaviour that uses AND. | |||
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
|
|
mdespuits
reviewed
Jan 24, 2013
View changes
| def method_missing(method, *args, &block) | ||
| right_relation = @scope.klass.unscoped { | ||
| @scope.klass.send(method, *args, &block) | ||
| } |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
gaelmuller
commented
Jan 24, 2013
|
Thank you @mattdbridges for your feedback. I updated the commit with corrections for the typo and the multiline block. |
This comment has been minimized.
This comment has been minimized.
|
Sorry but some time ago we reached the conclusion to not add this method. Some reason:
Thank you so much. |
rafaelfranca
closed this
Jan 24, 2013
This comment has been minimized.
This comment has been minimized.
gaelmuller
commented
Mar 13, 2013
|
@rafaelfranca, I feel like the arguments to not add this method are those used for the
If you still disagree, I will try to extract it as a plugin. |
This comment has been minimized.
This comment has been minimized.
gaelmuller
commented
Mar 15, 2013
|
@tenderlove, @steveklabnik, you commented on a similar pull request (#6817). What is your opinion about this ? |
This comment has been minimized.
This comment has been minimized.
|
I don't have strong opinions about this one, so I think we can reopen to discuss. @jeremy thoughts? |
rafaelfranca
reopened this
Mar 15, 2013
This comment has been minimized.
This comment has been minimized.
housekeeper
commented
Mar 27, 2013
|
I would +1 this too but for a true scoping issue... Developing a re-occurring calendaring gem and it would be awesome to chain scopes in the following ways Calendar::Event.for_current_user.today there are 3 scoped chains for 3 tables... an events table based on whether the user created the event or whether the user has been assigned/notified to/of the event in an event_users table. This is handled with no problem, my issue is when there is a re-occurring event which falls between a reoccurring start and end as well as an event start and end of which I need to OR - this is perfectly valid reason for this request and I believe a missing feature especially when wanting to create complex but efficient queries FYI |
This comment has been minimized.
This comment has been minimized.
|
|
This comment has been minimized.
This comment has been minimized.
midu
commented
Mar 27, 2013
|
|
This comment has been minimized.
This comment has been minimized.
crmaxx
commented
Apr 9, 2013
|
|
This comment has been minimized.
This comment has been minimized.
|
+1 |
1 similar comment
This comment has been minimized.
This comment has been minimized.
KevinBongart
commented
Apr 9, 2013
|
|
This comment has been minimized.
This comment has been minimized.
niuage
commented
Apr 9, 2013
|
|
This comment has been minimized.
This comment has been minimized.
ghalley
commented
Apr 25, 2013
|
+1 |
This comment has been minimized.
This comment has been minimized.
descentintomael
commented
Apr 25, 2013
|
+1 for this feature. I need some way to merge scopes together without having to do it manually column-by-column in squeel. |
This comment has been minimized.
This comment has been minimized.
atestu
commented
Apr 25, 2013
|
|
1 similar comment
This comment has been minimized.
This comment has been minimized.
jcapron
commented
Apr 25, 2013
|
|
This comment has been minimized.
This comment has been minimized.
christhekeele
commented
Apr 25, 2013
|
+1 |
This comment has been minimized.
This comment has been minimized.
williscool
commented
Apr 26, 2013
|
In my humble opinion, leaving this to arel is a much more elegant ie user_arel_table = User.arel_table
User.where(user_arel_table[:name].eq('bob').or(user_arel_table[:age].lt(25))).to_sqltranslates to SELECT "users".* FROM "users" WHERE (("users"."name" = 'bob' OR "users"."age" < 25))or to convert one of your examples from the pull request
posts = Post.arel_table
Post.where(posts[:id].eq(1).or(posts[:id].eq(2))).to_sqlSELECT "posts".* FROM "posts" WHERE (("posts"."id" = 1 OR "posts"."id" = 2))you can also check out a couple other good examples here (only the upvoted ones of course) http://stackoverflow.com/questions/7976358/activerecord-arel-or-condition you can also join in other model/tables and then use their arel tables also. I can add some examples like that if anyone wants to see. |
This comment has been minimized.
This comment has been minimized.
laskaridis
commented
Apr 26, 2013
|
It's embarrassing enough that this feature is NOT in ActiveRecord yet. +1 |
This comment has been minimized.
This comment has been minimized.
descentintomael
commented
Apr 26, 2013
|
@williscool That example works for small queries, but what if I have a large number of columns to search across? When I try the inject methods in the SO example I end up with Hash objects instead of ARel objects which don't work. I would prefer to have a native solution to combining scopes with OR. Rails is really good about having elegant solutions to many things, but this doesn't seem to be one of them. |
This comment has been minimized.
This comment has been minimized.
williscool
commented
Apr 26, 2013
|
@descentintomael can you give an example with a large number of columns where the arel version is cumbersome? And you would do your method chaining in between the where functions on the ActiveRecord::Relation objects. Not in the middle of them on the arel ones. Man the naming of those things is quite confusing I wish they had completely different names. What I mean is User.containing_the_letter_a.where(admins.or(authors))not User.where(admins.or(authors).containing_the_letter_a) |
This comment has been minimized.
This comment has been minimized.
descentintomael
commented
Apr 26, 2013
|
Right now I'm trying to search through my Order model which has quite a few columns. For instance, it has an amount and a currency column, those two need to be joined with 'AND' but then there are other columns like customer ID and PO number that come into play and need to be combined with 'OR'. I'm not really sure yet how the syntax would go (without using Squeel), but I do like the direction taken in the first example: Post.where("id = 1").or(Post.where("id = 2")) |
schuetzm
reviewed
Apr 27, 2013
| right = (ActiveRecord::Relation === opts) ? opts : klass.unscoped.where(opts, rest) | ||
|
|
||
| unless left.where_values.empty? || right.where_values.empty? | ||
| left.where_values = [left.where_ast.or(right.where_ast)] |
This comment has been minimized.
This comment has been minimized.
schuetzm
Apr 27, 2013
Contributor
There's a problem here: you are modifying the original relations. If you're lucky, this will fail with ActiveRecord::ImmutableRelation, but this only happens if the relations have already been loaded.
The fix is simple:
diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb
index 1e291e6..ced097c 100644
--- a/activerecord/lib/active_record/relation/query_methods.rb
+++ b/activerecord/lib/active_record/relation/query_methods.rb
@@ -607,6 +607,7 @@ module ActiveRecord
right = (ActiveRecord::Relation === opts) ? opts : klass.unscoped.where(opts, rest)
unless left.where_values.empty? || right.where_values.empty?
+ left, right = left.dup, right.dup
left.where_values = [left.where_ast.or(right.where_ast)]
right.where_values = []
end
This comment has been minimized.
This comment has been minimized.
gaelmuller
commented
Apr 30, 2013
|
@schuetzm, thank you for your feedback. I updated the commit to avoid ImmutableRelation exceptions. |
This comment has been minimized.
This comment has been minimized.
gaelmuller
commented
Jan 16, 2014
|
You have to think about how chaining works to be able to understand what behavior relation.where(id: 1).or(id: 2).where(id: 3)is equivalent to: relation = relation.where(id: 1)
relation = relation.or(id: 2)
relation = relation.where(id: 3)When trying to understand what this will do, you should always use the second block to decompose the chaining. In this case, it is pretty obvious that A few examples taken from this thread:
As noted by @al2o3cr we cannot produce
Please not that in SQL, There is another case that can be not as elegant as we could hope: when you want to apply
You have two possibilities. The first one is to change the order, to first call the
Or, if you don't really get to manage what comes first:
This is where we could probably use something like |
This comment has been minimized.
This comment has been minimized.
ghost
commented
Jan 17, 2014
|
@al2o3cr test cases would be great! it also solves a problem that is quite complex to solve with just AREL or plain old SQL(SQL variant being simple). for example, imagine: class Car < ActiveRecord::LOL
has_many :registrations
def self.registered_on(start, finish)
joins(:registrations).where(registrations: {value: start .. finish})
end
end
Car.registered_on(1.days.ago, 10.days.ago).or.registered_on(65.days.ago, 68.days.ago)unless i'm missing something, AR doesn't make that very easy right now unless you fallback to AREL - which is overly complicated to solve this problem and leaves ActiveRecord altogether. other option being a really long SQL string. |
This comment has been minimized.
This comment has been minimized.
ghost
commented
Jan 17, 2014
|
@gaelmuller is the above query possible with your pull request? do I need to do anything different? |
This comment has been minimized.
This comment has been minimized.
gaelmuller
commented
Jan 17, 2014
|
Yes, @robgleeson it should work. |
This comment has been minimized.
This comment has been minimized.
ghost
commented
Jan 17, 2014
|
awesome, thank you. |
This comment has been minimized.
This comment has been minimized.
br3nt
commented
Apr 29, 2014
|
Here's another example that needs The To make this work in rails currently, all query methods in PatientSearch would need to return arel instead of a relation so that they can be ORed together. This has a flow on effect such that any scopes used would also need to be rewritten as methods that return arel. Basically, throw the ActiveRecord query dsl and all its conveniences out the window. This wouldn't be an issue if queries.each do |query|
next if !query # skip if no query
if match_criteria == 'all'
results = results.where(query) # each search gets ANDed
elsif match_criteria == 'any'
results = results.or(query) # each search gets ORed
end
end |
This comment has been minimized.
This comment has been minimized.
midu
commented
Apr 29, 2014
|
The problem is more that all these chainable methods are an incitation to violating the law of demeter, which we all know is wrong. |
This comment has been minimized.
This comment has been minimized.
|
@midu I would say that the law of demeter isn't an issue here. You can think of it as the builder pattern: we don't rely on object's internals, or dependencies of dependency, if you wish; instead we add pieces to one object. The returning object is always the same. In alternative syntax it could look like
|
This comment has been minimized.
This comment has been minimized.
br3nt
commented
Apr 30, 2014
|
@midu, the example I provided is not an 'incitation to violating the law of demeter'. In fact it should quite cleanly build a query if there was an or operator. The example I provided is a very legitimate example of where or requires support from ActiveRecord. Besides, so long as its implementation is consistent with the rest of the dsl it should be the programmers prerogative to decide how they build queries. |
This comment has been minimized.
This comment has been minimized.
br3nt
commented
Apr 30, 2014
|
@midu, the law of demeter (and all other concerns) is secondary to a much larger issue. Here is why an 'or` operator should be supported by the ActiveRecord query dsl:
This fact should be considered above all else as it is at the heart of SQL and all the mathematical theory that it is built around. (on a side note, imagine if ruby didn't have an |
This comment has been minimized.
This comment has been minimized.
tarmotalu
commented
May 5, 2014
|
+1 |
1 similar comment
This comment has been minimized.
This comment has been minimized.
paxer
commented
May 9, 2014
|
+1 |
This comment has been minimized.
This comment has been minimized.
ghost
commented
May 9, 2014
|
sounds good to me, but i have no interest in rails/AR. |
This comment has been minimized.
This comment has been minimized.
ghost
commented
May 9, 2014
|
all the +1's are also strange. you would swear someone was using a bot-net to sway public opinion. |
This comment has been minimized.
This comment has been minimized.
ghost
commented
May 9, 2014
|
"law of demeter", oh get on that, +1 +1 +1 +1. |
matthewd
self-assigned this
May 9, 2014
This comment has been minimized.
This comment has been minimized.
dmgarland
commented
May 13, 2014
|
+1 |
1 similar comment
This comment has been minimized.
This comment has been minimized.
|
+1 |
This comment has been minimized.
This comment has been minimized.
fuyi
commented
May 30, 2014
|
would like to see this feature be added |
This comment has been minimized.
This comment has been minimized.
Sporky023
commented
Jun 12, 2014
|
@br3nt said it right: OR is a fundamental mathematical operation which deserves support. Without using OR, our actions are limited. |
rails
locked and limited conversation to collaborators
Jun 13, 2014
This comment has been minimized.
This comment has been minimized.
|
I locked the conversation. We already have enough reasons to thinks about it so we don't need more support comments like "+1" or "I agree with this". We are going to review and if we decide to merge we will reopen the discussion. |
gaelmuller commentedJan 23, 2013
ActiveRecord::Relation#or returns a new relation, which is the
result of filtering the current relation according to the
conditions in the arguments, joining WHERE clauses with OR
operand, contraty to the default behaviour that uses AND.
ActiveRecord::Relation#or accepts conditions in one of several
formats. In the examples below, the resulting SQL is given as an
illustration; the actual query generated may be different depending
on the database adapter.
If ActiveRecord::Relation#or is used without arguments, it returns
an ActiveRecord::OrChain object that can be used to chain queries
with any other relation method, like where:
It can also be chained with a named scope:
When #or is used with an ActiveRecord::Relation as an argument, it
merges the two relations, with the exception of the WHERE clauses,
that are joined using the OR operand.
ActiveRecord::Relation#or also accepts anything that could be passed
to the #where method, as a shortcut:
This is my first contribution to rails and I tried to keep things coherent with the current code, but I am obviously open for feedback.