RoR - inline query in array transform (collect) - ruby-on-rails

I'm building a summary of data based on multiple entities - to keep things simple for eg. a list of categories and the number of items present in each category returned as json e.g.
{"report":["Fruit",35]}
#array = []
#active_rec = Category.all
#array = #active_rec.collect{ |u| [u.name, ?how to insert AR query result? }
How can I plug a value along with the name that is the result of another query eg. is it possible to perform a query inline on a current row ?
Thanks!

Made some assumptions about your date model:
Fruit.joins(:category).group('categories.id').select('categories.name, COUNT(fruits.id)')
Or (depending on how you want to handle the case of duplicate category names):
Fruit.joins(:category).group('categories.name').count('fruits.id')
Note the output will be in a different format depending on which of these you choose.

Related

Sort a resource based on the number of associated resources of other type

I have a Movie model that has many comments,
I simply want to sort them (Movies) using SQL Inside active record based on the number of associated comments per movie.
How can we achieve a behavior like this in the most efficient way.
I want to do this on the fly without a counter cache column
you can do something like this
#top_ten_movies = Comment.includes(:movie).group(:movie_id).count(:id).sort_by{|k, v| v}.reverse.first(10)
include(:movie) this to prevent n+1 in sql
group(:movie_id) = grouping based on movie for each comment
sort_by{|k,v|v} = this will result an array of array for example [[3,15],[0,10][2,7],...]
for first part [3,15] = meaning movie with id = 3, has 15 comments
you can access array #top_ten_movies[0] = first movie which has top comments
default is ascending, with reverse you will get descending comments

Query in a string column for one of the value in an array like multiple OR (using full text search)

In a rails 4 app, in one model I have a column containing multiple ids as a string with comma separated values.
"123,4568,12"
I have a "search" engine that I use to retrieve the records with one or many values using the full text search of postgresql I can do something like this which is very useful:
records = MyModel.where("my_models.col_name ## ?", ["12","234"])
This return all the records that have both 12 and 234 in the targeted column. The array comes from a form with a multiple select.
Now I'm trying to make a query that will find all the records that have either 12 or 234 in there string.
I was hopping to be able to do something like:
records = MyModel.where("my_models.col_name IN (?)", ["12","234"])
But it's not working.
Should I iterate through all the values in the array to build a query with multiple OR ? Is there something more appropriate to do this?
EDIT / TL;DR
#BoraMa answer is a good way to achieve this.
To find all the records containing one or more ids referenced in the request use:
records = MyModel.where("my_models.col_name ## to_tsquery(?)", ["12","234"].join('|'))
You need the to_tsquery(?) and the join with a single pipe |to do a OR like query.
To find all the records containing exactly all the ids in the query use:
records = MyModel.where("my_models.col_name ## ?", ["12","234"])
And of course replace ["12","234"] with something like params[:params_from_my_form]
Postgres documentation for full text search
If you already started to use the fulltext search in Postgres in the first place,I'd try to leverage it again. I think you can use a fulltext OR query which can be constructed like this:
records = MyModel.where("my_models.col_name ## to_tsquery(?)", ["12","234"].join(" | "));
This uses the | operator for ORing fulltext queries in Postgres. I have not tested this and maybe you'll need to do to_tsvector('my_models.col_name') for this to work.
See the documentation for more info.
Suppose your ids are :
a="1,2,3,4"
You can simply use:
ModelName.find(a)
This will give you all the record of that model whose id is present in a.
I just think a super simple solution, we just sort the ids in saving callback of MyModel, then the query must be easier:
class MyModel < ActiveRecord::Base
before_save :sort_ids_in_col_name, if: :col_name_changed?
private
def sort_ids_in_col_name
self.col_name = self.col_name.to_s.split(',').sort.join(',')
end
end
Then the query will be easy:
ids = ["12","234"]
records = MyModel.where(col_name: ids.sort.join(',')

Rails 4 - order by translated / localized value

I have a table called items with a type column.
This column can have one of the following values:
rock
paper
scissor
Inside my translation file:
en:
rock: Stone
paper: Wood
scissor: Weapon
How can i fetch the results and order them by the translated value using ActiveRecord?
Obviously, if I do Item.where(something: true).order('name asc') I would get the results ordered by the value inside the database (rock) and not the translated value (Stone).
I am aware of some ruby methods such as sort_by and sort to order items with ruby, but I would like to order the results in ActiveRecord for performance reasons.
I managed to solve this by using a CASE statement.
Item.select("*,
CASE
WHEN type = 'rock' THEN '#{I18n.t(:rock)}'
WHEN type = 'paper' THEN '#{I18n.t(:paper)}'
WHEN type = 'scissor' THEN '#{I18n.t(:scissor)}'
END AS translated_type
")
.where(something: true)
.order('translated_type asc')
This works fine in my case since I know which types to expect.

How to include duplicate records in query?

Given an array of part ids containing duplicates, how can I find the corresponding records in my Part model, including the duplicates?
An example array of part ids would be ["1B", "4", "3421", "4"]. If we assume I have a record corresponding to each of those values I would like to see 4 records returned in total, not 3. If possible, I was hoping to be able to make additional SQL operations on whatever is returned.
Here's what I'm currently using which doesn't include the duplicates:
#parts = Part.where(:part_id => params[:ids])
To give a little background, I'm trying to upload an XML file containing a list of parts used in some item. My application is meant to parse the XML file and compare the parts listed within against my Parts database so that I can see how much the part weighs. These items will sometimes contain duplicates of various parts so that's what I'm trying to account for here.
The only way I can think of doing it is using map...
#parts = params[:ids].map { |id| Part.find_by_id(id) }
hard to tell exactly what you are doing, are you looking up weight from the xml or from your data?
parts_xml = some_method_that_loads_xml
part_ids_from_xml = part_xml.... # pull out the ids
parts = Part.where("id IN (?)", part_ids_from_xml)
now you have two arrays (xml data and your 'matching' database records) and you can use select or detect to do in memory lookups by part_id
part_ids_from_xml.each do |part_id|
weight = parts.detect { |item| item.id == part_id }.weight
puts "#{id} weighs #{weight}"
end
see http://ruby-doc.org/core-2.0.0/Enumerable.html#method-i-detect
and http://ruby-doc.org/core-2.0.0/Enumerable.html#method-i-select

Modifying the returned value of find_by_sql

So I am pulling my hair over this issue / gotcha. Basically I used find_by_sql to fetch data from my database. I did this because the query has lots of columns and table joins and I think using ActiveRecord and associations will slow it down.
I managed to pull the data and now I wanted to modify returned values. I did this by looping through the result ,for example.
a = Project.find_by_sql("SELECT mycolumn, mycolumn2 FROM my_table").each do |project|
project['mycolumn'] = project['mycolumn'].split('_').first
end
What I found out is that project['mycolumn'] was not changed at all.
So my question:
Does find_by_sql return an array Hashes?
Is it possible to modify the value of one of the attributes of hash as stated above?
Here is the code : http://pastie.org/4213454 . If you can have a look at summarize_roles2() that's where the action is taking place.
Thank you. Im using Rails 2.1.1 and Ruby 1.8. I can't really upgrade because of legacy codes.
Just change the method above to access the values, print value of project and you can clearly check the object property.
The results will be returned as an array with columns requested encapsulated as attributes of the model you call this method from.If you call Product.find_by_sql then the results will be returned in a Product object with the attributes you specified in the SQL query.
If you call a complicated SQL query which spans multiple tables the columns specified by the SELECT will be attributes of the model, whether or not they are columns of the corresponding table.
Post.find_by_sql "SELECT p.title, c.author FROM posts p, comments c WHERE p.id = c.post_id"
> [#<Post:0x36bff9c #attributes={"title"=>"Ruby Meetup", "first_name"=>"Quentin"}>, ...]
Source: http://api.rubyonrails.org/v2.3.8/
Have you tried
a = Project.find_by_sql("SELECT mycolumn, mycolumn2 FROM my_table").each do |project|
project['mycolumn'] = project['mycolumn'].split('_').first
project.save
end

Resources