ActiveRecord - Retrieve one record for each association - ruby-on-rails-3

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.

Related

SQL complicated query with joins

I have problem with one query.
Let me explain what I want:
For the sake of bravity let's say that I have three tables:
-Offers
-Ratings
-Users
Now what I want to do is to create SQL query:
I want Offers to be listed with all its fields and additional temporary column that IS NOT storred anywhere called AverageUserScore.
This AverageUserScore is product of grabbing all offers, belonging to particular user and then grabbing all ratings belonging to these offers and then evaluating those ratings average - this average score is AverageUserScore.
To explain it even further, I need this query for Ruby on Rails application. In the browser inside application you can see all offers of other users , with AverageUserScore at the very end, as the last column.
Associations:
Offer has many ratings
Offer belongs to user
Rating belongs to offer
User has many offers
Assumptions made:
You actually have a numeric column (of any type that SQL's AVG is fine with) in your Rating model. I'm using a column ratings.rating in my examples.
AverageUserScore is unconventional, so average_user_score is better.
You don't mind not getting users that have no offers: average rating is not clearly defined for them anyway.
You don't deviate from Rails' conventions far enough to have a primary key other than id.
Displaying offers for each user is a straightforward task: in a loop of #users.each do |user|, you can do user.offers.each do |offer| and be set. The only problem here is that it will execute a separate query for every user. Not good.
The "fetching offers" part is a standard N+1 counter seen even in the guides.
#users = User.includes(:offers).all
The interesting part here is only getting the averages.
For that I'm going to use Arel. It's already part of Rails, ActiveRecord is built on top of it, so you don't need to install anything extra.
You should be able to do a join like this:
User.joins(offers: :ratings)
And this won't get you anything interesting (apart from filtering users that have no offers). Inside though, you'll get a huge set of every rating joined with its corresponding offer and that offer's user. Since we're taking averages per-user we need to group by users.id, effectively making one entry per one users.id value. That is, one per user. A list of users, yes!
Let's stop for a second and make some assignments to make Arel-related code prettier. In fact, we only need two:
users = User.arel_table
ratings = Rating.arel_table
Okay. So. We need to get a list of users (all fields), and for each user fetch an average value seen on his offers' ratings' rating field. So let's compose these SQL expressions:
# users.*
user_fields = users[Arel.star] # Arel.star is a portable SQL "wildcard"
# AVG(ratings.rating) AS average_user_score
average_user_score = ratings[:rating].average.as('average_user_score')
All set. Ready for the final query:
User.includes(:offers) # N+1 counteraction
.joins(offers: :ratings) # dat join
.select(user_fields, average_user_score) # fields we need
.group(users[:id]) # grouping to only get one row per user

Display Country with the most occurrences Rails 3

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.

Single Table Inheritance (STI)... Multi Table Inheritance (MTI)... Polymorphic Associations

So I have found MANY posts on these subjects but none have answered my (probably very novice) question. I am brand new to Rails this month but I have been programming in OOL for a little while. I think my question might span multiple subjects about Ruby on Rails. I don't mind reading multiple blogs or tutorials to peace this all together.
I have 4 location models. Each have a "Name" attribute.
Country (has_many :states)
State (belongs_to :country has_many :counties has_many :cities, :through => :counties)
County (belongs_to :state has_many :cities)
City
My end goal is to have an autocomplete textbox for city, county, state. The user will select a country to narrow down the results. For example, the user will type in "SA" and the autocomplete textbox will show:
Kansas
Arkansas
Santa Clara County, California
San Jose, Santa Clara County, California
etc.
So I can easily return the text that I want displayed. Each model has something like:
def location_name
// the display name of this model. For example we want the County Model to return "Santa Clara County, California"
"#{name}, #{state.name}"
end
Solutions I tried to research:
Active Record Query Interface to make one select statement across the multiple tables. My goal would be to search across multiple models and get a single list of objects with the name containing the search text.
STI - Maybe create a Location model which all can inherit from but I couldn't find any basic tutorial online that include models with different attributes for each. Do I "generate" one model with all the fields that all 5 would need then just delete them from the auto generated .rb file? I assume I will then be able to use Location.where("name like ? and country_id = ?", params[:search], params[:country_id]) and get a full list of all location which fit the search parameters.
MTI - I looked at this since I'm not a big fan of excess columns in the DB that are mostly blank. For example :country_id would only be used by states. So it would be null for all counties and cities, etc. But all the tutorial seem to imply that STI would be the better way to go.
Polymorphic Associations - I just started looking into this a few hours ago. I found something about ":polymorphic => true" and I read another article that talked about a subclass feature.
I have spent quite a few days looking into the "right" way of doing this according to Ruby. I'd really like to learn and incorporate all that ruby has to offer. Thanks for any thought!
maybe you can watch this video for your further development too.
http://railscasts.com/episodes/88-dynamic-select-menus
one-to-many for your case should be alright i guess..
like this country has many states and the according state has many of cities.
correct me if im wrong ##

Ruby on Rails Query Count

I'm developing a site where users can post opinions, as well as rate opinion up or down.
In turn, I need to find a way to count opinion that have been rated up or down.
I'm currently working on those which have been rated up. I have a many-to-many relationship between the following entities: Opinion and Rating.
The two entities are joined together by a table called OpinionRatings. Below is the query I have come up with so far.
#topUpSize = OpinionRating.find(
:first,
:select => "count(opinion_ratings.opinion_id) as count",
:joins => "inner join ratings on opinion_ratings.rating_id=ratings.id",
:group => "opinion_ratings.opinion_id",
:having => ["ratings.up=?", true]
)
The problem I have is I currently have two separate test opinions that have been rated up so the count that should be displayed is two, however, the count that is displayed is one. I'm not sure why this is happening.
Any help would be appreciated.

ActiveRecord counting with conditions over two tables

After watching, the latest RailsCasts with the include and joins information, I know I can do what I am doing in a much more efficient way. I have a very simple User model and a Status model. The User belongs to Status and the Status has many Users. I need to count how many users have a specific kind of status, this creates a new SQL count query for every single status and I know that this is not a good way to do it. It looks like this right now.
statuses = Status.all
statuses.each do |status|
status.users.count
end
I end up with 4 queries of:
SELECT count(*) AS count_all FROM "users" WHERE ("users".status_id = 1)
SELECT count(*) AS count_all FROM "users" WHERE ("users".status_id = 2)
It goes on like that for as many different statuses as exist in the database. The big problem is that now I need to filter by another association as well which is Organization. So I need to find the count for all users who have a certain status in a certain organization. This ends up quadrupling the amount of queries I am making and feels horrible. I'm not sure what kind of join I could use to cut down on this or what I could possibly do to fix this. Thanks for the help :)
Ok so I am answering my own question, just in case anyone has the same issue.
Status.all(:joins => :users,
:select => "statuses.*, count(users.id) as users_count",
:group => "statuses.id")
This returns every status that has users and the count of the users as users_count. In order to further refine the query and only count users that belong to a certain organization the query changes to this.
Status.all(:joins => :users,
:select => "statuses.*, count(users.id) as users_count",
:conditions => {:users => {:organization_id => ORG_ID_HERE}},
:group => "statuses.id")
I hope this helps anyone with the same issue and thanks to Eimantas, and Ryan Bates(RailCasts).
You can try plain SQL:
SELECT s.name,COUNT(u.id) AS users_count FROM statuses s, users u WHERE s.id=u.status_id GROUP BY s.id;
Out of interest, what does your Status model contain? Does it really need to be its own model? I'm guessing here, but you may want to consider implementing a finite state machine. There are a number of rails plugins that make it easy to implement an FSM e.g. acts_as_state_machine