#search = Sunspot.search(Event, Person, Organization) do
keywords params[:q]
order_by(:score)
end
Based on the search results I'd like to create a list of Models with counts for each model.
Events (12)
People (5)
Organizations (3)
Is there a way to do this type of grouping in Sunspot?
<% #search.each_hit_with_result do |hit, result| -%>
<%= result.class %> <!- Gives me Model, but repeated -->
<% end %>
There is probably a smarter way, but one possible way is to get array of the classes like this
classes = #search.results.map(&:class) # this will give array of returned classes
then do as suggested in this link
h = Hash.new(0)
classes.each { | v | h.store(v, h[v]+1) }
# h = { 3=>3, 2=>1, 1=>1, 4=>1 }
hope that helps
Related
I have written some code to search via a couple of attributes in all my Recipe records. The code works, but I would like some input on if it's ok, or how to make it better/faster.
I have a Recipe model with various attributes including name:string and ingredients:[array of integers] (postgres database). The ingredients are the ID's of a separate model Ingredient. This is a learning experience, I don't want to use any gems.
My form
index.html.erb
<%= form_tag recipes_path, :method => 'get' do %>
<p>
<%= text_field_tag :search, params[:search] %>
<%= collection_select( :ingredients, :ingredient_ids, Ingredient.all, :id, :name, {:include_blank => false, include_hidden: false}, { :multiple => true } ) -%>
<%= submit_tag "Search" %>
</p>
<% end %>
recipes_controller.rb
def index
#recipes = Recipe.search(params[:search], params[:ingredients])
end
recipe.rb
def self.search(search, ids)
array = []
if search && !search.empty?
meals = where('name ILIKE ?', "%#{search}%")
meals.each do |meal|
array.push(meal)
end
if ids && !ids.empty?
ingredients(array, ids)
else
return array
end
elsif ids && !ids.empty?
ingredients(all, ids)
else
all
end
end
def self.ingredients(meals, ids)
newarray = []
if ids
meals.each do |me|
a = me.ingredients
b = ids[:ingredient_ids].map(&:to_i)
if (b - a).empty?
newarray.push(me)
end
end
return newarray
else
return meals
end
end
This works fine at the moment as I don't have many records, but I don't trust that it'll be very fast if I had hundreds or thousands of records. Any advice on improving things?
If you know you're going to be searching on one or more columns frequently, try adding a database-level index for those columns. Without an index, any search will be O(n) time where n is the number of records. However, if you use an index, a search will be O(log(n)) time, because with the data ordered by your search column, you can binary search through it.
Take a look at http://weblog.rubyonrails.org/2011/12/6/what-s-new-in-edge-rails-explain/ for more information at how to check if your query performance can be improved.
Two other things to consider performance-wise:
1) For your all condition, you might be returning waaaay more records than you want. You may want to consider using pagination (I know you mentioned no other gems, but there are some great gems for pagination out there).
2) If you do actually want to return a ton of records at once, consider using ActiveRecord's batching (I usually use #find_each) to ensure you don't load everything into memory at once and end up OOM-ing.
I need to make a helper that takes multiple values related to a User and translate them and return them as a string to my view.
I was thinking about something like this:
in the helper:
def interests(user)
#all_interests = user.interests
#all_interests.each_with_index do |interest|
t('user.#{interest}') + ", "
if index == #all_interests.size - 1
t('user.#{interest}') + "."
end
end
In the view:
<p> My interests are: <%= interests(#user) %> </p>
The desired result would be for example:
My interests are: Engineering, Sports, Gardening.
But at this moment it returns an array like following:
My interests are: ["Engineering", "Sports", "Gardening"]
Edit: All the values are correctly translated in my i18n file.
You should map the results and join them with a ', ' in your case:
def interests(user)
user.interests.map {|interest| t("user.#{interest}") }.join(', ')
end
BTW, there is no need to set an instance variable. The helper method is returning the result already.
Better use array map+join:
def interests(user)
user.interests.map{|i| t("user.#{i}")}.join(', ')
end
And in view:
<p> My interests are: <%= interests(#user) %>. </p>
Using Rails 4.2
I have two models, suppliers and clients. Both models contain a name (string) and email (string). They do not have any relationship between them.
I would like to generate a list of all the names and emails from both suppliers and clients. In this list I would also like to know if the partner is a supplier or client.
Controller
#suppliers = Supplier.all
#clients = Client.all
#all_partners = (#suppliers + #clients).sort { |x, y| x.name <=> y.name }
View
<% #all_partners.each do |partner| %>
<%= partner.name %>, <%= partner.email %>, <%= partner.type %>
<!-- I need some way to know if the partner type is a supplier or client -->
<% end %>
How can I put in which type of partner it is? Is there a way to do this with one single AR call or query? This is basically how to use an SQL Union statement in Rails.
You could get the class name of the object I believe <%= partner.class.model_name.human %>
Thanks for the help all.
I ended up using the same controller as in the question, with some additional information in the view.
View
<% #all_partners.each do |partner| %>
<%= partner.name %>, <%= partner.email %>, <%= partner.try(:client_type) %>, <%= partner.class.model_name.human %>
<% end %>
Union in ActiveRecord works only within a single model. You could use union for two different tables using raw SQL, something like this:
Supplier.connection.execute("(SELECT id, ..., 'suppliers' as table FROM suppliers WHERE...) UNION (SELECT id,... 'clients' as table FROM clientsWHERE...)")
but the result would be of type PG::Result.
So the best way, unfortunately, is to use two ActiveRecord queries.
OR if clients and suppliers have similar fields, you could put them in one table
class Partner < ActiveRecord::Base
default_scope where(is_supplier: true)
scope :clients, -> { where(is_supplier: false) }
end
so Partner.all will output only suppliers, Partner.unscoped - all partners
I've got a loop that looks like this:
<% current_user.brand.templates.each do |template| %>
<li><%= link_to(template.label, new_templated_document_path(template)) %></li>
<% end %>
A Template can have a category, but it's just a string and not an association. Is there a way to collect the Templates here based on category and sub-menu them accordingly? It feels like I'll have to build an array for each category in the model or something. Something like this:
categories = []
Template.categories.each do |c|
category = []
category << Template.where(category: c).all
categories << category
end
categories
Feels pretty clumsy though. Any help?
Update
I've changed it to this:
#categories = {}
Template.first.categories.each do |c|
#categories[c] = current_user.brand.templates.where(category: c)
end
But I can't seem to get the records into the hash. Is that even possible?
According to this you can use something like Template.where(category: Template.categories). You can also take a look here where it is stated that:
Client.where(orders_count: [1,3,5])
will produce
SELECT * FROM clients WHERE (clients.orders_count IN (1,3,5))
Which I think is what you want.
I think you can query the templates and group them by category, that would be much more easier
Template.all.group_by(&:category)
You will get a hash with key is the category and values are lists of templates
I'm using Rails 3.0 and the acts_as_taggable_on gem. I have a Candy model and candies can have multiple tags for flavors. Let's say
Candy1.tags #['apple', 'orange']
Candy2.tags #['orange', 'banana']
Candy3.tags #['apple', 'kiwi']
I want a list of tags with associated candies below them, like so:
Apple
Candy1
Candy3
Orange
Candy1
Candy2
...etc.
I've tried
Candy.all.group_by{ |candy| candy.tags }
but that treats the array of tags as a single entity, returning something like this:
['apple', 'orange']
Candy1
['orange', 'banana']
Candy2
Lacking a group_by_each method, whats the best way to accomplish this? Another Stack Overflow question explains how to do this in memory with simple hashes, but I wonder if there's a more database-oriented way to do it with ActiveRecord associations.
You can iterate over the candies and store them on a hash base on the tag:
grouped = {}
Candy.all.each do |candy|
candy.tags.each do |tag|
grouped[tag] ||= []
grouped[tag] << candy
end
end
At the end you will get:
{'apple' => [candy1, candy2], 'orange' => [] ...}
Hope this helps you
candies = Candy.all.inject({}) do |memo, candy|
candy.tags.each do |tag|
memo[tag] ||= []
memo[tag] << candy
memo
end
end
Now candies is a hash like this:
{
'apple' => [Candy1, Candy3],
'orange' => [Candy1, Candy2],
...
}
Which you can iterate over nicely:
candies.each do |key, value|
puts "Candy tag: #{key}"
value.each do |candy|
puts " * #{candy}"
end
end
In rails api documentation that group_by is for collecting enumerable. I think you need to try a more classic array iterator like the following since you've strings data
in views/candy/index.html.erb
<% #candys.each do |candy| %>
<%= candy.name %>
<% end %>
<%for tag in candy.tags %>
<li> <%= tag.name(or flavor %> </li> #tag.name_attribute
<% end %>
<% end %>
Let me know if it works