Rails: Search DB for part of datetime - ruby-on-rails

I have a problem. In my application I have a list with data. This data includes a datetime_utc column. There is also a searchbar which allows the user to search for datetime strings, but the searchbar is now requiring precise inputs, so for example these inputs return a result:
2023-01-01 02:00:00
2023-01-01 02:00
2023-01-01 02
As long as the input string starts with a valid datetime format. What I want is for example to look for only a given time like:
02:00
This should return all data with the time 02:00:00 as datetime_utc value.
Right now this is my code:
data = data.where(datetime_utc: "#{params[:search]}") if params[:search] && params[:search] != ''
And I have tried things like this:
data = data.where("to_char(datetime_utc, 'YYYY-MM-DD HH24:MI:SS') LIKE ?": "%#{params[:search]}%") if params[:search] && params[:search] != ''
But this gives me the error:
PG::UndefinedColumn: ERROR: column candlesticks.to_char(datetime_utc, 'YYYY-MM-DD HH24:MI:SS') LIKE ? does not exist
So I understand that Postgres formats the given input string to a datetime format first and I it can't do that, it crashes.
There must be a way to still get this to work right?

Rails is considering "to_char(datetime_utc, 'YYYY-MM-DD HH24:MI:SS') LIKE ?" as column name because of the incorrect format used
You need to change your query to (Notice the : is replaced with ,)
data = data.where("to_char(datetime_utc, 'YYYY-MM-DD HH24:MI:SS') LIKE ?", "%#{params[:search]}%") if params[:search] && params[:search] != ''

Related

Where with a condition

I have a table like this:
Calendar
- date
- description
- userid
And I would like to get all the events of a given month of a user by it's userid.
So I know that to get all events for a userid is :
Calendar.where(userid: params[:id])
And I tried (as I saw in some post already in here)
Calendar.where(userid: params[:id]).where("date ~= ?", '%2016-12%')
So this should give me all event from the 12th month of 2016 but instead I have :
ActiveRecord::StatementInvalid: PG::UndefinedFunction: ERROR: operator does not exist: date ~= unknown
LINE 1: ...endars" WHERE "calendars"."userid" = $1 AND (date ~= '%2016-...
HINT: No operator matches the given name and argument type(s). You might need to add explicit type casts.
What did I do wrong ?
You could try "parsing" your column date to char using the to_char Postgresql function passing the column and the format you want:
SELECT *
FROM calendars
WHERE to_char(date, 'YYYY-MM-DD') LIKE '%2016-12%'
So, you can use ActiveRecord#find_by_sql:
query = "SELECT * FROM calendars WHERE to_char(date, 'YYYY-MM-DD') LIKE '%2016-12%'"
Calendar.find_by_sql(query)
I think your solution is here
def index
#events = Calendar.where(userid: params[:id]).all
#event_months = #events.group_by { |t| t.date.month } // month is a given name
end
Please see the below link
https://stackoverflow.com/a/18862762/8175208
I think will help you

Postgres 9.5.3 JSON fields comparing dates with operators weird behavior on Mac OSX 10

I'm having a problem on both of my Mac machines, but not on my Linux machines. Using date comparison operators like this does not work on my Mac:
# Ruby code
dt_start = DateTime.current - 10.days
dt_end = DateTime.current
id = 1
# last_seen field looks like this in db when we store it:
# {"1":"2016-11-21T22:17:47.269Z"}
User.where("(last_seen->'?')::text <> 'null'", id
).where( "(last_seen->'?')::text > ?", id, dt_start
).where( "(last_seen->'?')::text <= ?", id, dt_end)
SELECT "public"."users".* FROM "public"."users" WHERE ((last_seen->'1')::text <> 'null') AND ((last_seen->'1')::text > '2016-11-12 18:13:03.432534') AND ((last_seen->'1')::text <= '2016-11-22 18:13:03.432534')
Returns no records on my Mac, but works on Linux
Upon breaking apart that query, when I use > operator, I get no records no matter what date range I put.
User.where( "(last_seen->'?')::text > ?", id, 10.years.ago).count
SELECT COUNT(*) FROM "public"."users" WHERE ((last_seen->'1')::text > '2006-11-22 23:46:59.199255')
=> 0
When I use only the < operator, I get all records that have non-empty last_seen fields no matter what date I put.
User.where( "(last_seen->'?')::text < ?", id, 10.years.ago).count
SELECT COUNT(*) FROM "public"."users" WHERE ((last_seen->'1')::text > '2006-11-22 23:46:59.199255')
=> 42
I've even tested by switching my time on my Mac to match my linux box timezone which is UTC. Any ideas?
UPDATE:
So DateTime and ActiveSupport::TimeWithZone formatted to ISO 8601 return different formats:
DateTime.current.iso8601 # => "2016-11-23T19:18:36+00:00"
Time.zone.now.iso8601 # => "2016-11-23T19:18:44Z"
Since the last_seen JSON field stored dates using ActiveSupport::TimeWithZone, I tried changing the SQL queries to match that format, but same problem:
last_seen: {"1"=>"2016-10-20T14:30:00Z"}
SELECT COUNT(*) FROM "public"."users" WHERE ((last_seen->'1')::text <> 'null') AND ((last_seen->'1')::text > '2016-01-23T19:03:11Z') AND ((last_seen->'1')::text <= '2016-11-23T19:01:10Z')
=> 0
Then I changed last_seen JSON to have the second format with DateTime, and queried with DateTime instead with the same problem.
You say that your JSON column contains things like:
{"1":"2016-11-21T22:17:47.269Z"}
The value in that object is a real ISO-8601 timestamp. The queries that ActiveRecord is producing:
SELECT "public"."users".*
FROM "public"."users"
WHERE ... ((last_seen->'1')::text > '2016-11-12 18:13:03.432534') ...
are using not-quite-ISO-8601 timestamps, not the missing T between the date and time components in '2016-11-12 18:13:03.432534'. The results of your text comparisons will depend on how 'T' and ' ' compare and that's not guaranteed to be what you want it to be or even to be consistent across platforms.
If you're going to do this sort of thing you'll need to make sure the formats are consistent. I'd go with strict ISO-8601 since that is the One True Timestamp Format and it will behave consistently everywhere. The #iso8601 method will take care the formatting for you:
User.where("(last_seen->'?')::text <> 'null'", id)
.where( "(last_seen->'?')::text > ?", id, dt_start.iso8601)
.where( "(last_seen->'?')::text <= ?", id, dt_end.iso8601)
Calling #iso8601 yourself will give ActiveRecord a string so you'll bypass whatever timestamp-to-string formatting AR wants to use. There's also a precision argument to iso8601 if you one second precision isn't good enough.
As an aside, are you sure that JSON is the right approach to this? A separate table might be a better fit.

Search records between two dates - Ruby on Rails

I am trying to create a search that returns records between two dates (today and a future date).
I can get it to return several records no problem if I use the following code in my model (film.rb):
def self.date_search(search_string)
self.where("release_date >= ?", search_string )
However, when I try something like the following, I receive syntax errors:
def self.date_search(search_string)
date = Date.today
self.where("release_date = created_at > date.strftime("%F") AND created_at < ? ", search_string )
I am still very new to Ruby so any help sorting out my syntax and code would be much appreciated.
Try:
def self.date_search(search_string)
self.where({release_date: Time.now..search_string})
end
This will give you entries where release_date is between the current time and the search_string (inclusive of the search string since you use two dots(..), it would be exclusive of the search string if you used three dots (...)

Select records based on date comparison

I have a bunch of "rides" in my database. I want to display just the ones whose "date_range_end" field (which is a Date) is after the current date. In my controller I have this:
Ride.find(:all, :conditions => [ :date_range_end > Date.today ])
but that causes this error: comparison of Symbol with Date failed
So how do I perform this query?
Ride.where('date_range_end > ?', Date.today).all

Get records where date.year == some_year in Rails

I have a date column in my database in a Rails 3.1 app, and I want to be able to get the records where the date's year matches a specific year.
I tried where(:date.year == year) but of course I got NoMethodError: undefined method 'year' for :date:Symbol. Is it possible to do this type of query?
You can use a scope to build something like:
scope :for_year, lambda {|date| where("date >= ? and date <= ?", "#{date.year}0101", "#{date.year}1231")}
In your Model:
scope :by_year, lambda { |year| where('extract(year from created_at) = ?', year) }
In your Controller:
#courses = Course.by_year(params[:year])
Jesse gave you, I think, the idea for the actual solution, but to explain why this failed - it's because it tried to evaluate ".year" as a method on the symbol you passed it: ":date".
The word :date is just a parameter to tell "where" which value it will later use to construct the SQL query to pass to the db.
It doesn't turn into the actual date of the record. But the ".year" will evaluate as you're passing it as a parameter, before anything has been done with the ":date" symbol.
Assuming your date format is: YYYY-MM-DD HH:MM:SS
Try this:
where(Date.strptime(:date, "%Y-%m-%d %H:%M:%S").year == year)
OR
where(["YEAR(?) = ?", :date, year])

Resources