Going Through Users in Database Individually - ruby-on-rails

I have been searching and searching for help on this question, but I have found no luck. I am wanting to have a page that displays each user in my database one at a time, and when I press a 'Next' button, it will go to the next user in the database and display their information. I have been seeing this code everywhere:
<% #users.each do |user| %>
<li>
# Add some code
</li>
<% end %>
but this is displaying every user all at once on my page. I would like to only display a single user at a time. If anyone could direct me to a good source or has a simple explanation, I would be forever grateful!

You just need to paginate the #users records. I prefer using will_paginate gem.
After including it in your gem file, you may use it like:
#users = User.paginate(:page => 1, :per_page => 1)
For basic understanding, the gem basically uses limit and offset in SQL query to attain the record. If you don't want to use the gem, then you may try like:
User.limit(1).offset(params[:offset].present? ? params[:offset] : 0)
And on clicking on next you may pass the incremented value of offset each time as parameter.

have a look at the kaminari gem. You will be able to paginate your results one by one (one result per page, hence the name).

Related

Boost query performance and showing records in Ruby on Rails

I have a table called Person with roughly a million records. I would like to display each of them as a card on the index page, but I am having performance issues. The view is rendered very slowly and is causing significant lag.
I simply had #persons = Person.all but it didn't work since it just crashed my app. Now, I am using the Person.find_in_batches method, which now allows me to display all the records within the page. Not only it takes a long time to load up the records but when I try to click a few buttons on the page, it either freezes or lags heavily. I'm lost on how I can improve performance.
For the record, here is my following code:
Person controller:
def index
batch_size = 5000
#people_in_batches = Person.find_in_batches(batch_size: batch_size)
end
Index view:
<% #people_in_batches.each do |batch|%>
<% batch.each do |person| %>
<div>
<span><%= person.name %></span>
<span><%= person.date_if_birth.strftime("%m/%d/%Y") %></span>
</div>
<% end %>
<% end %>
Thank you!
It doesn't matter how you query the million records, it still a million records.
What you should do is consider adding pagination on your page, so you would only needs to fetch records for the corresponding page. Just like Gmail.
So on the backend, you can use gem like https://github.com/kaminari/kaminari to help you with pagination.
Other than that, you should also consider adding filters on the page too, e.g. Active / Not Active, to reduce the number of records on page load.

How to use pagination in rails 4.2.5 and also have the ability to reorder the list with ajax?

I'm working on an forum-type app in Rails v 4.2.5. My index page is a list of all the questions being discussed in the application and they are default sorted by the created_at date. I am also using the Kaminari gem to paginate all of the questions (25 per page). I originally had my app set up like this:
Questions Controller:
def index
#questions = Question.order(:created_at).page params[:page]
end
Index View:
# I render a partial that iterates through the questions list to display
# the title of the questions, then I include the paginate code below.
<div class="pagination">
<%= paginate #questions %>
</div>
I eventually decided I wanted users to be able to sort the questions by different criteria (e.g., by total amount of upvotes, by total amount of responses for a question, and by recently asked questions). Right now, you can click a link corresponding to the type of sort you want and it will AJAX the new sorted list (a partial) onto the page. However, when I do this, the pagination does not work and when I click to see the second page of the results, everything becomes unsorted.
Index View with Sort Links:
<div class="sort_selection">
<h3> Sort By: </h3>
<%= link_to "By Upvotes", "/questions/top?sort=votes", class: "question_sort_link" %>
<%= link_to "Answers Provided", "/questions/top?sort=answers", class: "question_sort_link" %>
<%= link_to "Recently Asked", "/questions/top?sort=recent", class: "question_sort_link" %>
</div>
Index Controller:
def top
case params[:sort]
when "votes"
#questions = Question.sort_by_votes #sort_by_votes is a method in my Question model that performs a SQL query
when "answers"
#questions = Question.where.not(answers_count: nil).order(answers_count: :desc).limit(25)
when "recent"
#questions = Question.order(created_at: :desc).limit(25)
end
render partial: 'questions_list', layout: false
end
Javascript AJAX
$(document).on("click", ".question_sort_link", function(event){
event.preventDefault();
$.ajax({
method: "get",
url: $(this).attr("href")
}).done(function(sorted){
$('.questions_show_sorted').replaceWith(sorted);
});
});
I fooled around with the placement of the <%= paginate #questions %> in the view, as well as removed the 25 limit in my controller and added .page params[:page] after all of the queries in the Top route but I still cannot get the pagination to work after I've AJAX'ed a sorted list onto the page. Does anyone have any suggestions?
When you are switching pages the data about sorting is lost, because you are reloading site with different parameters. You can either try to pass this data (the column you are going to sort, and info is it asc or desc) to the new page, and sort it before loading, or paginate it using AJAX (but that means loading everything at the first load). I can't tell about the "pagination does not work problem", because I don't know what you mean.
In general, this thing you are trying to do is rather complicated, and there is no simple solution for that. There is a library for JS called Datatables that (in theory) makes it easier. There is another library called jQ-Bootgrid, and a ruby gem called "Smart listing".
I think you need to provide the pagination links in your ajax response and replace them in your javascript callback.
I assume that you return html rather than json, which will make this a bit awkward. Perhaps you could build up a json response with pagination links and html content
{
next: /list?page=3,
prev: /list?page=1,
content: "<ul>
<li>foo</li>
<li>bar</li>
</ul>"
}

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.

Rails link_to item with next position in array

I'm trying to create links to navigate through an array. I have three classes:
fsets, line_items, and exemplars.
In my app I have it so on a page a user is shown an image. I want the user to be able to click a next/previous link to go to the next element in the array. I can currently display all the images on the page but can't find out how to create the next/previous links. Elsewhere in the app I have used the following to go to the next record in the db but can't figure out how to implement the same for the array.
def previous
Exemplar.where(["id < ?", id]).last
end
def next
Exemplar.where(["id > ?", id]).first
end
I was hoping that if I could return the index position in the array I could link_to the next one but I can't figure out how to do that. I started by displaying the current position of the element and the previous/next positions.
next: <%= (params[:pos].gsub(/\D/, '').to_i + 1) %>
current: <%= params[:pos].gsub(/\D/, '').to_i %>
previous: <%= (params[:pos].gsub(/\D/, '').to_i - 1) %>
I am passing the index position of each element in the params using the :pos. So when the page first loads the :pos is set to 0. There are links on the bottom of the page allowing the user to jump to items in the array. Those links pass the index position as well. I know there is an easier way to do all this and I've gone down the wrong path/way of thinking about all of this. Just thought it would be helpful to show what I've tried even if completely wrong.
This gem will do what you need. https://github.com/mislav/will_paginate
If you can link to your github i might be able to help more. You will want to extract that pagination logic out to somewhere that is not the activerecord model.

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