I have the following structure:
User model: id, ..
Event model: id, ..
UserMapEvent model: user_id, event_id
For example the UserMapEvent table might have the following records:
User_id | Event_id
1 | 1
1 | 2
3 | 1
4 | 1
I'm using this structure to save when a user likes an event.
Now, if I'm in the shows controller and I know the event's id, I can get the number of times the user liked the event like:
`likes = UsersMapEvent.where("event_id = ?", event.id).count`
How do I get, for example, the top 3 liked events? Do I have to join the 2 tables?
The only solution that I could think of is to use Event.all and for each to do the exact same thing, but it sounds stupid and I assume this is not one of those cases 'if it's stupid and it works is fine'.
What type of database operation do I have to use to get my query?
You may select top 3 liked events with next code:
Event.left_joins(:users_map_events).group(:id).order('COUNT(users_map_events.id) DESC').limit(3)
Related
I'm using the ajax datatable gem to display the client name and total points in a store. the store-clients table looks like this.
store_id
client_id
total_points
But as it will be a report, I don't want to show the same client twice, even to different stores. I am trying the following:
StoreClient.joins(:client).group(:client_id)
But I get this error: column "store_clients.id" must appear in the GROUP BY clause or be used in an aggregate function
If I add the store_clients id the result is not as expected.
The expected would be:
client_id | store_id | total_points
1 1 10
1 2 20
2 1 5
-----------------------------------
client_id | store_id | total_points
1 1 10
2 1 5
Assume initial result is an array store in instance variable #result you can get a unique result like this:
#result.uniq {|o| o.client_id }
In a Rails app, I have a model named TopicEdition with 2 attributes:
edition and an associated Topic.
The topic_editions table may look like
id | edition | topic_id
1 | 1 | 1
2 | 2 | 1
3 | 1 | 2
Now I need to get the list of TopicEdition having the highest edition for each topic.
Referring the table above, I should get back record with id 2 and 3.
I have tried to play around with group and maximum but without success.
My last attempt has been
TopicEdition.group(:topic_id).maximum(:edition)
How can I get what I need?
The followings work but it is kind of ugly
TopicEdition.find_by_sql 'SELECT * FROM topic_editions WHERE (topic_id, edition)
IN (SELECT topic_id, MAX(edition) FROM topic_editions GROUP BY topic_id)'
I think the easiest would be to use normal SQL to sort your query like this:
TopicEdition.group(:topic_id).order('MAX(edition)')
That will return only the last edition of each topic.
I have a model called Event which belongs to Area and Task. I'm attempting to retrieve a collection of events that only contains the most recent event per area and task combination. That is, I only want the most recent event of the events that have the same area_id and task_id. Example collection of events:
|event_id|area_id|task_id| ... |
|--------|-------|-------|-----|
| 5 | 3 | 2 | ... |
| 4 | 3 | 1 | ... |
| 3 | 3 | 2 | ... |
Here I want only event 5 and 4 to be returned since 3 is older.
I've tried using Event.select(:area_id,:task_id).distinct which seems to work, but strips all other attributes of the returned events, including :id. Grateful for any help or suggestions!
You can use raw SQL inside select, so you could try something like this:
Event.select("DISTINCT(CONCAT(area_id, task_id)), id, attr1, attr2")
Where id, attr1 and attr2 are the other attributes from your Event table.
Or you could use .group instead of .distinct and forget about using raw SQL:
Event.all.group(:area_id,:task_id)
You will get the same result as using DISTINCT and all attributes will be available.
UPDATE
To order before grouping, you can use find_by_sql with nested queries (again, raw SQL):
Event.find_by_sql(
"SELECT * FROM (
SELECT * FROM `events`
ORDER BY `events`.`created_at`) AS t1
GROUP BY t1.`area_id`, t1.`task_id`";
)
In another words you need to group events by area_id and task_id, and select recent record in each group. There question about building sql-query for this case. PostgreSQL version for your table will be like this:
SELECT DISTINCT ON (area_id, task_id) events.*
FROM events
ORDER BY area_id ASC, task_id ASC, created_at DESC
And Rails code for this query:
Event.
select("DISTINCT ON (area_id, task_id) events.*").
order("area_id ASC, task_id ASC, created_at DESC")
I have a Rails 4 app using ActiveRecord and Postgresql with two tables: stores and open_hours. a store has many open_hours:
stores:
Column |
--------------------+
id |
name |
open_hours:
Column |
-----------------+
id |
open_time |
close_time |
store_id |
The open_time and close_time columns represent the number of seconds since midnight of Sunday (i.e. beginning of the week).
I would like to get list of store objects ordered by whether the store is open or not, so stores that are open will be ranked ahead of the stores that are closed. This is my query in Rails:
Store.joins(:open_hours).order("#{current_time} > open_time AND #{current_time} < close_time desc")
Notes that current_time is in number of seconds since midnight on the previous Sunday.
This gives me a list of stores with the currently open stores ranked ahead of the closed ones. However, I'm getting a lot of duplicates in the result.
I tried using the distinct, uniq and group methods, but none of them work:
Store.joins(:open_hours).group("stores.id").group("open_hours.open_time").group("open_hours.close_time").order("#{current_time} > open_time AND #{current_time} < close_time desc")
I've read a lot of the questions/answers already on Stackoverflow but most of them don't address the order method. This question seems to be the most relevant one but the MAX aggregate function does not work on booleans.
Would appreciate any help! Thanks.
Here is what I did to solve the issue:
In Rails:
is_open = "bool_or(#{current_time} > open_time AND #{current_time} < close_time)"
Store.select("stores.*, CASE WHEN #{is_open} THEN 1 WHEN #{is_open} IS NULL THEN 2 ELSE 3 END AS open").group("stores.id").joins("LEFT JOIN open_hours ON open_hours.store_id = stores.id").uniq.order("open asc")
Explanation:
The is_open variable is just there to shorten the select statement.
The bool_or aggregate function is needed here to group the open_hours records. Otherwise there likely will be two results for each store (one open and one closed), which is why using the uniq method alone doesn't eliminate the duplicate issues
LEFT JOIN is used instead of INNER JOIN so we can include the stores that don't have any open_hours objects
The store can be open (i.e. true), closed (i.e. false) or not determined (i.e. nil), so the CASE WHEN statement is needed here: if a store is open, then it's 1, 2 if not determined and 3 if closed
Ordering the results ASC will show open stores first, then the not determined ones, then the closed stores.
This solution works but doesn't feel very elegant. Please post your answer if you have a better solution. Thanks a lot!
Have you tried uniq method, just append it at the end
Store.joins(:open_hours).order("#{current_time} > open_time AND #{current_time} < close_time desc").uniq
Given a database with records like the following
Table: AuditLog
Fields: ID | USERID | TYPE | TIME | DATA
id:1, userId:1, type:PHOTO, time:2008-10-15 12:00:00, data:{photoId:2089, photoName:A trip to the beach}
Lets say the database had 50 records, with Rails How can I loop through the results as follows:
id, user_id, data.photoID, data.PhotoName
The main thing I don't get is how to extract what's inside the data column, the has that's inserted int the db field.
Thanks
If you have a list of results like this you would do something like the following:
#results.each do |result|
p result.data.photoId
end
You can certainly get fancier but hopefully this will get you along for now.