Fast Search within Ruby on Rails Array - ruby-on-rails

I'm having a huge array of Hashes. Each Hash a detail about a company. When i search within this Array of Hashes. it is extremely slow. How i can improve its performance?
I'm using Select method with conditions to get the different companies based on status, and division like
companies.count { |company| company.sectors.include?('educational') && company.status == 'completed' }
What is the best way to search from a huge hashes of arrays using ruby on rails ?

use SQL:
Company.where('selector like ? AND status = ?', '%educational%', 'completed').count
but you can use first for find companies
#companies = Company.includes(:my_associations).where(...)
than my sample .where('selector like ? AND......

Related

How to sanitise multiple variables into SQL query ActiveRecord Rails

In our application, the Recipe model has many ingredients (many-to-many relationship implemented using :through). There is a query to return all the recipes where at least one ingredient from the list is contained (using ILIKE or SIMILAR TO clause). I would like to pose two questions:
What is the cleanest way to write the query which will return this in Rails 6 with ActiveRecord. Here is what we ended up with
ingredients_clause = '%(' + params[:ingredients].map { |i| i.downcase }.join("|") + ')%'
recipes = recipes.where("LOWER(ingredients.name) SIMILAR TO ?", ingredients_clause)
Note that recipes is already created before this point.
However, this is a bit dirty solution.
I also tried to use ILIKE = any(array['ing1', 'ing2',..]) with the following:
ingredients_clause = params[:ingredients].map { |i| "'%#{i}%'" }.join(", ")
recipes = recipes.where("ingredients.name ILIKE ANY(ARRAY[?])", ingredients_clause)
This won't work since ? automatically adds single quotes so it would be
ILIKE ANY (ARRAY[''ing1', 'ing2', 'ing3'']) which is of course wrong.
Here, ? is used to sanitise parameters for SQL query, so avoid possible SQL injection attacks. That is why I don't want to write a plain query formed from params.
Is there any better way to do this?
What is the best approach to order results by the number of ingredients that are matched? For example, if I search for all recipes that contains ingredients ing1 and ing2 it should return those which contains both before those which contains only one ingredient.
Thanks in advance
For #1, a possible solution would be something like (assuming the ingredients table is already joined):
recipies = recipies.where(Ingredients.arel_table[:name].lower.matches_any(params[:ingredients]))
You can find more discussion on this kind of topic here: Case-insensitive search in Rails model
You can access a lot of great SQL query features via #arel_table.
#2 If we assume all the where clauses are applied to recipies already:
recipies = recipies
.group("recipies.id")
# Lets Rails know you meant to put a raw SQL expression here
.order(Arel.sql("count(*) DESC"))

How to query to return the most common foreign key in a join table with rails

I have 3 models. Project, ProjectMaterial, and Material
A Project has_many ProjectMaterials and many Materials through ProjectMaterials.
This is bidirectional, with ProjectMaterial acting as a join table with user-submittable attributes.
I'd like to query the ProjectMaterial model to find the most frequent value of material_id. This way I can use it to find the most frequently used material.
Any help with a query would be greatly appreciated. I'm stuck. Thanks in advance!
You can chain group, count and sort methods on your ActiveRecord query like this:
ProjectMaterial.group(:material_id).count.values.sort.last
The first part ProjectMaterial.group(:material_id).count gives you the hash of each {material_id0 => rows_count0, material_id1 => rows_count1, ...}. Then, you can just get the values of the hash in an array, sort it and get the last item.
One way could be pluck ids to get the array, then count the most frequent.
ids = ProjectMaterial.pluck[:material_id]
For example: Ruby: How to find item in array which has the most occurrences?
Or better, by query to get a hash with counts:
counts = ProjectMaterial.group(:material_id).count
Once you know that you get a hash, you can sort by any ruby method, picking the most frequent or the n most frequent. Example of sorting:
counts.sort_by { |_, v| v }

How to query .where on array of clients?

Using Rails 5.
I have an ordered array of clients. It's not possible for me to get an AR out of it, and this is the only way I can get these clients.
And I want to show only the clients where a certain condition is met. .where(category_id: 1), but as this is not an AR, I can't run a .where(category_id: 1) on the array of clients.
How do I achieve this? With a Scope? But how specifically? And is this still used in Rails 5?
I am basically in the same situation as second-most-upvoted in this question and basically I have an array with my clients:
ActiveRecord.find(array_of_ids), preserving order (Couldn't make the other suggestions work in the thread about turning my array into an AR)
Anyways, how can I achieve this?
I would iterate through the array and save the values in a variable that can be accessed by .where(category_id: )
array = [1,2,3,4,5,6,7]
array.each do |a|
if xyz.where("category_id (?)", a) == true
do something here
end
end
So something like xyz.where("category_id (?)", a)should be possible now.

How can I get an ActiveRecord::Relation from an array of records sorted in a specific order of their IDs?

I have a Rails engine that defines a class method top(count) on a model of choice. What this method does is grab count IDs from a sorted set (ZSET) in Redis. The idea is that each item of this model has a score, and this method is to return the "best" records. The method essentially looks like this:
def self.top(count = 1)
ids = redis.zrevrange(self.score_set, 0, count - 1)
items = self.find ids
if count == 1
return items.first
else
return items.sort { |x, y| ids.index(x.id) <=> ids.index(y.id) }
end
end
As you can see, I currently sort the records after the fact because databases (or the adapters) tend not to maintain order when finding by multiple IDs. For instance, Model.where('ID IN (?)', ids) will sort by ascending IDs rather than the order of the passed IDs.
Sorting the items returns an Array, but I'd like this method to return an ActiveRecord::Relation instead so that my users can chain it with other ActiveRecord query methods. Is there a way I can use the built-in ActiveRecord query methods to maintain order from my array of IDs? Or alternatively, is there a way to construct an ActiveRecord::Relation from an array of records?
It really depends on the underlying database you are using. I do not think rails has a pretty way to do this.
I have an application where I am doing the same thing as you. Storing ids in a Redis ZSET and then needing to fetch them in that order. Since we are using MySql we can take advantage of the field function.
Model.where('id in (?)', ids).order("field(id, #{order})")
PS: Make sure your order variable is sanitized!

How to get array with clear data from Active Record query without using map

Hi I would like to ask if its possible get array with clear data from ActiveRecord query without using map, collect or each.
names = User.find(:all, :select => "name")
return names == [#<User name:"Peter">,#<User name:"Martin">]
and I want names == ["Peter", "Martin"] without using map, collect or each. Thanks for your answers.
User.connection.select_values("SELECT name FROM users")
#=> ["francois"]
Even if ActiveRecord provided a method to do what you want (single column to array of values), it would internally use a loop (each or collect) to do it.
I don't see what is so wrong with using a loop in this case, Ruby makes it quite easy to do it.
users = User.find(:all, :select => "name").collect { |u| u.name }
No you can't do that w/o using map, collect each ... etc......
In otherwords you can't get result like this in a single query.

Resources