How to order by largest amount of identical entries with Rails? - sql

I have a survey where users can post answers and since the answers are being saved in the db as a foreign key for each question, I'd like to know which answer got the highest rating.
So if the DB looks somewhat like this:
answer_id
1
1
2
how can I find that the answer with an id of 1 was selected more times than the one with an id of 2 ?
EDIT
So far I've done this:
#question = AnswerContainer.where(user_id: params[:user_id]) which lists the things a given user has voted for, but, obviously, that's not what I need.

you could try:
YourModel.group(:answer_id).count
for your example return something like: {1 => 2, 2 => 1}

You can do group by and then sort
Select answer_id, count(*) as maxsel
From poll
Group by answer_id
Order by maxsel desc

As stated in rails documentation (http://api.rubyonrails.org/classes/ActiveRecord/Calculations.html) when you use group with count, active record "returns a Hash whose keys represent the aggregated column, and the values are the respective amounts"
Person.group(:city).count
# => { 'Rome' => 5, 'Paris' => 3 }

Related

Rails Select one random listings for premium users

In my rails app, I have Users and Listings. The Listings belong to a User. Listing has user_id and its filled with users id who is creating the listing.
A user can be a premium user, gold user or silver user.
What I want is for each premium user, select one random listing to show in premium listings.
I can do it in O(n**2) time or n+1 query as follow:
users_id = User.where(:role => "premium").pluck[:id]
final_array = Array.new
users_id.each do |id|
final_array << Listing.where(:user_id => id).sample(1)
end
final_array
Is there a better way of doing this?
You could try this:
listings = Listing.select(
<<~SQL
DISTINCT ON (users.id) users.id,
listings.*,
row_number() OVER (PARTITION BY users.id ORDER BY random())
SQL
)
.joins(:user)
.includes(:user)
.where(users: { role: :premium })
It gives a random Listing for every premium user.
It produces the only request to db and also it won't make an extra request for getting listing's user, so you are free to do something like this:
listings.each do |listing|
p listing.user
end
random_user_listings = []
User.includes(:listings).where(role: "premium").find_each do |user|
random_user_listings << user.listings.sample(1)
end
random_user_listings
To avoid N+1 query you need to combine them, perform query one time like this:
list = Listing.includes(:user).where(:role => "premium").sample(1)
Feel free to deal with list instead of Listing. Because now you're dealing with variable, not Query.
ids = list.pluck(:user_id).uniq
Getting array of ids like above and doing further steps as you did (but with list, not Listing)
Need to be noticed that, when you deal with Model you're dealing with QUERY. Avoiding doing that in loop statement.

Group by query, each group has to not have any item not in a List

In need a query that will help me solve this.
Here's my table 'tags':
id (int)
name (String)
user_id (int)
hardware_id (int)
I am grouping the results of the 'tags' table by hardware_id. I also have a List of tags (List<string>).
I want to get the hardware Id of the groups that all of the tags in the custom List matches at a name in the table above.
In other words, I want to get the hardware_id's that the custom List tags matches their name's. There might be name's that doesn't have a match in the custom list, but all of the custom list tags, must be in the group, and if it satisfies this need, I can the Id of that group.
I found it hard to explain and I didn't get an answer for that. I thought about doing it with foreach because it was so hard to solve, but I couldn't do it either and it's very inefficient to do it that way.
Example:
List : ['tag1', 'tag2']
Table Rows:
1, tag1, 5, 1
2, tag2, 5, 1
3, tag3, 5, 1
4, tag4, 5, 2
5, tag5, 6, 2
In this case, I should get the hardware_id of 1, because although one of the hardware Id's have tag3, it doesn't have any rows with a tag name that isn't in the List. IF the List had 'tag4', the hardware_id = 1 WOULDN'T be returned, because the List has a tag that the hardware_id group doesn't have.
If the Group doesn't have an item that the List has, it won't appear in the final result.
Someone game me this code, but it didn't work:
List<decimal> matchingHardareIds = db.tags.GroupBy(x => x.hardware_id)
.Where(x => x.All(s => tags.Contains(s.name.ToLower()) || 0 == tags.Count() && (s.user_id == userId)))
.Select(x => x.Key).ToList();
In that query, when I have one tag in the List and in the table I have several items with hardware_id 1 and one of them has a 'name' that is equal to the value in the List it will return empty results. this is because the rows in the table for a specific group by hardware_id, has a row with a name that doesn't appear in the custom List.
I need a query in either Entity Framework or Linq. Thanks a lot.
Use this:
var t = db.tags.GroupBy(x => x.hardware_Id)
.Where(x => tags.All(y =>
x.Any(z=> z.name == y)))
.Select(x=>x.Key).ToList();
Can not provide you with the entity framework or linq query, but the sql solution is to count the matches like this:
SELECT hardware_id
FROM tags
WHERE name IN (<list>)
GROUP BY hardware_id
HAVING COUNT(DISTINCT name) = <listDistinctCount>
<listDistinctCount> is the count of distinct values in the list. Which you can prepare prior to the query.

Active Record query to match every subset element

In my RoR application, I've got a database lookup similar to this one:
Client.joins(:products).where({'product.id' => [1,2,3]})
Unfortunately this will return all clients that have bought product 1, 2 or 3 but I only want to get back the clients, that bought all of the three products. In other words, I'd like to write a query that matches for n elements in a given set.
Are there any elegant solutions for this?
This is not really elegant. But it should translate into the needed SQL.
Client.joins(:products).
where({'products.id' => [1,2,3]}).
group('users.id').
having('COUNT(DISTINCT products.id) >= 3')
Same answer with more dynamic way
ids = [1,2,3]
Client.joins(:products).
where({'products.id' => ids}).
group('users.id').
having('COUNT(DISTINCT products.id) >= ?', ids.size)

Group feedback by category and limit between date range

Feedback belongs to Category and has scope :between, ->(start_date, end_date) { where(created_at: start_date.beginning_of_day..end_date.end_of_day) }
Question is, how do we get the count of feedbacks grouped by category and scoped to a certain date range to get something like:
1.5.2013 - 31.5.2013
Good 3
Bad 10
etc.
I got so far: Category.group(:name).joins(:feedbacks).count, but I'm stuck on how to plug the between date condition there.
Feedback.between(sd,ed).joins(:category).group("categories.name").count
# => {"Good" => 3, "Bad" => 1}

Rails ID and order

Rails 3 app. How would I find only records with an id of "1" and put them in DESC order? I feel like this is a simple question but I can't figure it out?
Although I do not understand why would you need to order only one record, the solution (provided that I understood) that solves your question is:
Item.where("id = 1").order("id DESC").first
# => Item Load (0.7ms) SELECT `items`.* FROM `items` WHERE id = 1 ORDER BY id DESC LIMIT 1