find_by_sql which model should I use? - ruby-on-rails

There are three tables:
users
schedules
schedules_users
The user-model and the schedules-model each have the has_and_belongs_to_many-relationship.
Now I simply want to do this:
user_id_binded = Schedule/User/Object/#I dont know!#.find_by_sql ["SELECT schedules_users.user_id FROM schedules_users WHERE schedules_users.schedule_id = ?", schedule.id]
#user_schedules_binded = User.find(user_id_binded)
BUT the return-value of the first find_by_sql must be a model, as I understood the Rails.Api correctly.
It's neither a user-model-return-value or a schedule-model-return-value.
In the schedules_users-table are all relationships between users and schedules.
So I want to get all users which are binded to a specific schedule.
First I thought this should be the right way to solve it, but at that moment I didn't know that the return-value must be a model.
How could I solve this problem?

It appears you have a schedule ID and want the users in the end - that can be done easier by join statement like #user_schedules_binded = User.joins(:schedules).where(schedules: { id: schedule_id })
Or, if you have the schedule object, schedule.users will do the same, both going through schedules_users table.

Related

Chaining multiple .map calls together, more efficient way to query data?

I find myself sometimes needing to "find" an item that's buried deep within an association structure.
In this case, it starts with a task_group. And from that task_group, I need to get the parent_image's, which resides at the end of several associations.
Here is how I'm currently handling it:
task_group.tasks
.map(&:submissions).flatten
.map(&:taggables).flatten
.map(&:images).flatten
.map(&:parent_image)
Is there a more efficient way of going about this type of query?
Perhaps the query could somehow limit the content the amount of data returned each time? For example, rather than have &:submissions, &:taggables, &:images .map calls return the entire records each time, could I have them pull out only, say, the :id attribute, and let the final &:parent_image map call return the full record?
Thank you in advance for any guidance or insights!
UPDATE: Learning about how to use the .joins method as recommended by #Deepak . I'm almost there!
parent_images = Image.joins(crops: [tags: [:submission [task: :task_group]]])
This returns an Image::ActiveRecord_Relation that I can call .ids or .uniq on to get an array.
It seems .joins directly references field names. Which is well and good, except in this instance, taggables that I used in the original .map calls is a quick and dirty "catch-all" for several different associations.
has_many :tags
has_many :people
has_many :businesses
...
def taggables
tags + people + businesses ...
end
I wonder, then, is it possible to reference a method as part of .joins query (possibly with .where(), though I'm not sure how that would it in...)? Or will I need to explicitly state each of the taggables associations directly somehow?
Thanks to #Deepak's suggestion, I am able to combine .joins with .where in the following manner, in order to get all of the parent_images related to a given task_group:
tg = TaskGroup.find(2)
parent_images = Image.joins(crops: [tags: [submission: [task: :task_group]]]).where(task_groups: { id: tg })
Or say I'm to pull all of the images related to a given TaskGroup, one association 'removed' from the parent_images:
tg = TaskGroup.find(2)
images = Image.joins(tags: [submission: [task: :task_group]]).where(task_groups: { id: tg })
Now, my understanding of .joins is that the arguments need to relate to fields found in their respective tables. This means that, when using .map, I could call the taggables method, but when using .joins, this does not seem to be possible.
So the workaround I'm using at the moment is to call each of the taggables models separately, then combine the results.
tag_crops = Image.joins(people: [submission: [task: :task_group]]).where(task_groups: { id: tg })
business_crops = Image.joins(businesses: [submission: [task: :task_group]]).where(task_groups: { id: tg })
...
And then combine each of those together into one giant list
crop_image_ids = tag_crops.ids.uniq +
person_crops.ids.uniq +
business_crops.ids.uniq +
...
crop_image_ids = crop_image_ids.uniq

Rails - finding an object in an active record association by an attribute without issuing more queries

Very short question, I feel like the answer must be already on StackOverflow but I couldn't find it.
I have some incoming parameters. They each have a unique ID.
I query the database and get an active record association using something like:
existing_things = current_user.things.where(uuid: [param_uuids])
This gives me an Active Record Association of those objects.
However, what I later do is something like:
existing_things.where(uuid: some_specific_uuid)
Of course, when I run the above query, it issues an SQL statement.
What I would love to do is find an object in a pre-loaded Active Record array of objects, and return that object without issuing another query.
How should I do that?
The same way you would if it was an ordinary array: Enumerable#find.
existing_things.find { |t| t.uuid == some_specific_uuid }
(or select if you're expecting more than one match)
If you're doing a lot of lookups like that, you might want to get a Hash involved:
things_by_uuid = existing_things.index_by(&:uuid)
things_by_uuid[some_specific_uuid]
(again, if you're expecting more than one match per value, there's group_by)

Extra attribute in many to many join table

I have a many to many association between User and Todo through a join model called UserTodo
Of the many users a todo has, there's one owner. So, I created a column in the user_todos join table called is_owner.
Question is, how do I populate this attribute while creating a Todo?
Currently, I'm creating the todo, then separately updating this attribute in TodoController#create action.
#todo = current_user.todos.create(todo_params)
#todo.user_todos.first.update_attribute(:is_owner, true)
This seems wrong. Is there a single call I can make to populate this attribute while creating the todo?
Second, is there a way to query if a user is an owner of a todo, this way?
current_user.todos.first.is_owner?
I would make a user_todo.rb file with a UserTodo class and do stuff like:
ut=UserTodo.new
ut.todo = Todo.create(todo_params)
ut.user = current_user
ut.is_owner = true
ut.save
current_user.todos_as_usertodos.first.is_owner?
You can make on user.rb
def todos_as_usertodos
UserTodo.where(user_id: id).to_a
end
See where I'm going with this? You want to return and use UserTodo objects vs. Todo objects because they have more info in them. The info you need. That extra is_owner boolean. When you goto just a plain todo object you lose that info.
That seems a bad way since you use .first to get your instance. I'll do something like
UserTodo.create(user: current_user, is_owner: true, todo: Todo.create(todo_params))
Second
I'm not sure if that is possible
You can use this one-liner to create the join model with extra attributes:
current_user.user_todos.create(todo: Todo.create(todo_params), is_owner: true)
Since is_owner is an attribute of the join model, you have to access it through that model too:
current_user.user_todos.first.is_owner?

n+1 for counting items in a has_many association

(Using rails 4)
I have an Event model that has_many subscriptions. In the list of events, I display the number of subscriptions. To do this in my view for every event I have:
event.subscriptions.count
However, this causes a performance hit because for every event a subsequent query is performed to obtain the subscriptions count.
To fix this I thought to get all the subscriptions, something along these lines:
subscription_count = Event.all.map { |e| {e.id => e.subscriptions.count} }
Then instead of performing a query for every event, I would simply look up the event id in the subscription_count hash. However, this obviously still performs all the queries separately. Is there a way to perform some aggregate query to do the job in one go?
Thanks!
You can set a counter_cache column in your event table for the subscription. another way you can fetch events like Event.includes(:subscription) and in the view you can use e.subscription.size instead of count also you can check this link also
if you only need the count of the associated model
http://railscasts.com/episodes/23-counter-cache-column
You can do sth like:
#events = Event.joins('LEFT JOIN subscriptions ON subscription.event_id = event.id').group('events.id').select('events.*, COUNT(subscriptions.id) AS number_of_subscriptions')
And then inside the view use:
event.number_of_subscriptions

Avoiding subqueries in HQL using Grails

I have two object, a room type and a reservation. Simplified they are:
class Room {
String description
int quantity
}
class Reservation {
String who
Room room
}
I want to query for all rooms along with the number of rooms available for each type. In SQL this does what I want:
select id, quantity, occupied, quantity-coalesce(occupied, 0) as available
from room left join(select room_id, count(room_id) as occupied from reservation)
on id = room_id;
I'm not getting anywhere trying to work out how to do this with HQL.
I'd appreciate any pointers since it seems like I'm missing something fairly fundamental in either HQL or GORM.
The problem here is your trying to represent fields that are not your domain classes like available and occupied. Trying to get HQL\GORM to do this can be a bit a little frustrating, but not impossible. I think you have a couple options here...
1.) Build your domain classes so that there easier to use. Maybe your Room needs to know about it's Reservations via a mapping table or, perhaps write what you want the code to look like and then adjust the design.
For example. Maybe you want your code to look like this...
RoomReservation.queryAllByRoomAndDateBetween(room, arrivalDate, departureDate);
Then you would implement it like this...
class RoomReservation{
...
def queryAllByRoomAndDateBetween(def room, Date arrivalDate, Date departureDate){
return RoomReservation.withCriteria {
eq('room', room)
and {
between('departureDate', arrivalDate, departureDate)
}
}
}
2.) My second thought is... It's okay to use the database for what it's good for. Sometimes using sql in you code is simply the most effective way to do something. Just do it in moderation and keep it centralized and unit tested. I don't suggest you use this approach because you query isn't that complex, but it is an option. I use stored procedures for things like 'dashboard view's' that query millions of objects for summary data.
class Room{
...
def queryReservations(){
def sql = new Sql(dataSoruce);
return sql.call("{call GetReservations(?)}", [this.id]) //<-- stored procedure.
}
}
I'm not sure how you can describe a left join with a subquery in HQL. INn any case you can easily execute raw SQL in grails too, if HQL is not expressive enough:
in your service, inject the dataSource and create a groovy.sql.Sql instance
def dataSource
[...]
def sql= new Sql(dataSource)
sql.eachRow("...."){row->
[...]
}
I know it's very annoying when people try to patronize you into their way of thinking when you ask a question, instead of answering your question or just shut up, but in my opinion, this query is sufficiently complex that I would create a concept for this number in my data structure, perhaps an Availability table associated to the Room, which would keep count not only of the quantity but also of the occupied value.
This is instead of computing it every time you need it.
Just my $.02 just ignore it if it annoys you.

Resources