Ok so in my rails project I have these models: pools, memberships, and week_scores
Pool has_many :memberships
Membership has_many :week_scores
WeekScore also has an attribute week:integer that tells what week it is. That way I know if that table is of week 1 or week 6, etc.
WeekScore has an attribute score:integer where I save the points of each user. This makes me think I should change my model name to week_points...
Anyways, I want to show a table of all me the members of a pool sorted by the score they got in a certain week_score.
Just to clarify
The way I get to a ceratin week score right now is
member_score = member.week_scores.find_by(week: 5).score
//member is a member of a pool
For example,
Lets say its week 5,
I want to display the week 5 score of each member of the pool sorted by the score in descending order like so,
David.... 30pts
John..... 28pts
Josh..... 28pts
Mike..... 21pts
...so on
what would be the query i need to achieve this? Ive tried joins but it wont work but im new at this so im pretty sure this is easy to do Im just too newb to know.
Also.. I want to read more about databases and sql for rails so I get more familiar with this. Anyone care to recommend a book?
week5 = Pool.first.memberships.includes(:week_scores)
.where(:week_scores => {week: 5}).order('week_scores.score DESC')
week5.each do |membership|
puts "#{membership.member_name}: #{membership.week_scores.first.score}"
end
Related
I have 2 models. Gif, GifStatistic
gif has_many :gif_statistics
GifStatistics has column called state. It can be either like or dislike
What i want to achieve is to query gifs, but order them in highest count of likes
Something like(Pseudo code)
Gif.joins(:gif_statistics).order(count(gif.gif_statistics.state))
How do i achieve this?
Try this query.
Gif.joins(:gif_statistics).select("*, count(gif_statistics.state) as state_count").order("state_count desc")
My personal recommendation is that you create two new fields in the gif model so that you can store the count of like and dislike as and when its created so that you don't have to do such a complex query. Placing the counter cache would improve the speed.
I have a country model and would like to display the country with the most occurrences, country names are held in the column 'mame', however the country db is pre populated and the relationship is a country
has_many recipes
and recipe
belongs_to country
So far I have
Country.group('name').order('count_name DESC').limit(1).count('name')
but this will not work will it as there are 1 of every country in the table? Do i need to do a count on the number of times the country_id is used? if so what would the syntax be for that? would it be
Recipe.group('country_id').order('count_country_id DESC').limit(1).count('country_id')
or using joins and select
Country.joins(:recipes).select('countries.*, count(country_id) as "country_count"').group(:country_id).order(' country_count desc')
Any pointers appreciated
You can do it by using queries. However, RoR has built in support to achieve the same. It is called Counter Cache.
I can explain here but I think it's better if you follow this screencast.
http://railscasts.com/episodes/23-counter-cache-column
This will give you very good idea how to use counter cache and get what you've tried to achieve.
I have a user model. I want to place a limitation of the rate of which the user can change his/her name.
Obivously a person doesn't change a name every day or every week.
How would be best to implement such a limit for example able to change once a month?
The only way I can think of is placing another attributes for each existing attribute so for the :name I will created :name_last_changed_at and each time I would test it.
Is there a more elegant/rails-way to do it?
You can relate on updated_at column and validate it in model.
Merchant has_many Shops
Shop belongs_to Merchant
i.e. One merchant (Starbucks) can have many shops locations.
I'm using Gecoder to get the nearby shops, e.g. #shops = Shop.near("Times Square").
I would like to return only 1 record for each merchant only. I.e. #shops only contain 1 Starbucks, 1 Subway, but is a collection.
Sorry I've been Googling and searching on SO to no avail. Perhaps I'm not using the right word to describe what I want. Thanks in advance.
To answer what you should be googling for, joined or combined queries within a scope will probably solve what you are looking to do. If you build a scope with :or logic combining queries, one each for each shop, limited to the first record, you should get what you are looking for.
I won't pretend that I understand Geocoder or advanced scopes enough to do this, but I found an example that shows this approach in another problem:
named_scope :or, lambda { |l, r| {
:conditions =>
"annotations.id IN (#{l.send(:construct_finder_sql,{:select => :id})}) or " +
"annotations.id IN (#{r.send(:construct_finder_sql,{:select => :id})})"
}}
This comes from this SO question: Combine two named scopes with OR (instead of AND)
Hope this helps you find the solution.
I googled a bit more and stumbled on group by for SQL.
If I have 4 shops belonging to 2 merchants near a location called "Raffles Place", within 1 kilometer.
Calling Shop.near("Raffles Place",1) returns 4 shops.
If I add a group to Shop.near("Raffles Place",1).group(:merchant_id), only 2 shops are returned.
This can be used with other conditions too, such as Shop.where(:featured=>true).group(:merchant_id) to only show 1 shop per featured merchant.
In my application Users register for Events, which belong to a Stream. The registrations are managed in the Registration model, which have a boolean field called 'attended'.
I'm trying to generate a leaderboard and need to know: the total number of registrations for each user, as well as a count for user registrations in each individual event stream.
I'm trying this (in User.rb):
# returns an array of users and their attendence count
def self.attendance_counts
User.all(
:select => "users.*, sum(attended) as attendance_count",
:joins => 'left join `registrations` ON registrations.user_id = users.id',
:group => 'registrations.user_id',
:order => 'attendance_count DESC'
)
end
The generated SQL works for just returning the total attended count for each user when I run it in the database, but all that gets returned is the User record in Rails.
I'm about to give up and hardcode a counter_cache for each stream (they are fairly fixed) into the User table, which gets manually updated whenever the attended attribute changes on a Registration model save.
Still, I'm really curious as to how to perform a query like this. It must come up all the time when calculating statistics and reports on records with relationships.
Your time and consideration is much appreciated. Thanks in advance.
Firstly as a couple of points on style and rails functions to help you with building DB queries.
1) You're better writing this as a scope rather than a method i.e.
scope attendance_counts, select("users.*, sum(attended) as attendance_count").joins(:registrations).group('registrations.user_id').order('attendance_count DESC')
2) It's better not to call all/find/first on the query you've built up until you actually need it (i.e. in the controller or view). That way if you decide to implement action / fragment caching later on the DB query won't get called if the cached action / fragment is served to the user.
3) Rails has a series of functions to help with aggregating db data. for example if you only wanted a user's id and the sum of attended you could use something like the following code:
Registrations.group(:user_id).sum(:attended)
Other functions include count, avg, minimum, maximum
Finally in answer to your question, rails will create an attribute for you to access the value of any custom fields you have in the select part of your query. e.g.
#users = User.attendance_counts
#users[0].attendance_count # The attendance count for the first user returned by the query