Strange ordering of ActiveRecord object - ruby-on-rails

I am trying to get the records to order by :id in the view. I have records ordered by :id in the controller, like so
#bands = Band.where(available: true).order(:id)
Everything appears normal when the app starts, but as records are updated, the ordering behaviour goes wonky. Most often, recently edited records move to the very end, but not always. This shouldn't happen since the records are ordered by :id which shouldn't be changing
For context, in the view is something like
<% #bands.each do |b| %>
<%= b.name %>
<% end %>
Also note, in the rails console, both of these return the correctly ordered results (which makes this problem even stranger):
ActiveRecord::Base.connection.execute("SELECT * FROM bands WHERE available = 'true' ORDER BY id")
and
Band.where(available: true).order(:id)
Also note, when I load the index view, and observe the rails server, I can see that the results are not in the correct order.
I can also see the sql query that has been executed, and it ignores the order part, it simply doesn't have any mention of order in the query

Actually, I don't think it is possible.
But, try this:
#bands = Band.where(available: true).order(:id).to_a

Related

RAILS Active record search with multiple ordering -- out of order

I want to display reports that meet criteria and display them in a particular order.
Sort by user, then by report creation date of the report, newest on top
#report_results.order('created_at DESC, user_id')
Above is the code I use, however, when the data is displayed, toward the end of each user's reports, they start to show up out of order.
How do I Fix this?
View:
<% #report_results.order('created_at DESC, user_id').each do |report| %>
You could do:
variable = report_results.order('created_at DESC')
#report_results = variable.group_by(&:user_id)
This should return a hash of user_id with the respective reports ordered.
<% #report_results.order('user_id').order('created_at DESC').each do |report| %>
Not sure why this works, I thought the last order command would overwrite the previous, but it seems like it retains it.
Items now show up grouped by user_id, in descending chronological order.

Rails 5.2 cache key for relations generating two queries

I have a view using fragment caching for an ActiveRecord relation, e.g.
<% cache MyModel.all do %>
...
<% end %>
I see two DB queries generated in this case
SELECT COUNT(*) AS "size", MAX("my_model"."updated_at") AS timestamp FROM "my_model"
SELECT "my_model".* from "my_model"
I expect the first one, and it's usually a much more efficient query. I did not expect the second one.
If I instead use:
<% cache ActiveSupport::Cache.expand_cache_key(MyModel.all) do %>
...
<% end %>
then I get only the first query with the same resulting cache key.
Is it a bug or am I doing something wrong?
EDIT: narrowed down to where this happens: see https://github.com/rails/rails/pull/29092#issuecomment-437572543
when
normalize_version
is executed, the relation does not respond to cache_version, and
therefore ends up being expanded with
to_a.
So essentially, calling Product.all.to_a and then for each object
calling cache_version, which returns nil.
Yes, this does look like a bug. Hopefully this would be fixed on this PR, since my own PR for a stopgap fix was rejected

Ruby Find last record value in has many

I'm trying to find the last Econ_Result that belongs to a Econ_Report. I want to display the last record of the Econ_Result (ordered by "release_date") for each Econ_Report on the index view. In the controller I tried to take the list of all reports and find the last result using the following:
#econ_reports = EconReport.all
if #econ_reports.econ_results.size >= 1
#last_result = #econ_report.econ_results.last.release_date
end
econ_report.econ_results.size works on the index view when I place it in for each loop. When I try to call the value of the last record I run into issues with the fact that some reports don't yet have results (a temporary issue) so I threw in the if then check in the controller which is currently failing.
Thanks in advance for the rookie help.
Since #econ_reports is a collection of EconReport objects, you can't call an instance method like .econ_results on it. Instead, you can only call it on instances within the collection:
#econ_reports.each do |econ_report|
if econ_report.econ_results.any?
last_result = econ_report.econ_results.last
end
end
However, this can be terribly inefficient for a large collection of #econ_reports: both lines with econ_report.econ_results will query the database separately, meaning that you'll query the database independently for each econ_report in the collection. This is known as the N+1 query problem.
Luckily for you, as discussed in the link, Rails has a built-in solution to optimize this code so you'll only query the database once:
<% #econ_reports.includes(:econ_results).each do |econ_report| %>
<% if econ_report.econ_results.any? %>
<% last_result = econ_report.econ_results.last %>
# do something to display last_result
<% end %>
<% end %>
If you just want the release date you might try:
#last_result = #econ_report.econ_results.order('release_date DESC').limit(1).pluck(:release_date).first
It's worth noting that a Ruby if statement generally looks like:
if condition
end
The then is almost always omitted even though it is allowed.

Remembering records returned from DB query in Rails

I currently use a query of the form
#recordsfound = Model.where(...)
This is located in my controller and returns all records matching the query. I am using pagination to show a limited number of records at a time. However, when I select to show the next page the query is run again. Is there a way to store the records returned in a variable other than an instance variable and therefore not require the query to be rerun?
Thanks a lot guys
I'm not entirely sure I understand what you mean, but if you want the query to only run once per page even on subsequent visits, you can use fragment caching.
Rails will lazy load, and then use the cache when it hits the query in the view.
<% results.each do |result| %>
<% cache result do %>
<%= result.foo %>
<% end %>
<% end %>
If you have any dependent models, you'll have to make sure you expire the cache when they get updated if necessary:
belongs_to :result, touch: true
Note that if you are in development environment the query will still run. You can change this in your development.rb config file. If you do change this setting, don't forget to revert it . Otherwise strange things will happen and you'll waste your time trying to figure out why your changes aren't visible.
config.action_controller.perform_caching = true

Is this way of calling object supposed to be bad practice when considering loading speed?

My way
controller pattern 1 (note: Here, it's calling all users!!)
#users = User.confirmed.joins(:profile)
view pattern 1 (note: Here, it only shows first 10 users but it show the number of all users!!)
<%= "ALL ("+ #users.count.to_s + " users)" %>
<% #users.limit(10).each do |users| %>
<%= render 'users/user', :user => users %>
<% end %>
Should it be just like this below if I'm considering page loading speed?
Or it won't be changed?
controller pattern 2 (note: I added limit(10), and #users_count to count all users)
#users = User.confirmed.joins(:profile).limit(10)
#users_count = User.confirmed.joins(:profile).count
view pattern 2 (note: I took it off limit(10) and use #users_count for count)
<%= "ALL ("+ #users_count.to_s + " users)" %>
<% #users.each do |users| %>
<%= render 'users/user', :user => users %>
<% end %>
If you have lazy loading disabled, then the second approach would be faster because Rails doesn't need to fetch all records from the database. You should really fetch only the records you need when performing queries.
If you have lazy loading enabled (by default), then it is the same, because the data is fetched when it is needed, so the effect will be the same. You can also put two variables in controller and write the same query as you did in the view and the data will be fetched only if and when it is needed.
#users = User.confirmed.joins(:profile)
#users_count = #users.count
#users = #users.limit(10)
You can check sql generated by the app in your rails console and then decide.
Also, if you are using profile in user.html.erb, consider using includes instead of join. Join can cause n+1 problem if you need associated records. If you don't, you do not want to fetch records you don't need. You can read more about it here, in 12 Eager Loading Associations.
The two options are exactly the same. Neither of them loads all the Users because you're just chaining scopes. The query is only run when you call .each in the view, at which point you've applied the .limit(10) anyway. I'd go with the first option because the code is cleaner.
#users.count does one query to get the count, it doesn't instantiate any User objects.
#users.limit(10).each ... does one query (actually two because you've used includes) with a limit, so it will instantiate 10 objects plus your includes.
you can try #users.find_in_batches
Please take a look
Find in batches
Please let me know
If you want speed loading
I can suggest you memcache Memcache

Resources