Paginating search results with Kaminari - ruby-on-rails

I've been trying to do this for the last 2 hours with no success, I'm sure it's something really simple :)
So, I have form where users can search things based on some keywords. Here's the form code:
<%= form_tag search_by_description_path,:method => "get" do %>
<div class="column span-3">
<label>Search</label>
</div>
<div class="column span-5">
<input type="text" name="search_keywords" id="search_keywords"/>
</div>
<div class="column span-6">
<%= collection_select :category,:id,Category.all,:id,:name,:include_blank => "Everything" %>
</div>
<div class="column span-3">
<%= submit_tag "search" %>
</div>
<% end %>
I have this in the routes.rb:
get "search_by_description" => "search#search_by_description",:as => "search_by_description"
I have this in the controller:
def search_by_description
category = params[:category_id]
kw = params[:search_keywords]
#results = Posts.where("description LIKE ?","%#{kw}%").page(params[:page])
end
Ignore the fact that I'm not keeping track of category_id. In my view I have this:
<%= render "results",:locals => {:results => #results} %>
<%= paginate(#results) %>
The problem is, the when I go to the second page, I don't see anything displayed. Looking in the console, I noticed something that for the 1st page, the following SQL gets generated:
SELECT "posts".* FROM "posts" WHERE (description LIKE '%lorem%') ORDER BY id LIMIT 25 OF
SELECT COUNT(*) FROM "posts" WHERE (description LIKE '%lorem%')
while for the second only:
SELECT COUNT(*) FROM "posts" WHERE (description LIKE '%lorem%') LIMIT 25 OFFSET 25
Please give me some suggestions, I don't want to resort to writing my own pagination :)

The root cause of this problem is ActiveRecord 3.0's bug, which will be fixed in ActiveRecord 3.1 https://github.com/rails/rails/commit/d5994ee
https://github.com/rails/rails/commit/28c73f0
And I did a monkeypatch on Kaminari to work with ActiveRecord 3.0 https://github.com/amatsuda/kaminari/blob/9d0eebe38e2a22fb8100e491a6d94839d76c868f/lib/kaminari/models/active_record_relation_methods.rb#L7-11
In short, updating your Kaminari gem to the newest one will fix your problem. Thanks!

It was a bug, or misuse, from my side. I had this in my view:
<% if results.size > 0 %>
...
<% else %>
<p>No results found</p>
<% end %>
and this was always going on the else clause for pages different than the first. If I change the results.size to results.all.size it works. However, I don't understand why it doesn't work directly with results.size.

Related

Rails 4, how to ordering using custom method

I want to order the Conversation model, using a custom method.
I found some solution:
How do you order by a custom model method that has no attribute in SQL?
and
http://awaxman11.github.io/blog/2013/10/11/sorting-a-rails-resource-based-on-a-calculated-value/ ,
but Conversation order have priority.
First- answer_percent desc,
second- order to last_answer time
(using custom model method last_answered_to_i ).
last_answered_to_i method source:
def last_answered_to_i
if Conversation.where(company_id: self.id, is_answered: true).present?
last_conversation = Conversation.where(company_id: self.id, is_answered: true).first
if last_conversation.answered_at.blank? || last_conversation.asked_at.blank?
minutes = (Time.now- last_conversation.updated_at)/1.minutes
else
minutes = (last_conversation.answered_at - last_conversation.asked_at)/1.minutes
end
minutes.to_i
else
nil
end
end
after ordering I want add pagination using kaminari gem.
#lists = Company.searchable.order("answer_percent desc").page(params[:page]).per(20)
How do I order by column and custom method and add pagination?
I think the answer depends on what you want to see in the view because some of the problem could actually be solved in how you call #lists there. Also, some of the links you found make sorting by a model method sound more difficult than it is.
In your case, you can sort your conversations by a custom method like so:
Conversation.all.sort_by(&:custom_method)
Or specifically:
Conversation.all.sort_by(&:last_answered_to_i)
Specifically, you cannot use SQL to sort or order by something not in the actual database, so you use the Ruby sort_by method. For more info on the ampersand, see this post.
For your actual view, I'm not sure really how you want to organize it. I recently did something where I needed to group my resource by another resource called "categories", and then sort the original resource by "netvotes" which was a custom model method, then order by name. I did it by:
Ordering by name in the controller: #resources = Resource.order(:name)
Grouping by category in the outer loop of the view: <% #resources.group_by(&:category).each do |category, resources| %>
Then sorting the resources by votes in the partial for resources: <%= render resources.sort_by(&:netvotes).reverse %>
The view is a bit confusing, so here is the full view loop in index.html.erb:
<% #resources.group_by(&:category).each do |category, resources| %>
<div class="well">
<h3 class="brand-text"><%= category.name %></h3>
<%= render resources.sort_by(&:netvotes).reverse %>
</div>
<% end %>
And here is the _resource.html.erb partial:
<div class="row resource">
<div class="col-sm-2 text-center">
<div class="vote-box">
<%= link_to fa_icon('chevron-up lg'), upvote_resource_path(resource), method: :put %><br>
<%= resource.netvotes %><br>
<%= link_to fa_icon('chevron-down lg'), downvote_resource_path(resource), method: :put %>
</div>
</div>
<div class="col-sm-10">
<%= link_to resource.name, resource.link, target: "_blank" %>
<p><%= resource.notes %></p>
</div>
</div>
I hope that helps you think through some more ways to address your problem.

Ruby on Rails: Grouping search results by category

I am working on a RoR WebApp. I'm trying to group results on the search page based on their taxonomy. What I want to do is to show a header for a category and list all results under that category. Something like:
CAT 1
products
CAT2
products
CAT3
.
.
I am trying using the following code:
<% if products.any? %> #products is the list of search results
<%= render :partial=> 'product_listing_feature', :locals => {:scope => scope, :scope_type => scope_type} %>
<div id="ql_product"></div>
<div class="product_rows">
<%taxons.each do |taxon|%> # taxons contains the list of unique categories in products
<div class = "product_row">
<h1><%=taxon%></h1>
<% taxonProducts = Array.new %>
<% products.each do |product| %>
<%#ptaxon = product.get_taxonomy%>
<%if #ptaxon == taxon%>
<% taxonProducts.push(product) %>
<% end %>
<% end %>
<div class ="featured_product_list">
<ul class = "featured_products">
<div class = "page">
<%= render :partial=> 'product_listing', :locals=>{:collection=> taxonProducts} %>
</div>
</ul>
</div>
</div>
<% end %>
</div>
<% end %>
Surprisingly it starts the 2nd category from a new row, but the following categories appeared jumbled up, something like
CAT1
products
CAT2
products CAT3
products
picture would give a better idea.
I am really surprised why it works only for one iteration. Could someone please help me fix this.
Thanks a lot
Way, way too much logic for a view. Just use group_by in your controller, which will give you a mapping of names to arrays of products:
products = Product.includes(:taxon).group_by { |p| p.taxon.name }

combining 2 tables, looping through the results and displaying items from each

So I'm trying to combine two tables and show the results in order of the start_date.
I've tried a few things but because its technically a nested loop its giving me double results for each item.
The code i currently have is as follows
<% #subcategory = Subcategory.all %>
<% #product = Product.all %>
<% (#product + #subcategory).each do |product, subcategory|%>
<% if product.display_on_home_page and !product.is_highlight_product and !(product == '..') or subcategory.
display_on_home_page and !subcategory.is_highlight_product and !(subcategory == '..')%>
<div class="column_entry">
<%= link_to image_tag(subcategory.image_attachment.url(:normal_page_size)), subcategories_content_url(subcategory.id), :controller=>'subcategories' %>
</div>
<% end %>
<% if product.price_from %>
<div class="column_entry">
<div class="product_special">
<span class="a">From Only</span>
<span class="b"><%= number_to_currency(product.price,:unit=>'€') %></span>
</div>
<%= link_to image_tag(product.product_image.url(:normal_page_size)), products_content_url(product.id), :controller=>'products' %>
</div>
<% else %>
<div class="column_entry">
<div class="product_special">
<span class="a">Only</span>
<span class="b"><%= number_to_currency(product.price,:unit=>'€') %></span>
</div>
<%= link_to image_tag(product.product_image.url(:normal_page_size)), products_content_url(product.id), :controller=>'products' %>
</div>
<% end %>
<% end %>
I know this is quite a long an complex statement, its supposed to loop through all of the subcategories and all of the products and display the images, there are also two different ways of displaying the price based on a boolean that says whether the price is a specific amount or it starts from a given price.
at the moment its reading through the loop but its giving me the error
undefined method `is_highlight_product' for nil:NilClass
since this is the first column in the table that is referenced and its breaking here I think that there must be some conflict in its ability to see the information stored in the table.
I'm still quite new to ruby on rails so any help or even just a nudge in the right direction would be very much appreciated.
If you would like more information just ask in the comments and I'll put it up as fast as I can.
The problem here is, when you do something like this:
(#product + #subcategory).each do |product, subcategory|
The local variable product will iterate firstly through products, then through subcategories, and the local variable subcategory will always be nil.
What you can do, a dirty way - check
if product.is_a?(Product)
# do your things
elsif product.is_a?(Subcategory)
# do other things
end

Ruby on Rails: Display objects by most recent in the view?

I have this method at the moment:
<% for comment in #critical_process.review.comments %>
<div id="comment">
<%=h comment.comment %> <br/>
<span id="comment_email">
By: <%=h comment.user.email%>
</span>
</div>
<% end %>
however I need it to display the comments in order of the most recently updated at the top and work its way down.
Thanks
Assuming the Comment model has an updated_at column, and you are using Rails 3, you can just tell ActiveRecord to order the Comment records appropriately as follows:
<% for comment in #critical_process.review.comments.order('updated_at DESC') %>
The Rails 2.x equivalent would be:
<% for comment in #critical_process.review.comments.all(:order => 'updated_at DESC') %>
Whilst this will work perfectly well, it's generally considered best to move most of your query generation into the controller, in which case you might do the following in the controller:
#comments = #critical_process.review.comments.order('updated_at DESC')
... and then iterate over the #comments collection in the view.

How do you order by a custom model method that has no attribute in SQL?

Previously I ordered my posts as this:
#posts = Post.find(:all, :order => "created_at DESC")
But now I want to replace created_at with a custom method I wrote in the Post model that gives a number as its result.
My guess:
#posts = Post.find(:all, :order => "custom_method DESC")
which fails..
It fails because you are asking your db to do the sorting.
#posts = Post.all.sort {|a,b| a.custom_method <=> b.custom_method}
Note that this becomes non-trivial when you want to start paging results and no longer wish to fetch .all. Think about your design a bit before you go with this.
Just to expand on #Robbie's answer
Post.all.sort_by {|post| post.custom_method }.reverse
As the first answer noted, order is an Active Record command that essentially does a SQL query on your database, but that field doesn't actually exist in your database.
As someone else commented, you can more cleanly run the Ruby method sort_by by using the ampersand (more info here):
Post.all.sort_by(&:custom_method)
However, things do get complicated depending on what you want to do in your view. I'll share a case I recently did in case that helps you think through your problem. I needed to group my resource by another resource called "categories", and then sort the original resource by "netvotes" which was a custom model method, then order by name. I did it by:
Ordering by name in the controller: #resources = Resource.order(:name)
Grouping by category in the outer loop of the view: <% #resources.group_by(&:category).each do |category, resources| %>
Then sorting the resources by votes in the partial for resources: <%= render resources.sort_by(&:netvotes).reverse %>
The view is a bit confusing, so here is the full view loop in index.html.erb:
<% #resources.group_by(&:category).each do |category, resources| %>
<div class="well">
<h3 class="brand-text"><%= category.name %></h3>
<%= render resources.sort_by(&:netvotes).reverse %>
</div>
<% end %>
And here is the _resource.html.erb partial:
<div class="row resource">
<div class="col-sm-2 text-center">
<div class="vote-box">
<%= link_to fa_icon('chevron-up lg'), upvote_resource_path(resource), method: :put %><br>
<%= resource.netvotes %><br>
<%= link_to fa_icon('chevron-down lg'), downvote_resource_path(resource), method: :put %>
</div>
</div>
<div class="col-sm-10">
<%= link_to resource.name, resource.link, target: "_blank" %>
<p><%= resource.notes %></p>
</div>
</div>
This is a bit more complicated than what I like but this I like to keep my sort to stay as a active record model so its bit more complicated than just
Post.all.sort_by {|post| post.custom_method }
what I do is:
ids = Post.all.sort_by {|post| post.custom_method }.map(&:ids)
Post.for_ids_with_order(ids)
this is a custom scope in the Post model
#app/models/post.rb
class Post < ApplicationRecord
...
scope :for_ids_with_order, ->(ids) {
order = sanitize_sql_array(
["position(id::text in ?)", ids.join(',')]
)
where(:id => ids).order(order)
}
...
end
I hope that this help
Well, just Post.find(:all) would return an array of AR objects. So you could use Array.sort_by and pass it a block, and since those records are already fetched, you can access the virtual attribute inside the block that sort_by takes.
RDoc: Enumerable.sort_by
Keep in mind that sort_by will return an Array, not an ActiveRecord::Relation, which you might need for pagination or some other some view logic. To get an ActiveRecord::Relation back, use something like this:
order_by_clause = Post.sanitize_sql_array(<<custom method expressed in SQL>>, <<parameters>>)
Post.all.order(Arel.sql(order_by_clause))
in rails 3 we can do this as: Post.order("custom_method DESC")
When upgrading app from rails2 to rails3

Resources