Rails, how to use searchlogic to reorder returned objects - ruby-on-rails

So I have had various forms of this working in the last while, but never all working together.
for reference I have categories / Brands / Products, with the right relationships working: the site is http://emeraldcityguitars.com to see it in action.
So in my brands controller show action:
#category = Category.find_by_url_name(params[:category_id])
#brand = Brand.find(params[:id])
#search = Product.brand_id_equals(#brand.id).category_id_equals(#category.id).descend_by_price
#products = #search.paginate(:page => params[:page])
this works fine, as is evidenced in my log:
Category Load (25.6ms) SELECT * FROM "categories"
Category Load (0.2ms) SELECT * FROM "categories" WHERE ("categories"."url_name" = 'acoustic-guitars') LIMIT 1
Brand Load (0.6ms) SELECT * FROM "brands" WHERE ("brands"."id" = 14)
Product Load (4.8ms) SELECT * FROM "products" WHERE ((products.category_id = 3) AND (products.brand_id = 14)) ORDER BY products.price DESC LIMIT 6 OFFSET 0
SQL (0.2ms) SELECT count(*) AS count_all FROM "products" WHERE ((products.category_id = 3) AND (products.brand_id = 14))
Rendering template within layouts/application
Rendering brands/show
You can see that its grabbing products descending by price.
In my Brand#show I am doing the following:
<%- form_for [#category, #brand], :html => {:method => 'get', :id => 'sort_form', :class => 'sort_form'} do -%>
<label>Sort by: </label> <%= select_tag :order, product_sort_options %>
<%= submit_tag 'Go' %>
<%- end -%>
The goal being that a user could sort by a couple different options.
I also have this in my products_helper:
def product_sort_options
options_for_select([
['', nil],
['Newest to Oldest', 'descend_by_date'],
['Oldest to Newest', 'ascend_by_date'],
['Price: Highest to Lowest', 'descend_by_price'],
['Price: Lowest to Highest', 'ascend_by_price'],
['Name', 'ascend_by_name']
])
end
The issue I am having is that if I click the drop down and do price lowest to highest it reloads the page, with "?order=ascend_by_price" at the end of the url but there is no change in the order of the products.
any help is appreciated.

You'll need to add in a call to include the value of your :order parameter. It only gets included in the search by default if you're doing something like Product.search(params[:search]) (and the order parameter would then have to be in params[:search][:order]).
So, something like this should work:
#search = Product.brand_id_equals(#brand.id)
#search.category_id_equals(#category.id)
#search.order(params[:order] || :descend_by_price)
I've split it out to make it easier to read. The last line specifies to order by the value of your order parameter or the default ordering if that's not available.

Related

Limit scope on rails-jquery-autocomplete (rails3-jquery-autocomplete) gem - where clause issue

There is a recommended solution and it seems to work. The issue is in my where clause and I'm not sure what's wrong.
For reference, here is the solution(s):
https://stackoverflow.com/a/7250426/4379077
https://stackoverflow.com/a/7250341/4379077
I am trying to scope users that are members of the current_user's family tree memberships(branches) user's within my Nodes controller. This would normally be done using this code (current_user.family_tree.memberships).
Note I have successfully set this up to autocomplete showing all users (User.all):
In my routes:
resources :nodes do
get :autocomplete_user_first_name, :on => :collection
end
In my Node controller I have the following code:
autocomplete :user, :first_name, :extra_data => [:last_name, :email],
display_value: :full_name
And in my view I have the following form:
<%= form_for node do |f| %>
<%= f.label :user_tags %>
<%= f.autocomplete_field :user_tags, autocomplete_user_first_name_nodes_path, 'data-auto-focus' => true, value: nil %>
<%= f.submit %>
<% end %>
When I attempt to add the recommended solution to my nodes controller:
def get_autocomplete_items(parameters)
items = super(parameters)
items = items.where(:user_id => current_user.family_tree.memberships)
end
I get this message:
NoMethodError - super: no superclass method "get_autocomplete_items" for #<NodesController:0x007fc516692278>:
So, I found this article https://stackoverflow.com/a/18717327/4379077 and changed it to
def get_autocomplete_items(parameters)
items = active_record_get_autocomplete_items(parameters)
items = items.where(:user_id => current_user.family_tree.memberships)
end
It works, but I get the following error
PG::UndefinedColumn: ERROR: column users.user_id does not exist, so I changed the where clause to this :id => current_user.family_tree.memberships and I get this result
User Load (0.9ms) SELECT users.id, users.first_name, "users"."last_name",
"users"."email"
FROM "users" WHERE (LOWER(users.first_name) ILIKE 'mi%')
AND "users"."id" IN (SELECT "memberships"."id" FROM "memberships"
WHERE "memberships"."family_tree_id" = $1)
ORDER BY LOWER(users.first_name) ASC LIMIT 10 [["family_tree_id", 1]]
The issue is that I believe I need to get a collection within the membership model comparing the attribute membership.user_id to user.id. What am I doing wrong in my where clause?
Are Membership objects the same thing as Users?
if not, you need to get the user_id off the membership record
This line would need to change
# use pluck to get an array of user_ids.
items = items.where(:id => current_user.family_tree.memberships.pluck(:user_id))

Calculated values from data returned by Ransack are removed when paginating with Kaminari

On my Rails application, I am performing calculations on the data returned by Ransack. These are:
Total count
Averages on certain fields
The averages work but as soon as I go to the next page (using Kaminari pagination) the values disappear. However, total count continues to work.
From the view, this works:
<%= #products.total_count %>
Also from the view, this works, but when going to the next page, it doesn't:
<%= number_to_percentage(#products.average(:status), precision: 0) %>
Does anyone have any ideas?
Products controller:
def index
#q = Product.ransack(params[:q])
#q.sorts = 'start_date desc' if #q.sorts.empty?
#products = #q.result.page(params[:page]).per(30)
end
This is the SQL query from when it works on the first page:
(23.7ms) SELECT AVG("products"."price_eur") AS avg_id FROM "products" WHERE (status > 100) AND "products"."above_revenue_average" = 't' AND ("products"."name" ILIKE '%a%') LIMIT 30 OFFSET 0
And the params:
Parameters: {"utf8"=>"✓", "q"=>{"name_cont"=>"a", "type_eq"=>"", "upward_trending"=>"1", "downward_trending"=>"", "category_id_in"=>[""], "country_eq"=>"", "price_eur_gteq"=>"", "price_eur_lteq"=>"", "start_date_gteq"=>"", "start_date_lteq"=>""}, "commit"=>"All"}
This is the SQL query from when it stops working on the second page:
(23.8ms) SELECT AVG("products"."price_eur") AS avg_id FROM "products" WHERE (status > 100) AND "products"."above_revenue_average" = 't' AND ("products"."name" ILIKE '%a%')
And the params:
Parameters: {"commit"=>"All", "page"=>"2", "q"=>{"category_id_in"=>[""], "country_eq"=>"", "downward_trending"=>"", "name_cont"=>"a", "price_eur_gteq"=>"", "price_eur_lteq"=>"", "start_date_gteq"=>"", "start_date_lteq"=>"", "type_eq"=>"", "upward_trending"=>"1"}, "utf8"=>"✓"}
This turned out to be quite an easy fix, just need to run the calculations on #q.result
<%= number_to_percentage(#q.result.average(:status), precision: 0) %>

optimize the query using acts-as-taggable-on rubygem

I am using acts-as-taggable-on gem on my project. However, it takes about 80s to load all the tags.
I have added eager loading in the run_controller, but it is not working.
Here is the piece of code:
def index
#runs = Measurement.includes(:tags).order( "run_name DESC" ).group( :run_name )#.descending #.order_by( :run => 'desc' )
respond_to do |format|
format.html # index.html.erb
format.json { render :json => #runs }
end
end
I am using tag_list, which is supported by acts-as-taggable-on, to display tags. So using eager loading on Measurements has no impact on the performance. The following two are the related issues in stackflow:
1.
acts_as_taggable_on: how to optimize the query?
2.
Optimizing queries with acts_as_taggable_on
I looked at the log file and found that the most time-costing part is loading the tags, like
ActsAsTaggableOn::Tag Load (69.9ms) SELECT tags.* FROM tags INNER JOIN taggings ON tags.id = taggings.tag_id WHERE taggings.taggable_id = 223866 AND taggings.taggable_type = 'Measurement' AND (taggings.context = 'tags' AND taggings.tagger_id IS NULL)
ActsAsTaggableOn::Tag Load (70.2ms) SELECT tags.* FROM tags INNER JOIN taggings ON tags.id = taggings.tag_id WHERE taggings.taggable_id = 223854 AND taggings.taggable_type = 'Measurement' AND (taggings.context = 'tags'
It got thousands of queries to load the tag and each query cost about 0.07s.
Those following codes are using for displaying tags.
= form_for (run), :remote => true, :method => :put, :html => { :class => "myform"} do |f|
=f.text_field :tag_list, :class => "tags"
Any help? Thanks.
I just came across this today. You can eager load through the tags association, as long as you don't use the tag_list method.
Mentioned here and here
Have considered switching to https://github.com/tmiyamon/acts-as-taggable-array-on . You will need to be using postgres as your database.
Take a look at this http://adamnengland.wordpress.com/2014/02/19/benchmarks-acts-as-taggable-on-vs-postgresql-arrays

How to make a search form with acts_as_taggable

I have a search form to search for images by their tags. The form kinda works, it sends the parameters to the /search_results page but it sends as this:
search_results?utf8=✓&search=squid%2C+color&x=0&y=0
And here is my form:
<%= form_tag ("/search_results"), :method => "get", :class=>"search_form" do %>
<%= text_field_tag ("search"), nil, :class => 'search_input',
:onblur=>"if(this.value=='')this.setAttribute('class', 'search_input');",
:onfocus=>"this.setAttribute('class', 'search_input_clear');"
%>
<%= image_submit_tag("search.png") %>
<% end %>
and and my route/controller:
match "/search_results/" => "index#search_results", :via => :get, :as =>"search_results"
class IndexController < ApplicationController
def search_results
#tattoos = Tattoo.tagged_with("%#{params[:search]}%")
end
But I never get any results.
Rails console shows this:
Parameters: {"utf8"=>"✓", "search"=>"color, animals", "x"=>"0", "y"=>"0"}
SQL (0.5ms) SHOW TABLES
ActsAsTaggableOn::Tag Load (0.2ms) SELECT `tags`.* FROM `tags` WHERE (name LIKE '\\%color' OR name LIKE 'animals\\%')
SQL (0.1ms) SELECT COUNT(*) FROM `tattoos` WHERE (1 = 0)
Tattoo Load (0.3ms) SELECT `tattoos`.* FROM `tattoos` WHERE (1 = 0) ORDER BY tattoos.created_at DESC
I removed the % surrounding my params and that seemed to do the trick:
def search_results
#tattoos = Tattoo.tagged_with("#{params[:search]}")
end

Named Scope Problem With "Has Many Through" Association

I'm using named scopes to process some filtering actions, and the log is showing that everything is working perfectly, except that after the app goes and finds the correct data, it then ignores what it found and just lists a find.all instead of the filtered result. Here's the details.
I have 3 models: Users, Markets and Schedules.
User has_many :schedules
User has_many :markets, :through => :schedules
Market has_many :schedules
Market has_many :users, :through => :schedules
Schedule belongs_to :user
Schedule belongs_to :market
On the "show" page for each market, I display the users who sell things at that market. I also display the days of the week that such users are at the market. This data is contained in the join model (i.e. schedules).
On the market page, I need to support filtering the users by day of the week that the user is at the market.
In my Market controller, I have this:
def show
#market = Market.find(params[:id])
#users = #market.users.filter_marketdate(params[:filter])
end
In my Market model, I have this:
def self.filter_marketdate(filter)
case filter
when 'monday' then monday.all
else find(:all)
end
end
In my User model, I have this:
named_scope :monday, :include => :schedules, :conditions => {'schedules.monday' => true }
AND
def self.filter_marketdate(filter)
case filter
when 'monday' then monday.all
else find(:all)
end
end
In my show view for Markets, I have this:
<%= link_to_unless_current "All", :filter => 'all' %>
<%= link_to_unless_current "Mon", :filter => 'monday' %>
<% #market.schedules.each do |c| %>
<%= link_to c.user.name, c.user %>
<% end %>
Here's the weird part. The following is my log output when I select the Monday filter. If you look at the Select Schedules line of the output, you can see that the query is finding a limited number of user IDs. These are, in fact, the correct user IDs that I want to display for my filtered query. The part I don't understand is that after the app does the query perfectly, it then goes and loads all of the users instead of just the filtered results. I'm not sure what I'm missing.
Processing MarketsController#show (for ip at 2009-09-21 05:19:25) [GET]
Parameters: {"id"=>"1", "filter"=>"monday"}
Market Load (0.8ms) SELECT * FROM "markets" WHERE ("markets"."id" = 1)
User Load (7.3ms) SELECT "users".* FROM "users" INNER JOIN "schedules" ON "users".id = "schedules".user_id WHERE ((("schedules".market_id = 1)) AND ("schedules"."monday" = 't'))
Schedule Load (4.3ms) SELECT "schedules".* FROM "schedules" WHERE ("schedules".user_id IN (16,23,25,30,39,61,73,75,85,95,97,111,112,116,121,123,126,134,136,145,160,165,171,188,189))
Rendering template within layouts/application
Rendering markets/show
Schedule Load (14.3ms) SELECT * FROM "schedules" WHERE ("schedules".market_id = 1)
User Load (0.8ms) SELECT * FROM "users" WHERE ("users"."id" = 2)
User Load (0.8ms) SELECT * FROM "users" WHERE ("users"."id" = 8)
It goes on to list every user who is connected to this particular market, essentially doing a find.all.
UPDATE
In response to Brad's comment below, I tried changing the code in my markets/show view to the following, but I got the same result. I agree that the problem is in here somewhere, but I can't quite figure out how to solve it.
<%= link_to_unless_current "All", :filter => 'all' %>
<%= link_to_unless_current "Mon", :filter => 'monday' %>
<% #market.users.each do |user| %>
<%= link_to user.name, user %>
<% end %>
In your view, you are iterating over each market.schedule, not over the #users variable that you set. In fact, you aren't even using #users in the code you show us.
Shouldnt that be:
#Market.rb
has_many :users, :through => :schedules
Beside this, maybe this plugin helps you:
http://github.com/ntalbott/query_trace
It provides feedback for each sql statement and where it comes from..
f.e.:
Before:
Schedule Load (0.023687) SELECT * FROM schedules WHERE (schedules.id = 3) LIMIT 1
Resource Load (0.001076) SELECT * FROM resources WHERE (resources.id = 328) LIMIT 1
Schedule Load (0.011488) SELECT * FROM schedules WHERE (schedules.id = 3) LIMIT 1
Resource Load (0.022471) SELECT * FROM resources WHERE (resources.id = 328) LIMIT 1
After:
Schedule Load (0.023687) SELECT * FROM schedules WHERE (schedules.id = 3) LIMIT 1
app/models/available_work.rb:50:in `study_method'
app/helpers/plan_helper.rb:4:in `work_description'
app/views/plan/_resource_schedule.rhtml:27:in `_run_rhtml_plan__resource_schedule'
app/views/plan/_resource_schedule.rhtml:24:in `_run_rhtml_plan__resource_schedule'
app/views/plan/_schedule_listing.rhtml:5:in `_run_rhtml_plan__schedule_listing'
app/views/plan/_schedule_listing.rhtml:3:in `_run_rhtml_plan__schedule_listing'
app/views/plan/_schedule_listing.rhtml:1:in `_run_rhtml_plan__schedule_listing'
app/views/plan/index.rhtml:6:in `_run_rhtml_plan_index'
vendor/plugins/textmate_footnotes/lib/textmate_footnotes.rb:60:in `render'
Resource Load (0.001076) SELECT * FROM resources WHERE (resources.id = 328) LIMIT 1
app/models/available_work.rb:54:in `div_type'
app/helpers/plan_helper.rb:6:in `work_description'
app/views/plan/_resource_schedule.rhtml:27:in `_run_rhtml_plan__resource_schedule'
app/views/plan/_resource_schedule.rhtml:24:in `_run_rhtml_plan__resource_schedule'
app/views/plan/_schedule_listing.rhtml:5:in `_run_rhtml_plan__schedule_listing'
app/views/plan/_schedule_listing.rhtml:3:in `_run_rhtml_plan__schedule_listing'
app/views/plan/_schedule_listing.rhtml:1:in `_run_rhtml_plan__schedule_listing'
app/views/plan/index.rhtml:6:in `_run_rhtml_plan_index'
vendor/plugins/textmate_footnotes/lib/textmate_footnotes.rb:60:in `render'
UPDATE:
You have to call
<%= link_to_unless_current "All", :filter => 'all' %>
<%= link_to_unless_current "Mon", :filter => 'monday' %>
<% #users.each do |user| %>
<%= link_to user.name, user %>
<% end %>

Resources