Get Unique records per attribute in Active Record - ruby-on-rails

I have Student model and I would like to get one record per term_id (one of the attributes).
Student.select(:term_id).distinct
works but the result is an array of term_ids, not the objects themselves - which is what I would like to get

Try this:
Student.pluck("DISTINCT id, term_id")

Student.select("DISTINCT term_id, `students`.* ")
Incase if you are using older versions of ruby (< 2.3.8),
Student.find(:all, :select => "DISTINCT(term_id), `students`.*")
or if you want id alone,
Student.find(:all, :select => "DISTINCT(term_id), id")
where students is your table name. You will get array of objects.

Related

QUERY: where has_many association does not contain specific value

I have two models Invoice and InvoiceEvent
Invoice has many InvoiceEvents
InvoiceEvent has a field state
I'd like to write a query to find all Invoices that DOES NOT have an InvoiceEvent with the state = 'paid'
My first attempt was
Invoice.joins(:events).where("invoice_events.state != 'paid'")
But this simply grabbed all Invoices since a paid invoice will still have other events leading up to the paid event.
First you want all the InvoiceEvents that do have a state of 'paid':
InvoiceEvent.where(:state => 'paid')
From there you can get the Invoices that have events with such a state:
InvoiceEvent.where(:state => 'paid').select(:invoice_id)
Then you can use a subquery to find all the Invoices that aren't in that list:
Invoice.where.not(
:id => InvoiceEvent.where(:state => 'paid').select(:invoice_id)
)
Note the select(:invoice_id) call. ActiveRecord will convert that to a subquery like:
where id not in (select invoice_id from ...)
so that database will be doing the work without having to pull a bunch of data in Rails-land only to send it back to the database.

Ruby on Rails: Fetch database result and search through results

I have a set of IDs for a table called "brands". I want to fetch the name column for each record in the brand table without having to re-query the database using Brand.find(brand_id). Instead, is there a way to store the database results into a variable and query the variable?
Thanks.
if you already collected the records in a var #brands, you can use
name = #brands.find {|b| b.id == brand_id}.name
not a query
This will return an array of all Brands for a brand_id
Brand.find(:all, :conditions => ["brand_id = ?", brand_id])
For a collection of brand_ids
Brand.find(:all, :conditions => ["brand_id IN (?)", [brand_id1, brand_id2]])
HTH
You can write a method in your model brand.
def get_associated_brand
b = self.brand_id
brand = Brand.find(b).name
end
From your view just call this method so that in your view you ll get brand name instead of ID like
brand.get_associated_brand
instead of
brand.brand_id
About what was commented on #Harsh Gupta's answers, maybe
#filtered_brands = Brand.where(id: <some_id>)
Then you can access all info of each brand in a regular each loop.

Querying based on two associated records

I have a product that has many variants, those variants have two attributes: Size and Color.
I want to query for the Variant based on the two attributes I pass in - I got it to work with following:
variants = Spree::Variant.joins(:option_values).where(:spree_option_values => {:id => size.id}, :product_id => prod.id).joins(:option_values)
variant = variants.select{|v| v.option_values.include?(size)}
From my understanding, the select method more or less iterates through the array, which is kinda slow. I would rather have a query that finds the variant directly based on those two attributes.
I tried the following:
Spree::Variant.joins(:option_values).where(:spree_option_values => {:id => size.id}, :product_id => prod.id).joins(:option_values).where(:spree_option_values => {:id => color.id})
but this only ended up in returning an empty array.
How would I go about this?
Edit: Here are the product, variant and option_values models:
Product:
https://github.com/spree/spree/blob/master/core/app/models/spree/product.rb
Variant:
https://github.com/spree/spree/blob/master/core/app/models/spree/variant.rb
OptionValue: https://github.com/spree/spree/blob/master/core/app/models/spree/option_value.rb
OptionType: https://github.com/spree/spree/blob/master/core/app/models/spree/option_type.rb
Updated 2: you're right, this is not what you looking for.
So you can:
1) Build SQL subquery: (if joined table has size and has color at the same time then return TRUE). How quick it will be working - is a question...
2) Imagine you've created a model "ValuesVariants" for table "spree_option_values_variants" and kicked out habtm (replace with 2 has_manys + 2 has_manys through). Now you can search ValuesVariants with (option_type_id = size_id||color_id AND variant_id IN (array of product's variant ids)), extracting matched variants. It can be quick enough...
3) You can use :includes. so associated objects loaded into the memory and the second search do by array methods. In this case the concern is in memory usage.

rails 3 group by and sum

I have the following model:
activity_types: id, name
activities: id, id_activity_type, occurrences, date (other fields)
The activities table store how many times an activity occurs by day. But now I want to show to the user how many activities from each type occurred by month.
I got the following solution based on this post which seems ok:
Activity.all(:joins => :activity_types,
:select => "activity_types.id, activity_types.name, SUM(activities.occurrences) as occurrences",
:group => "activity_types.id, activity_types.name",
:order => "activity_types.id")
but this seems a lot of code for the rails standards and rails API says it's deprecated.
I found the following solution which is a lot simple:
Activity.sum(:occurrences).group(:activity_type_id)
Which returns an hash with activity_type_id => occurrences.
What shall I do to get the following hash: activity_type.name => occurrences ?
If the original query worked, then just try rewriting it with Rails 3 syntax:
Activity.joins(:activity_types)
.select("activity_types.id, activity_types.name, SUM(activities.occurrences) as occurrences")
.group("activity_types.id, activity_types.name")
.order("activity_types.id")
Activity.joins(:activity_types).group('activity_types.name').sum(:occurrences)
SELECT SUM(activities.occurrences) AS sum_occurrences, activity_types.name AS activity_types_name FROM activity_types INNER JOIN activity_types ON activity_types.id = activities.activity_types_id GROUP BY activity_types.name
in case you needed an ordered hash based on activity_types.id and assuming activity_types_id is not needed as a part of hash key.
Activity.joins(:activity_types).group('activity_types.name').order(:activity_types_id).sum(:occurrences)
incase [activity_type_id, activity_types.name] needed as a part of key
Activity.joins(:activity_types).group(:activity_types_id, 'activity_types.name').order(:activity_types_id).sum(:occurrences)

How do I find() all records unique in certain fields?

I have a list of 'request' objects, each of which has fairly normal activerecord qualities. The requests table is related to the games table with a join table, 'games_requests,' so that a request has a request.games array.
The question is, is there a way to do a find for the last n unique requests, where uniqueness is defined by the games column and a couple others, but specifically ignores other colums (like the name of the requesting user?)
I saw a syntax like 'find (:all, :limit=>5, :include=>[:games,:stage])' but that was returning duplicates.
Thanks...
EDIT: Thanks to chaos for a great response. You got me really close, but I still need the returns to be valid request objects: the first 5 records that are distinct in the requested rows. I could just use the find as you constructed it and then do a second find for the first row in the table that matches each of the sets returned by the first find.
EDIT:
Games.find(
:all, :limit => 5,
:include => [:games, :requests],
:group => 'games, whatever, whatever_else'
)
...gives an SQL error:
Mysql::Error: Unknown column 'games' in 'group statement': SELECT * FROM `games` GROUP BY games
I made a few changes for what I assumed to be correct for my project; getting a list of requests instead of games, etc:
Request.find(
:all, :order=>"id DESC", :limit=>5,
:include=>[:games], #including requests here generates an sql error
:group=>'games, etc' #mysql error: games isn't an attribute of requests
:conditions=>'etc'
)
I'm thinking I'm going to have to use the :join=> option here.
Games.find(
:all, :limit => 5,
:include => [:games, :requests],
:group => 'games, whatever, whatever_else'
)
Try Rails uniq_by.It also works with association and returns array.
#document = Model.uniq_by(&:field)
More Detail
I think you'll be able to do this using find_by_sql and GROUP BY:
Games.find_by_sql("SELECT * FROM games GROUP BY user_id")

Resources