Ruby on Rails querying over dateTime - ruby-on-rails

I need to rewrite some DB query so my Rails app can run on Heroku. In one query I need to fetch all entries which are not older then one day. I tried it like this=>
Sendnotifications.where(:actionID => actionID.to_s,
:time => ['? >= time => ?', DateTime.now,DateTime.now-1]).all.count
But this does not work, I guess because of a wrong syntax. So the question is how can I solve this query problem?
Thanks :D

No need to specify two boundaries, one will be more than enough (this way the code will look cleaner):
Sendnotifications.where('time > ?', DateTime.now - 1) #chain all your other methods

Sendnotifications.where(actionID: actionID.to_s)
.where("time between ? and ?", DateTime.now, DateTime.now-1)
.count

Related

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)

Getting the id with a rails find with :select

I have this rails find that i need to get the id as well but if i put the id in the :select wont it effect the query and is there another way to get the id
#past_requests = Request.find_all_by_artist(name, :conditions => ["showdate < ?", Time.now], :select => "distinct venue, showdate")
#past_requests = Request.find_all_by_artist(name, :group => "venue, showdate")
code long for view. i'm remove your condition. sorry about this. Hope it'll helpful for you. :)
To be fair in cases where distinct returns a single row out of maybe 5 duplicates who's to say which id out of those 5 should be displayed in your result? I'm afraid what you are asking for is not practical. Maybe you misunderstand what distinct is used for ? Give us more info pls.

Rails SQL query with % Wildcards works in SQLite but not PostgreSQL?

I have a query I'm using for a search with :conditions like this:
:conditions => ['family_name LIKE ? OR given_name LIKE ?', "%#{params[:search]}%", "%#{params[:search]}%"]
The query works fine locally on SQLite, but when I push to Heroku on PostgreSQL, only the first % works for both family_name and given_name. In other words, it will match a keyword that occurs at the end of a word but not the beginning or middle.
Example:
There is an existing record with :family_name => "Washington" and :given_name => "George"
A search for "ington" or "rge" will retrieve this record. A search for "Wash" or "Geo" will not.
I'm a new user and am pretty new to rails. Thanks in advance for your help.
Solution
Related threads:
1
2
Here's the fix I am using:
:conditions => ['LOWER(family_name) LIKE ? OR LOWER(given_name) LIKE ?', "%#{params[:search].downcase}%", "%#{params[:search].downcase}%"]
LIKE is a case-sensitive operator in Postgres. For case-insensitive pattern matching use ILIKE or other standard methods.

Getting objects updated in last 10 seconds from ActiveRecord's find

I'd like to get the objects that are updated in the last 10 seconds from the ActiveRecord's find.
I tried doing this
#chats = Chat.find(:all, :conditions => ["updated_at > ?", 10.seconds.ago] )
as well as
#chats = Chat.find(:all, :conditions => ["updated_at > ?", Time.now-10.seconds] )
and even
#chats = Chat.find(:all, :conditions => {:updated_at => 10.seconds.ago..0.seconds.ago}]
But I still can't get it to work :(
EDIT: I am updating the column from another application to keep it alive and I'm looking at all the rows I kept alive in the last 10 seconds, to dispose the dead chats.
Are you sure that you are querying on the right column? I am thinking that you might need to look at the created_at timestamp instead. I don't know your application, but I am surprised you are updating existing chats, instead of just inserting new ones.
So... if my assumption is correct, this might be what you are looking for:
#chats = Chat.find(:all, :conditions => ["created_at > ?", 10.seconds.ago] )
If not... please provide a little more info. The query should work as you have it, and I am guessing the updated_at may not be getting updated.
The problem was in UTC-default of Rails. I changed it to
config.time_zone = 'Pacific Time (US & Canada)'
and now it works alright :)

Resources