Added #or to ActiveRecord::Relation #9052
ActiveRecord::Relation#or returns a new relation, which is the
ActiveRecord::Relation#or accepts conditions in one of several
If ActiveRecord::Relation#or is used without arguments, it returns
It can also be chained with a named scope:
When #or is used with an ActiveRecord::Relation as an argument, it
ActiveRecord::Relation#or also accepts anything that could be passed
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.
Sorry but some time ago we reached the conclusion to not add this method.
Thank you so much.
@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.
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
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
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. * without arguments 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: Post.where("id = 1").or.where("id = 2") > SELECT `posts`.* FROM `posts` WHERE (('id = 1' OR 'id = 2')) It can also be chained with a named scope: Post.where("id = 1").or.containing_the_letter_a > SELECT `posts`.* FROM `posts` WHERE (('id = 1' OR 'body LIKE \\'%a%\\'')) * ActiveRecord::Relation 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. Post.where("id = 1").or(Post.where("id = 2")) > SELECT `posts`.* FROM `posts` WHERE (('id = 1' OR 'id = 2')) * anything you would pass to #where ActiveRecord::Relation#or also accepts anything that could be passed to the #where method, as a shortcut: Post.where("id = 1").or("id = ?", 2) > SELECT `posts`.* FROM `posts` WHERE (('id = 1' OR 'id = 2'))
In my humble opinion, leaving this to arel is a much more elegant
user_arel_table = User.arel_table User.where(user_arel_table[:name].eq('bob').or(user_arel_table[:age].lt(25))).to_sql
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_sql
SELECT "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)
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.
@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.
@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
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"))
Here's another example that needs
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
@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
@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.
@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