Count distinct records while plucking from another table - ruby-on-rails

Given Rails app where:
class Team < ActiveRecord::Base
belongs_to :captain, class_name: User
I'm trying to succinctly query database and get a summary of Captains and the number of Teams they belong to:
[[23, "Pat", "Smith"]] Teams: 1
[[46, "Kate", "Jones"]] Teams: 1
[[107, "John", "Doe"]] Teams: 3
...
I can get a hash with each Captain's ID and the number of teams:
> Team.group(:captain_id).count
=> {85=>3, 106=>1, 81=>1, 25=>1, 32=>1, 8=>3, 1=>1, 79=>2, 26=>1}
But then it gets ugly. The following works but seems ugly and has n+1 SQL queries:
Team.group(:captain_id).count.sort.to_h.each { |key,val| puts "#{User.where(id: key).pluck(:id, :first, :last)} Teams: #{val}" }
What's the Rails way?

Since you are trying to get detailed info about users, you may want to define a relation like this on your User model:
class User < ActiveRecord::Base
has_many :captained_teams, foreign_key: :captain_id, class_name: Team
Then you can work with User for your query:
User.joins(:captained_teams).includes(:captained_teams).each do |user|
puts "#{[user.id, user.first, user.last]} Teams: #{user.captained_teams.length}"
end

sql = "**Select count then group by then having count(...)**"
result = ActiveRecord::Base.connection.execute(sql);
You can use the raw sql to get what you want.

Related

Count total association number on search result

I have following many-to-many association...
class User
has_and_belongs_to_many :starred_jobs, class_name: "Job",
join_table: "starred_jobs"
end
class Job
has_and_belongs_to_many :starred_by, join_table: "starred_jobs",
class_name: "User"
end
In a query, I get number of users between two dates like
users = User.where(created_at: start_date..end_date)
In a summary page, I want to view total number of jobs of selected users. Now, how I get the total number of jobs from searched users? For example:
#total_users_jobs = users.jobs.count
Thanks in advance.
Simplest and maybe resource heavy method would be
users.map { |u| u.jobs.count }.sum
another method
JobUser.where("user_id in (?)", users.map(&:id)).count
Another
Job.joins(:users).where(users: {id: users.map(&:id)}.uniq.count
This should give you the total count of all Jobs for all matching Users
#total_users_jobs = Job.joins(:users).where(users: {id: users.map(&:id)}.uniq.count

rails: query and filter n:m related objects using active record

I'm trying to use active-record query possible connections between airports.
I described the models I created already in another question here:
n:m self-join with ruby on rails active record
Basically, what I can do now is that:
ny = Airport.create({"city" => "New York"})
la = Airport.create({"city" => "Los Angeles"})
ny.destinations << la
la.destinations << ny
I ran into an issue querying the data I'm looking for, which is quite simple in SQL but I had no luck with active record yet.
ny = Airport.where('city = ?', 'New York')
ny.destinations
returns the correct objects, but all of them.
The SQL query looks like that:
SELECT "airports".* FROM "airports" INNER JOIN "connections" ON "airports"."id" = "connections"."destination_id" WHERE "connections"."airport_id" = 3
I'd like to filter those results by cities starting with "s" for example, so an SQL query could look like that:
SELECT "airports".* FROM "airports" INNER JOIN "connections" ON "airports"."id" = "connections"."destination_id" WHERE "connections"."airport_id" = 3 AND airports"."city" LIKE "s%"
I tried it this way:
ny.destinations.where('city LIKE ?', '#{params[:query]}%')
But I always get an empty result.
How could I use active record to filter my resulting objetcs?
edit: Thats the best solution I found so far:
I added the cityLike() method to the Airport model:
app/models/airport.rb:
class Airport < ActiveRecord::Base
attr_accessible :city, :name
has_many :connections
has_many :destinations, :through => :connections
has_many :inverse_connections, :class_name => "Connection", :foreign_key => "destination_id"
has_many :inverse_destinations, :through => :inverse_connections, :source => :airport
def self.cityLike(query)
where("city LIKE ?", "%#{query}%")
end
end
app/model/connection.rb:
class Connection < ActiveRecord::Base
attr_accessible :destination_id, :airport_id
belongs_to :airport
belongs_to :destination, :class_name => "Airport"
end
Now I can query the objects with the following statement:
Airport.find(1).destinations.cityLike("a")
Not sure if it's the best solution, but it produces the query I was looking for.
Thanks a lot to all af you!
ActiveRecord::Base.execute(sql) lets you use pure SQL to do your query and returns the relevant model.
What about this?
Airport.find(:all, joins: "INNER JOIN `connections` ON airports.id = connections.destination_id").where("connections.airport_id = ? AND airports.city LIKE ?", ny_id, "s%")
This code:
ny.destinations.where('city LIKE ?', '#{params[:query]}%')
works like this--first you have an object ny repesenting the city of New York. When you say ".destinations" you have now followed a relation you defined in your model to retrieve all the destinations that you can get to from New York. However, if I'm imagining your database schema correctly, these destinations don't actually have a field called "city"; instead, they have a destination_id, which ties the destination to a particular airport, and it's the airport that has a city associated with it.
So when you query the destination table for 'city LIKE ?', it doesn't find any matching records.
Instead, try
ny.destinations.joins(:airports).where('city LIKE ?', '#{params[:query]}%')

Rails - Join query difficulties

I have two basic models: Stadium and Owner
The relations between those two models are:
A Stadium:
belongs_to :owner
An Owner:
has_many :stadiums
The thing here is, an Owner has also Categories associated, and here is where owner_category model comes in.
An Owner:
has_and_belongs_to_many :owner_categories,
:join_table => 'l_owners_owner_categories',
And OwnerCategory:
has_and_belongs_to_many :owners, :join_table => 'l_owners_owner_categories'
Basically,
The OwnerCategory table looks like this:
id,name
1,sport
2,kids
So, my question is:
Given that I let the user to choose a City and a Category, how would I get all the Stadiums from that city which Owner has the given Category?
So for example:
If I have the following Stadiums:
id,name,city,owner_id
1,'soccer stadium','New York',5
2,'music stadium','New York',4
2,'music stadium','San Francisco',4
The following Owners:
id,name
4, 'John'
5, 'Peter'
The following OwnersCategories table:
id,name,description
1,'sports','this category is associated with stadiums that support sports'
2,'music','this category is associated with stadiums that support music'
And the following join table:
owner_id,owner_category_id
5, 1
When the users chooses 'New York' and 'Sports' it should give this stadium:
1,'soccer stadium','New York',5
Try something like this (warning: not tested):
Stadium.joins(:owner).joins(:owner => :owner_categories)
.where(:city => "New York").where("owners_categories.name = ?", "sports")
class Stadium < ActiveRecord::Base
scope :for_city, lambda { |city_name| where(:city => city_name) }
scope :for_owner_category,
lambda { |owner_category_id|
joins("INNER JOIN owners ON owners.id = stadium.owner_id" +
" INNER JOIN l_owners_owner_categories ON l_owners_owner_categories.owner_id = owners.id").
where("l_owners_owner_categories.owner_category_id = :category_id",
:category_id => owner_category_id)
}
end
Stadium.for_city("New York").for_owner_category(1)

Trying to find a count of items from a two level deep association, any ideas?

I am working on an application where I need to find the count of submitted items by users that have been referred by a user.
For Example -
User1 has referred 3 people (User2, User3, User4) and each of those users has submitted 5 articles.
I am trying to find a way to get the count of submitted items in User1's tree (should be 15 in this case).
My user model looks like the following (simplified)
class User < ActiveRecord::Base
# Code for user referrals
belongs_to :referrer, :class_name => "User"
has_many :referrals, :class_name => "User", :foreign_key => "referrer_id"
has_many :items
end
I can find out the count for each user easily (User.items.size), but I am having trouble finding a solution to get the referral counts as one sum.
Any ideas?
Try this:
user = User.find(1)
total_items_size = user.referrals.map(&:items).flatten.size
You can use select_value to manually run the SQL query:
def referred_items_count
select_value("select count(*) as referred_items
from items inner join users on users.id = items.user_id
where users.referrer_id = #{self.id};", "referred_items")
end
The benefit is that it is a lot more scalable than using Ruby to count.
Get all items of User with id of 1
total = 0
Users.find(1).referrals.each do |refer|
total += refer.items.size
end

Rails named_scope with has_and_belongs_to_many

i have 3 tables - films, films_genres (for connect 2 tables) and genres.
In Model film.rb - has_and_belongs_to_many :genres
In Model genre.rb - has_and_belongs_to_many :films
So, how I can write this sql code:
SELECT * FROM genres INNER JOIN films_genres ON genres.id = films_genres.genre_id WHERE (films_genres.film_id = 1 )
with named_scope in Model film.rb for show all film rolled genres?
class Model < ActiveRecord::Base
named_scope :by_genre, lambda { |*genres|
{
:include => :genres,
:conditions => [ "genres.id IN (?)", genres.map(&:id) ]
}
}
end
Film.by_genre(western, sci_fi).find(:all)
I made this one slightly more complex in order to specify multiple genres as part of your named scope. Hope it helps.
In English, what are you trying to pull from the DB? To retrieve the genres for a particular film just do:
#genres = #film.genres
chap! I try to pool from the db list of genres connected to the film. And my question is: how I can to do this using named_scome in film.rb Model? I need this for films filter.
Link for example: http://clearcove.ca/blog/2008/12/recipe-restful-search-for-rails/#more-218
Databases:
films:
id
name
descr
year
films_genres:
id
film_id
genre_id
genres:
id
name

Resources