I have that kind of script.
Search_controller.rb :
def results
#users = #search.cached_users_count.page(params[:page]).per_page(10)
end
Search.rb Model :
users = users.order('current_sign_in_at DESC')
def cached_users_count
Rails.cache.fetch(["user_count", expires_in: 5.minutes]) { users }
end
What i want to do is cache all query because when some user will login after search results are executed, will_paginate replace results on next page - because just logged user go on first position and results are changed for order by current_sign_in_at
Can somebody help me with that ? Right now no cache is made , for every search results theres is sql response.
There are a few things to change in your current implementation to get the behavior you're looking for.
The users object you're starting with is returning a relation, but
only the scope, which means it is not storing results as you expect.
To store the results of the query you'll need to load them using a
query method. Use all if in Rails 3, or load if in Rails 4:
users.order('current_sign_in_at DESC').all #Rails 3
users.order('current_sign_in_at DESC').load #Rails 4, all is deprecated
The syntax for the Rails cache is incorrect if you really want it to
expire. You're passing expires_in as part of the key, which is an
array [] in your current code. You want to just pass normal
parameters, with expires_in as an option.
Rails.cache.fetch("user_count", expires_in: 5.minutes) { users }
See the following for a similar question on SO as well:
Confusion caching Active Record queries with Rails.cache.fetch
Related
i think i used the right terminology for what i need, i currently have a database call in my home_controller that is returning a call to my database with all the entries in that table specified, Freelancer.
There is an attribute on these records that has either a true or false value, which is "featured".
I need a way to call a sort method, or some other way, on that object with the true being first and then the false being afterwards, i tried using this code
def index
#freelancers = Freelancer.all
p 'below im outputting featured freelancer i hope'
#freelancers.sort_by { |row| [row.featured ? 0 : 1, row.id]}
p #freelancers
end
But unfortunately this did not work, can anyone advise me on a way to get this to work? Id rather have the sorted object returned as is, rather then assigning it to a new one. Just for future features of adding pagy and a filter by cost.
Use order method
def index
#freelancers = Freelancer.order(featured: :desc)
end
Here's the situation:
I have an Event model and I want to add prev / next buttons to a view to get the next event, but sorted by the event start datetime, not the ID/created_at.
So the events are created in the order that start, so I can compare IDs or get the next highest ID or anything like that. E.g. Event ID 2 starts before Event ID 3. So Event.next(3) should return Event ID 2.
At first I was passing the start datetime as a param and getting the next one, but this failed when there were 2 events with the same start. The param start datetime doesn't include microseconds, so what would happen is something like this:
order("start > ?",current_start).first
would keep returning the same event over and over because current_start wouldn't include microseconds, so the current event would technically be > than current_start by 0.000000124 seconds or something like that.
The way I got to work for everything was with a concern like this:
module PrevNext
extend ActiveSupport::Concern
module ClassMethods
def next(id)
find_by(id: chron_ids[current_index(id)+1])
end
def prev(id)
find_by(id: chron_ids[current_index(id)-1])
end
def chron_ids
#chron_ids ||= order("#{order_by_attr} ASC").ids
end
def current_index(id)
chron_ids.find_index(id)
end
def order_by_attr
#order_by_attr ||= 'created_at'
end
end
end
Model:
class Event < ActiveRecord::Base
...
include PrevNext
def self.order_by_attr
#order_by_attr ||= "start_datetime"
end
...
end
I know pulling all the IDs into an array is bad and dumb* but i don't know how to
Get a list of the records in the order I want
Jump to a specific record in that list (current event)
and then get the next record
...all in one ActiveRecord query. (Using Rails 4 w/ PostgreSQL)
*This table will likely never have more than 10k records, so it's not catastrophically bad and dumb.
The best I could manage was to pull out only the IDs in order and then memoize them.
Ideally, i'd like to do this by just passing the Event ID, rather than a start date params, since it's passed via GET param, so the less URL encoding and decoding the better.
There has to be a better way to do this. I posted it on Reddit as well, but the only suggested response didn't actually work.
Reddit Link
Any help or insight is appreciated. Thanks!
You can get the next n records by using the SQL OFFSET keyword:
china = Country.order(:population).first
india = City.order(:population).offset(1).take
# SELECT * FROM countries ORDER BY population LIMIT 1 OFFSET 1
Which is how pagination for example often is done:
#countries = Country.order(:population).limit(50)
#countries = scope.offset( params[:page].to_i * 50 ) if params[:page]
Another way to do this is by using would be query cursors. However ActiveRecord does not support this and it building a generally reusable solution would be quite a task and may not be very useful in the end.
Ok so I have an app that allows users to pull App Store data, specifically top free top paid etc. The various attributes are quite limited, but users can filter by category and country. So obviously this leads to a lot of repeated queries, now normally this wouldn't be a problem, but I also use this data with google api which has a credits system. So What I want to do is save these results in my database if the results are unique. I have this all set up and fine but my only hang up is how I determine if a query has been made before, so my solution is to make a hashtable that stores all queries that have been made before and if not NULL(nil) then I call the api to fetch the data then create a new record.
Issue is the App Store refreshes every day or so(not exactly sure the schedule but will look it up later). I would like to have this Hashtable reference function refresh or reset itself to all NULL at this interval.
What would be the most efficient or simple way to start a refresh for this? Additionally I am kinda new to rails, so where should I place this function? In the helper modules? Controller?
Thanks!
Edit:
ok so here is my HashTable helper module
module MapsHelper
queryHistoryLookUp = {}
i = 0
31.times do |i|
queryTableLookup.merge!(i =>[] )
end
def queryTableLookup(asciiNum, queryString)
if queryTableLookup[asciiNum % 31].size == 0
queryTableLookup[asciiNum % 31].push(queryString)
else
a = queryTableLookup[asciiNum % 31].size
arrayOfQueries = queryTableLookup[asciiNum % 31]
a.times do |i|
if arrayOfQueries[i] == queryString
return true
else
return false
end
end
end
end
end
def queryHash(query)
asciSum = 0
query.each_char do |i|
asciSum += i.sum
end
queryTableLookup(asciSum, query)
end
end
additionally I am kinda new to rails, can I interact with these functions using Javascript, since on the client side I create the string query.
In my opinion, your best bet would be to use the Rails cache system. It provides a method of caching data, with an optional expires_in time.
From the docs:
http://guides.rubyonrails.org/caching_with_rails.html#low-level-caching
class MyModel < ActiveRecord::Base
def self.get_api_data(key)
Rails.cache.fetch("my_model/api_data:#{key}", expires_in: 12.hours) do
SomeService::API.get_data(key)
end
end
end
In your hash (which I think it could exist in a class variable) you can store both the query and the last access datetime:
Suppose you have a hash as class variable to the Foo class with name cache and that the query variable is your current query that you want to check.
if Foo.cache[query].nil? || (DateTime.now - Foo.cache[query].last_fetch).to_i > 0
results = your_method_to_fetch_data_for(query)
Foo.cache[query] = {:results => results, :last_fetch => Datetime.now}
else
results = Foo.cache[query][:results]
end
I have a dashboard(esque) view in a Rails app which is showing some data in similar ways but broken out into many time periods.
I have some code in my controller like so:
#issues_this_month = Issue.where('issues.created_at BETWEEN ? AND ?', DateTime.now.in_time_zone.beginning_of_month, DateTime.now.in_time_zone.end_of_month)
and I also want to create a variables which shows issues this year and issues all time so I have this code:
#issues_this_year = Issue.where('issues.created_at BETWEEN ? AND ?', DateTime.now.in_time_zone.beginning_of_year, DateTime.now.in_time_zone.end_of_year)
I am curious if someone can think of a good way of doing one query, and from that inferring the date ranges all while avoiding the extra queries. Should I pass the results to a helper method and do the logic there?
in the model... you can define
def date
self.created_at.to_date
end
then in the controller
start = Date.today.beginning_of_year
end = Date.today.end_of_year
#issues_this_year = Issue.where(create_at: start..end).group_by(&:date)
now you have a hash of [month_1, {issues that exist in month_1}, month_2, {issues that exist in month_2}, etc]. play with it in the console to find the proper keys... #issues_this_year.keys
How about defining a method like
def self.in_timeframe(start_time=DateTime.now.in_time_zone.beginning_of_month,
end_time=DateTime.now.in_time_zone.end_of_month)
Issue.where('issues.created_at BETWEEN ? AND ?', start_time, end_time)
end
You can now invoke this as follows:
Issue.in_timeframe # For issues in the month
Issue.in_timeframe(x,y) # For issues within x-y timeframe
If you want the data in a single query, you could do stuff like:
def self.in_timeframes(time_frames)
data = {}
times_frames.each do |time_frame|
data[time_frame[:name]] = Issue.in_timeframe(time_frame[:srtart]. time_frame[:end])
end
data
end
You can invoke the above method using:
time_frames = [{:name=>"month"},
{:name=>"x-y", :start=>x, :end=>y}]
Issue.in_timeframes(time_frames)
Given a query like:
current_user.conversations.where("params[:projectid] = ?", projectid).limit(10).find(:all)
params[:projectid] is being sent from jQuery ajax. Sometimes that is an integer and the above works fine. But if the use selects "All Projects, that's a value of '' which rails turns into 0. which yields an invalid query
How with rails do you say search params[:projectid] = ? if defined?
Thanks
I think you may have mistyped the query a bit. "params[:projectid] = ?" shouldn't be a valid query condition under any circumstances.
In any case, you could do some sort of conditional statement:
if params[:project_id].blank?
#conversations = current_user.conversations.limit(10)
else
#conversations = current_user.conversations.where("project_id = ?", params[:project_id]).limit(10)
end
Although, I'd probably prefer something like this:
#conversations = current_user.conversations.limit(10)
#converstaions.where("project_id = ?", params[:project_id]) unless params[:project_id].blank?
Sidenotes:
You don't have to use .find(:all). Rails will automatically execute the query when the resultset is required (such as when you do #conversations.each).
Wherever possible, try to adhere to Rails' snakecasing naming scheme (eg. project_id as opposed to projectid). You'll save yourself and collaborators a lot of headaches in the long run.
Thanks but if the where query has lets say 3 params, project_id, project_status, ... for example, then the unless idea won't work. I'm shocked that Rails doesn't have a better way to handle conditional query params
EDIT: If you have multiple params that could be a part of the query, consider the fact that where takes a hash as its argument. With that, you can easily build a parameter hash dynamically, and pass it to where. Something like this, maybe:
conditions = [:project_id, :project_status, :something_else].inject({}) do |hsh, field|
hsh[field] = params[field] unless params[field].blank?
hsh
end
#conversations = current_user.conversations.where(conditions).limit(10)
In the above case, you'd loop over all fields in the array, and add each one of them to the resulting hash unless it's blank. Then, you pass the hash to the where function, and everything's fine and dandy.
I didn't understand why you put:
where("params[:projectid] = ?", projectid)
if you receive params[:project] from the ajax request, the query string shouldn't be:
where("projectid = ?", params[:projectid])
intead?
And if you are receiving an empty string ('') as the parameter you can always test for:
unless params[:projectid].blank?
I don't think i undestood your question, but i hope this helps.