How find table column with different values -rails - ruby-on-rails

The Case:
table - movie:
columns:
rating:
title:
Query 1- get all
Movie.all(:order => "title ASC");
Query 2- get with rating="G" || rating="K" ---Not working
ratingsArr=["G","K"]; also can be = [] =>real code params[:ratings] ..
Movie.all(:order => "title ASC", ratingsArr);
How should I build Query 2? ratingsArr may or may not be empty. User set params, according them ratingsArr has the value.

I believe you need to use Movie.where(:rating => ratingsArr).order("title ASC")
Being new, you should read the Rails Guides and perhaps the API documentation for ActiveRecord

Try this:
Movie.where( params[:ratings].present? ? {:rating => params[:ratings]} : {} ).
order("title ASC");

You should be able to use the SQL IN operator by doing: Movie.where(:rating => ratings_array).order("title ASC")
Note that I changed ratingsArr to ratings_array. This is code convention for Ruby. CamelDot is only used for Class names. In variables words are seperated by _

Related

Rails order query method for multiple and child attributes

While the new rails syntax implements an attribute that chooses the order direction as a symbol with colons,
I have not found a way to enact this syntaxic method for the following old-school style of ordering, when invoking multiple attributes where at least one is from a child table
#items = Item.eager_load(:itemoptions)
.order('model_id ASC, itemoptions.modeloption_id ASC')
.where( [...])
.all
how can this be achieved with the new approach?
As you're joining both tables, you need to prefix the columns you're using with their corresponding table names.
The only arguments for direction that order is able to receive are [:asc, :desc, :ASC, :DESC, "asc", "desc", "ASC", "DESC"], so that forces you to pass the columns as hash keys in the form { "table_name.column_name" => <direction> }.
Try with:
Item.eager_load(:itemoptions)
.where( [...])
.order('items.id' => :ASC, 'itemoptions.modeloption_id' => :ASC)
You don't need the all method there.
The error message that I receive says that it's possible to use Arel's sql escape to do this. Currently I am able to do something like Item.eager_load(:itemoptions).order(Arel.sql("model_id ASC, itemoptions.modeloption_id ASC"))
Also I know not part of the question, but I don't think you need the .all at the end

Active Record Query used "NOT IN" [duplicate]

I'm hoping there is a easy solution that doesn't involve find_by_sql, if not then I guess that will have to work.
I found this article which references this:
Topic.find(:all, :conditions => { :forum_id => #forums.map(&:id) })
which is the same as
SELECT * FROM topics WHERE forum_id IN (<#forum ids>)
I am wondering if there is a way to do NOT IN with that, like:
SELECT * FROM topics WHERE forum_id NOT IN (<#forum ids>)
Rails 4+:
Article.where.not(title: ['Rails 3', 'Rails 5'])
Rails 3:
Topic.where('id NOT IN (?)', Array.wrap(actions))
Where actions is an array with: [1,2,3,4,5]
FYI, In Rails 4, you can use not syntax:
Article.where.not(title: ['Rails 3', 'Rails 5'])
Using Arel:
topics=Topic.arel_table
Topic.where(topics[:forum_id].not_in(#forum_ids))
or, if preferred:
topics=Topic.arel_table
Topic.where(topics[:forum_id].in(#forum_ids).not)
and since rails 4 on:
topics=Topic.arel_table
Topic.where.not(topics[:forum_id].in(#forum_ids))
Please notice that eventually you do not want the forum_ids to be the ids list, but rather a subquery, if so then you should do something like this before getting the topics:
#forum_ids = Forum.where(/*whatever conditions are desirable*/).select(:id)
in this way you get everything in a single query: something like:
select * from topic
where forum_id in (select id
from forum
where /*whatever conditions are desirable*/)
Also notice that eventually you do not want to do this, but rather a join - what might be more efficient.
You can try something like:
Topic.find(:all, :conditions => ['forum_id not in (?)', #forums.map(&:id)])
You might need to do #forums.map(&:id).join(','). I can't remember if Rails will the argument into a CSV list if it is enumerable.
You could also do this:
# in topic.rb
named_scope :not_in_forums, lambda { |forums| { :conditions => ['forum_id not in (?)', forums.select(&:id).join(',')] }
# in your controller
Topic.not_in_forums(#forums)
To expand on #Trung Lê answer, in Rails 4 you can do the following:
Topic.where.not(forum_id:#forums.map(&:id))
And you could take it a step further.
If you need to first filter for only published Topics and then filter out the ids you don't want, you could do this:
Topic.where(published:true).where.not(forum_id:#forums.map(&:id))
Rails 4 makes it so much easier!
The accepted solution fails if #forums is empty. To workaround this I had to do
Topic.find(:all, :conditions => ['forum_id not in (?)', (#forums.empty? ? '' : #forums.map(&:id))])
Or, if using Rails 3+:
Topic.where( 'forum_id not in (?)', (#forums.empty? ? '' : #forums.map(&:id)) ).all
Most of the answers above should suffice you but if you are doing a lot more of such predicate and complex combinations check out Squeel. You will be able to doing something like:
Topic.where{{forum_id.not_in => #forums.map(&:id)}}
Topic.where{forum_id.not_in #forums.map(&:id)}
Topic.where{forum_id << #forums.map(&:id)}
You may want to have a look at the meta_where plugin by Ernie Miller. Your SQL statement:
SELECT * FROM topics WHERE forum_id NOT IN (<#forum ids>)
...could be expressed like this:
Topic.where(:forum_id.nin => #forum_ids)
Ryan Bates of Railscasts created a nice screencast explaining MetaWhere.
Not sure if this is what you're looking for but to my eyes it certainly looks better than an embedded SQL query.
The original post specifically mentions using numeric IDs, but I came here looking for the syntax for doing a NOT IN with an array of strings.
ActiveRecord will handle that nicely for you too:
Thing.where(['state NOT IN (?)', %w{state1 state2}])
Can these forum ids be worked out in a pragmatic way? e.g. can you find these forums somehow - if that is the case you should do something like
Topic.all(:joins => "left join forums on (forums.id = topics.forum_id and some_condition)", :conditions => "forums.id is null")
Which would be more efficient than doing an SQL not in
This way optimizes for readability, but it's not as efficient in terms of database queries:
# Retrieve all topics, then use array subtraction to
# find the ones not in our list
Topic.all - #forums.map(&:id)
You can use sql in your conditions:
Topic.find(:all, :conditions => [ "forum_id NOT IN (?)", #forums.map(&:id)])
Piggybacking off of jonnii:
Topic.find(:all, :conditions => ['forum_id not in (?)', #forums.pluck(:id)])
using pluck rather than mapping over the elements
found via railsconf 2012 10 things you did not know rails could do
When you query a blank array add "<< 0" to the array in the where block so it doesn't return "NULL" and break the query.
Topic.where('id not in (?)',actions << 0)
If actions could be an empty or blank array.
Here is a more complex "not in" query, using a subquery in rails 4 using squeel. Of course very slow compared to the equivalent sql, but hey, it works.
scope :translations_not_in_english, ->(calmapp_version_id, language_iso_code){
join_to_cavs_tls_arr(calmapp_version_id).
joins_to_tl_arr.
where{ tl1.iso_code == 'en' }.
where{ cavtl1.calmapp_version_id == my{calmapp_version_id}}.
where{ dot_key_code << (Translation.
join_to_cavs_tls_arr(calmapp_version_id).
joins_to_tl_arr.
where{ tl1.iso_code == my{language_iso_code} }.
select{ "dot_key_code" }.all)}
}
The first 2 methods in the scope are other scopes which declare the aliases cavtl1 and tl1. << is the not in operator in squeel.
Hope this helps someone.
If someone want to use two or more conditions, you can do that:
your_array = [1,2,3,4]
your_string = "SOMETHING"
YourModel.where('variable1 NOT IN (?) AND variable2=(?)',Array.wrap(your_array),your_string)

How to express a NOT IN query with ActiveRecord/Rails?

I'm hoping there is a easy solution that doesn't involve find_by_sql, if not then I guess that will have to work.
I found this article which references this:
Topic.find(:all, :conditions => { :forum_id => #forums.map(&:id) })
which is the same as
SELECT * FROM topics WHERE forum_id IN (<#forum ids>)
I am wondering if there is a way to do NOT IN with that, like:
SELECT * FROM topics WHERE forum_id NOT IN (<#forum ids>)
Rails 4+:
Article.where.not(title: ['Rails 3', 'Rails 5'])
Rails 3:
Topic.where('id NOT IN (?)', Array.wrap(actions))
Where actions is an array with: [1,2,3,4,5]
FYI, In Rails 4, you can use not syntax:
Article.where.not(title: ['Rails 3', 'Rails 5'])
Using Arel:
topics=Topic.arel_table
Topic.where(topics[:forum_id].not_in(#forum_ids))
or, if preferred:
topics=Topic.arel_table
Topic.where(topics[:forum_id].in(#forum_ids).not)
and since rails 4 on:
topics=Topic.arel_table
Topic.where.not(topics[:forum_id].in(#forum_ids))
Please notice that eventually you do not want the forum_ids to be the ids list, but rather a subquery, if so then you should do something like this before getting the topics:
#forum_ids = Forum.where(/*whatever conditions are desirable*/).select(:id)
in this way you get everything in a single query: something like:
select * from topic
where forum_id in (select id
from forum
where /*whatever conditions are desirable*/)
Also notice that eventually you do not want to do this, but rather a join - what might be more efficient.
You can try something like:
Topic.find(:all, :conditions => ['forum_id not in (?)', #forums.map(&:id)])
You might need to do #forums.map(&:id).join(','). I can't remember if Rails will the argument into a CSV list if it is enumerable.
You could also do this:
# in topic.rb
named_scope :not_in_forums, lambda { |forums| { :conditions => ['forum_id not in (?)', forums.select(&:id).join(',')] }
# in your controller
Topic.not_in_forums(#forums)
To expand on #Trung Lê answer, in Rails 4 you can do the following:
Topic.where.not(forum_id:#forums.map(&:id))
And you could take it a step further.
If you need to first filter for only published Topics and then filter out the ids you don't want, you could do this:
Topic.where(published:true).where.not(forum_id:#forums.map(&:id))
Rails 4 makes it so much easier!
The accepted solution fails if #forums is empty. To workaround this I had to do
Topic.find(:all, :conditions => ['forum_id not in (?)', (#forums.empty? ? '' : #forums.map(&:id))])
Or, if using Rails 3+:
Topic.where( 'forum_id not in (?)', (#forums.empty? ? '' : #forums.map(&:id)) ).all
Most of the answers above should suffice you but if you are doing a lot more of such predicate and complex combinations check out Squeel. You will be able to doing something like:
Topic.where{{forum_id.not_in => #forums.map(&:id)}}
Topic.where{forum_id.not_in #forums.map(&:id)}
Topic.where{forum_id << #forums.map(&:id)}
You may want to have a look at the meta_where plugin by Ernie Miller. Your SQL statement:
SELECT * FROM topics WHERE forum_id NOT IN (<#forum ids>)
...could be expressed like this:
Topic.where(:forum_id.nin => #forum_ids)
Ryan Bates of Railscasts created a nice screencast explaining MetaWhere.
Not sure if this is what you're looking for but to my eyes it certainly looks better than an embedded SQL query.
The original post specifically mentions using numeric IDs, but I came here looking for the syntax for doing a NOT IN with an array of strings.
ActiveRecord will handle that nicely for you too:
Thing.where(['state NOT IN (?)', %w{state1 state2}])
Can these forum ids be worked out in a pragmatic way? e.g. can you find these forums somehow - if that is the case you should do something like
Topic.all(:joins => "left join forums on (forums.id = topics.forum_id and some_condition)", :conditions => "forums.id is null")
Which would be more efficient than doing an SQL not in
This way optimizes for readability, but it's not as efficient in terms of database queries:
# Retrieve all topics, then use array subtraction to
# find the ones not in our list
Topic.all - #forums.map(&:id)
You can use sql in your conditions:
Topic.find(:all, :conditions => [ "forum_id NOT IN (?)", #forums.map(&:id)])
Piggybacking off of jonnii:
Topic.find(:all, :conditions => ['forum_id not in (?)', #forums.pluck(:id)])
using pluck rather than mapping over the elements
found via railsconf 2012 10 things you did not know rails could do
When you query a blank array add "<< 0" to the array in the where block so it doesn't return "NULL" and break the query.
Topic.where('id not in (?)',actions << 0)
If actions could be an empty or blank array.
Here is a more complex "not in" query, using a subquery in rails 4 using squeel. Of course very slow compared to the equivalent sql, but hey, it works.
scope :translations_not_in_english, ->(calmapp_version_id, language_iso_code){
join_to_cavs_tls_arr(calmapp_version_id).
joins_to_tl_arr.
where{ tl1.iso_code == 'en' }.
where{ cavtl1.calmapp_version_id == my{calmapp_version_id}}.
where{ dot_key_code << (Translation.
join_to_cavs_tls_arr(calmapp_version_id).
joins_to_tl_arr.
where{ tl1.iso_code == my{language_iso_code} }.
select{ "dot_key_code" }.all)}
}
The first 2 methods in the scope are other scopes which declare the aliases cavtl1 and tl1. << is the not in operator in squeel.
Hope this helps someone.
If someone want to use two or more conditions, you can do that:
your_array = [1,2,3,4]
your_string = "SOMETHING"
YourModel.where('variable1 NOT IN (?) AND variable2=(?)',Array.wrap(your_array),your_string)

ActiveRecord OR query

How do you do an OR query in Rails 3 ActiveRecord. All the examples I find just have AND queries.
Edit: OR method is available since Rails 5. See ActiveRecord::QueryMethods
If you want to use an OR operator on one column's value, you can pass an array to .where and ActiveRecord will use IN(value,other_value):
Model.where(:column => ["value", "other_value"]
outputs:
SELECT `table_name`.* FROM `table_name` WHERE `table_name`.`column` IN ('value', 'other_value')
This should achieve the equivalent of an OR on a single column
in Rails 3, it should be
Model.where("column = ? or other_column = ?", value, other_value)
This also includes raw sql but I dont think there is a way in ActiveRecord to do OR operation. Your question is not a noob question.
Rails 5 added or, so this is easier now in an app with Rails version greater than 5:
Model.where(column: value).or(Model.where(other_column: other_value)
this handles nil values as well
Use ARel
t = Post.arel_table
results = Post.where(
t[:author].eq("Someone").
or(t[:title].matches("%something%"))
)
The resulting SQL:
ree-1.8.7-2010.02 > puts Post.where(t[:author].eq("Someone").or(t[:title].matches("%something%"))).to_sql
SELECT "posts".* FROM "posts" WHERE (("posts"."author" = 'Someone' OR "posts"."title" LIKE '%something%'))
An updated version of Rails/ActiveRecord may support this syntax natively. It would look similar to:
Foo.where(foo: 'bar').or.where(bar: 'bar')
As noted in this pull request https://github.com/rails/rails/pull/9052
For now, simply sticking with the following works great:
Foo.where('foo= ? OR bar= ?', 'bar', 'bar')
Update: According to https://github.com/rails/rails/pull/16052 the or feature will be available in Rails 5
Update: Feature has been merged to Rails 5 branch
Rails has recently added this into ActiveRecord. It looks to be released in Rails 5. Committed to master already:
https://github.com/rails/rails/commit/9e42cf019f2417473e7dcbfcb885709fa2709f89
Post.where(column: 'something').or(Post.where(other: 'else'))
# => SELECT * FROM posts WHERE (column = 'something') OR (other = 'else)
Rails 5 comes with an or method. (link to documentation)
This method accepts an ActiveRecord::Relation object. eg:
User.where(first_name: 'James').or(User.where(last_name: 'Scott'))
If you want to use arrays as arguments, the following code works in Rails 4:
query = Order.where(uuid: uuids, id: ids)
Order.where(query.where_values.map(&:to_sql).join(" OR "))
#=> Order Load (0.7ms) SELECT "orders".* FROM "orders" WHERE ("orders"."uuid" IN ('5459eed8350e1b472bfee48375034103', '21313213jkads', '43ujrefdk2384us') OR "orders"."id" IN (2, 3, 4))
More information: OR queries with arrays as arguments in Rails 4.
The MetaWhere plugin is completely amazing.
Easily mix OR's and AND's, join conditions on any association, and even specify OUTER JOIN's!
Post.where({sharing_level: Post::Sharing[:everyone]} | ({sharing_level: Post::Sharing[:friends]} & {user: {followers: current_user} }).joins(:user.outer => :followers.outer}
Just add an OR in the conditions
Model.find(:all, :conditions => ["column = ? OR other_column = ?",value, other_value])
With rails + arel, a more clear way:
# Table name: messages
#
# sender_id: integer
# recipient_id: integer
# content: text
class Message < ActiveRecord::Base
scope :by_participant, ->(user_id) do
left = arel_table[:sender_id].eq(user_id)
right = arel_table[:recipient_id].eq(user_id)
where(Arel::Nodes::Or.new(left, right))
end
end
Produces:
$ Message.by_participant(User.first.id).to_sql
=> SELECT `messages`.*
FROM `messages`
WHERE `messages`.`sender_id` = 1
OR `messages`.`recipient_id` = 1
You could do it like:
Person.where("name = ? OR age = ?", 'Pearl', 24)
or more elegant, install rails_or gem and do it like:
Person.where(:name => 'Pearl').or(:age => 24)
I just extracted this plugin from client work that lets you combine scopes with .or., ex. Post.published.or.authored_by(current_user). Squeel (newer implementation of MetaSearch) is also great, but doesn't let you OR scopes, so query logic can get a bit redundant.
I'd like to add this is a solution to search multiple attributes of an ActiveRecord. Since
.where(A: param[:A], B: param[:B])
will search for A and B.
Using the activerecord_any_of gem, you can write
Book.where.any_of(Book.where(:author => 'Poe'), Book.where(:author => 'Hemingway')

How can I make an 'OR' statement in ActiveRecord?

I am trying to create an 'OR' sql statement in ActiveRecord 3, I tried all kinds of variations but can't figure it out...
For example I want this query to include multiple 'channel_ids' and have it return all posts for any of the channel IDs. This works for one:
Post.where(:user => 'mike').where(:channel_id => 0).limit(20)
but I cant figure out how to do it with multiples, I've tried for example:
Post.where(:user => 'mike').where(:channel_id => ?, [0,1,2,3]).limit(20)
but it didn't work. How can I make this work?
Use the Arel methods to do this:
t = Post.arel_table
ids = [1,2,3]
Post.where(
t[:user].eq("mike").or(t[:channel_id].in(ids))
)
Try this:
Post.where("posts.user = ? OR posts.channel_id IN (?)", "mike", ids)

Resources