Sort by join table field - ruby-on-rails

I have a users table and a reporting_events table where reporting_events belongs to user.
I want to sort the users table based on the reporting events values. Users should be sortable based on values of various reporting event names.
For example, there can be reporting events of two users with reporting event name as "avg_response_time" and reporting event values as 1 and 2 for the two users. We should be able to sort with avg_response_time of user both in desc and asc orders.
What I have tried:
reports = users.joins(:reporting_events).where("reporting_events.name='avg_response_time'").order("reporting_events.value desc")
filter_params[:sort_order] gives the column and direction it should sort by. When I don't use distict I'm getting duplicate users in users list.
I want to list all users even if reporting_events does't exist for the user.

You need a user an outer join instead on an inner join (default) :
users.joins('LEFT OUTER JOIN reporting_events ON reporting_events.user_id = users.id').where(...

Related

Should I join a join table to another table?

Let's say I am building a potluck organizing app. I have a join table tracking Participants and Events (MTM), I also have a Dishes table with dishes that are unique and associated to each Participant (OTM). Participants will sign up to events and agree to bring one or more of their Dishes.
The ParticipantEvents join table will track the RSVP but what is the best way to track which dishes the Participant is contributing to that Event?
Should I create a join table between the Dishes and the Events?
Should I create a join table between the ParticipantEvents join table and the Dishes table (ParticipantEventsDishes)?
Is there a better way?
The case looks like a three way (ternary) relationship to me. So I would create a three way join table, with three foreign keys.
Promised (DishID, EventID, ParticipantID)
Now you can join this with all three tables to produce the details for all participants. Note that a participant can promise more than one dish for an event.

How to ensure that join table (without model) data are unique

I'm trying to have read and unread status on a particular post by the specific user using Rails 5. I'm having post_users join table in which post_id and user_id will be stored. Posts and users associated with many to many (has and belongs to many). I don't have a model for the post_users table. How to check the join table in the controller?

Order by count of a model's association with a particular attribute

Say I have the two models Users and Appointments where a user has_many appointments.
An appointment can be of two different types: typeA and typeB.
How can I write a query to order the users by the amount of typeB appointments they have?
I've looked into counter_cache but it seems to just count the number of the association (so in this case the number of appointments a user would have) and does not allow for the counting of a particular type of appointment.
With joins (INNER JOIN) you'll get only those users, who have at least one appointment associated:
User.joins(:appointments)
.where(appointments: { type: 'typeB' })
.group('users.id')
.order('count(appointments.id) DESC')
If you use includes (LEFT OUTER JOIN) instead, you'll get a list of all users having those without appointments of 'typeB' at the end of the list.
Depending on the size and complexity of the database, it may be sometimes better to do two queries instead of joins on tables. In case you want to skip joins one way could be to get the order of Ids from one select query, and then retrieving the records from the second query.
ordered_user_ids = Appointments.select('user_id, count(1) AS N')
.where(:type => 'typeB').group(:user_id).order('N desc').collect(&:user_id)
# find users keeping the order of the users intact
User.find(ordered_user_ids, :order => "field(id, #{ordered_user_ids.join(',')})")

Can I Order Results Using Two Columns from Different Tables?

I have a Rails application featuring a city in the US. I'm working on a database process that will feature businesses that pay to be on the website. The goal is to feature businesses within an hour's drive of the city's location in order to make visitors aware of what is available. My goal is to group the businesses by city where the businesses in the city are listed first then the businesses from the next closest city are displayed. I want the cities to be listed by distance and the businesses within the city group to be listed by the business name.
I have two tables that I want to join in order to accomplish this.
city (has_many :businesses) - name, distance
business (belongs_to :city) - name, city_id, other columns
I know I can do something like the statement below that should only show data where business rows exist for a city row.
#businesses = City.order(“distance ASC").joins('JOIN businesses ON businesses.city_id = cities.id')
I would like to add order by businesses.name. I've seen an example ORDER BY a.Date, p.title which referencing columns from two databases.
ORDER BY a.Date, p.title
Can I add code to my existing statement to order businesses by name or will I have to embed SQL code to do this? I have seen examples with other databases doing this but either the answer is not Rails specific or not using PostgreSQL.
After lots more research I was finally able to get this working the way I wanted to.
Using .joins(:businesses) did not yield anything because it only included the columns for City aka BusinessCity and no columns for Business. I found that you have to use .pluck or .select to get access to the columns from the table you are joining. This is something I did not want to do because I foresee more columns being added in the future.
I ended up making Business the main table instead of BusinessCity as my starting point since I was listing data from Business on my view as stated in my initial question. When I did this I could not use the .joins(:business_cities) clause because it said the relation did not exist. I decided to go back to what I had originally started with using Business as the main table.
I came up with the following statement that provides all the columns from both tables ordered by distance on the BusinessCity table and name on the Business table. I was successful in added .where clauses as needed to accommodate the search functionality on my view.
#businesses = Business.joins("JOIN business_cities ON business_cities.id = businesses.business_city_id").order("business_cities.distance, businesses.name")

Rails - Only pull in some HABTM associations on a case-by-case basis to avoid unnecessary joins

In Rails 4, I have a project in which I've set up three models with the following many-to-many relationships:
An Item
has_and_belongs_to_many categories
has_and_belongs_to_many tags
A Category
has_and_belongs_to_many items
A Tag
has_and_belongs_to_many items
And while it's easy to select an Item and automatically get all associated categories and tags, there are some situations in which I'd want to select items AND their associated categories, but NOT their tags. In these cases, I'd like to avoid doing extra database joins against the Tags table and ItemsTags join table. Can anyone help me with the correct find syntax to only join Items to categories? (Side note: I'm also planning on adding 10 additional many-to-many relationships between items and other models, but I'm just simplifying the scenario for this question. In the end, I'm trying to avoid doing a join with an excessive number of tables whenever I can.)
Thanks!
Rails will by default not load associated records unless you request it
Item.all will only fetch record from 'items' table
Then later in your code if you call item.categories that's the point when a query is performed to fetch all categories of this particular item. If you never call item.tags then the query to 'tags' table is never executed and the records are not fetch. Bottom line is: you can have as many associations as needed, as long as you don't explicitly call them they won't be loaded.
Side note about performance, rails offer several ways to join and include associated tables:
Item.include(:category).all Will trigger only 2 queries to fetch all items, and all associated categories.
Item.include(:category).joins(:category).all -> will trigger only 1 query joining the items and categories tables (but it may be slower than 2 requests)
So you have all control over what's loaded from the database. Those can apply for scope as well.

Resources