Difference Between find and Where with Relationships - ruby-on-rails

I wouldn't think there is a difference when it comes to active record and finding data.
Here are my models
class User < ActiveRecord::Base
has_many :shows
end
class Show < ActiveRecord::Base
belongs_to :user
end
When I use the rails console I can do the following and it works.
u = User.find(1)
u.shows
It gives me all the shows for that user.
However when I do
u = User.where("username = ?", "percent20")
u.shows # this is doesn't work gives me a now instance error
I get the same user and relevant information, but not the relationship. The only problem I can see is maybe I am doing something wrong because there is some difference between where and find.
Any help is appreciated.

The problem is not the relationship.
u = User.find(1)
returns one User
#return a Set of users. In your case its only one user.
u = User.where("username = ?", "percent20")
The result type is ActiveRecord::Relation --> [User, User, User]
use e.g. first to get the first User
#returns the first user
u = User.where("username = ?", "percent20").first
u.class.name
=> "User"

User.find(1) is retrieving a specific record with its ID, whereas User.where("username = ?", "percent20") is retrieving the set of records that match the condition.
Try:
u = User.where("username = ?", "percent20").first
u.shows

The where is method that returns an array of objects. So, in your case try
u.each { |user| user.shows }

Related

How to Sort Record in Ruby on Rails based on Last Record Timestamp from References Table

I need to create a live chat app and now I have three models :
ChatRoom
ChatRoomMember
ChatRoomMessage
From this three models, I use includes and references to get the list of chat_rooms for current login user. Here is the code that I have wrote.
#chat_rooms = ChatRoom.includes(:members).references(:members)
#chat_rooms = #chat_rooms.includes(:messages).references(:messages)
#chat_rooms = #chat_rooms.where 'chat_room_members.user_id = ?', #current_user.id
#chat_rooms = #chat_rooms.order 'chat_room_messages.created_at DESC'
#chat_rooms = #chat_rooms.limit(limit).offset(offset)
However, the order didn't work as I expected. What I want is that the chat_rooms are sorted by created_at column from the last message in that room. How can I do this ?
Here is the database structure :
Use association to avoid where 'chat_room_members.user_id = ?', #current_user.id
Here is my suggestion, assuming User has associations looking like:
class User
has_many :chat_room_members
has_many :chat_rooms, through: :chat_room_members
end
# list only rooms with at least on message
#chat_rooms = #current_user.chat_rooms.joins(:messages).order('chat_room_messages.created_at DESC').limit(limit).offset(offset)
# list all rooms even if there is no message attached
#chat_rooms = #current_user.chat_rooms.distinct.left_joins(:messages).order('chat_room_messages.created_at DESC').limit(limit).offset(offset)
Try this:
ChatRoom.includes(:messages).order('chat_room_messages.created_at DESC')
Thanks for everyone has help me to solve this probel. I have my own answer. Here is it.
SELECT chat_rooms.*, (SELECT chat_room_messages.created_at FROM chat_room_messages WHERE chat_room_messages.chat_room_id = chat_rooms.id ORDER BY chat_room_messages.id DESC LIMIT 1) AS last_message_at FROM chat_rooms INNER JOIN chat_room_members ON chat_room_members.chat_room_id = chat_rooms.id WHERE chat_room_members.user_id = #{#current_user.id} ORDER BY last_message_at DESC
I use raw query for solve this problem. Hope this can help anyone who need it !

Rails query with condition in count

I'm having a little trouble with a query in Rails.
Actually my problem is:
I want to select all users which do not have any user_plans AND his role.name is equals to default... OR has user_plans and all user_plans.expire_date are lower than today
user has_many roles
user has_many user_plans
users = User.where(gym_id: current_user.id).order(:id)
#users = []
for u in users
if u.roles.pluck(:name).include?('default')
add = true
for up in u.user_plans
if up.end_date > DateTime.now.to_date
add = false
end
end
if add
#users << u
end
end
end
This code up here, is doing exactly what I need, but with multiple queries.
I don't know how to build this in just one query.
I was wondering if it is possible to do something like
where COUNT(user_plans.expire_date < TODAY) == 0
User.joins(:user_plans, :roles).where("roles.name = 'default' OR user_plans.expire_date < ?", Date.today)
Should work, not tested, but should give you some idea you can play with (calling .distinct at the end may be necessary)
There is also where OR in Rails 5:
User.joins(:user_plans, :roles).where(roles: { name: 'default' }).or(
User.joins(:user_plans).where('user_plans.expire_date < ?', Date.today)
)
FYI: Calling .joins on User will only fetch those users who have at least one user_plan (in other words: will not fetch those who have no plans)

Query optimization in associated models

I have a User model
class User < ActiveRecord::Base
has_many :skills
has_one :profile
end
Profile table has two columns named, age & experience
Now, I've a search form where the parameters are passed are:
params[:skill_ids] = [273,122,233]
params[:age] = "23"
params[:experience] = "2"
I've to search through all the users where user's skills meet any of the params[:skill_ids] and also from the user's profile, their age and experience.
Do I have to go through a loop like:
users = []
User.all.each do |user|
if (user.skills.collect{|s| s.id} & params[:skill_ids] ) > 0
// skip other parts
users << user
end
end
or, any of you have any better solution?
Because your skills belong to exactly one user, you could first fetch all users belonging to the skill ids provided and filter them by the other criteria:
matching_users = User.includes(:skills, :profile)
.where(["skills.id in (?) AND profile.age = ? AND profile.experience = ?",
params[:skill_ids],
params[:age].to_i,
params[:experience].to_i]
)
Try this:
#users = User.includes(:skills).where(["skills.id in (?)", params[:skill_ids]]).all

Self-referential find in controller count relations

I'm having real trouble pulling out a set of records that are self-referentially related to a user in order to show these on a user's 'show' page.
Here's the idea:
Users (current_user) rate the compatibility between two other users (user_a and user_b). They can rate compatibility either positively or negatively: rating two users "compatible" creates a positive_connection between user_a and user_b, and rating them "incompatible" creates a negative_connection. So there are models for positive_connection, negative_connection and user.
Now I need to display only users that are overall_positively_connected_to(#user) (i.e. where positive_connections_to(#user).count > negative_connections_to(#user).count).
This is where I've got to, but I can't get any further:
User model:
def overall_positive_connected_to(user)
positive_connections_to(user).count > negative_connections_to(user).count
end
def positive_connections_to(user)
positive_connections.where("user_b_id = ?", user)
end
def negative_connections_to(user)
negative_connections.where("user_b_id = ?", user)
end
Controller
#user.user_bs.each do |user_b|
if user_b.overall_pos_connected_to(#user)
#compatibles = user_b
end
end
The code in the controller is clearly wrong, but how should I go about doing this? I'm completely new to rails (and sql), so may have done something naive.
Any help would be great.
So am I right in saying you have 3 models
User (id, name)
PositiveConnection (user_a_id, user_b_id)
NegativeConnection (user_a_id, user_b_id)
Or something of that sort.
I think you just want 2 models
and for convenience I'm going to rename the relations as "from_user" and "to_user"
User (id, name)
Connection (value:integer, from_user_id, to_user_id)
Where value is -1 for a negative
and +1 for a positive.
Now we can have do something like
(note: you need to sort out the exact syntax, like :foreign_key, and :source, and stuff)
class User
has_many :connections, :foreign_key => "from_user_id"
has_many :connected_users, :through => :connections, :source => :to_user
def positive_connections
connections.where(:value => 1)
end
def negative_connections
...
end
end
But we also now have a framework to create a complex sql query
(again you need to fill in the blanks... but something like)
class User
def positive_connected_users
connected_users.joins(:connections).group("from_user_id").having("SUM(connections.value) > 0")
end
end
this isn't quite going to work
but is kind of pseudo code for a real solution
(it might be better to think in pure sql terms)
SELECT users.* FROM users
INNER JOIN connections ON to_user_id = users.id
WHERE from_user_id = #{user.id}
HAVING SUM(connections.value) > 0

Help in ActiveRecord find with include on conditions

I have a Coach Model which:
has_many :qualifications
I want to find all coaches whose some attribute_id is nil and they have some qualifications. Something which is like.
def requirement
legal_coaches = []
coaches = find_all_by_attribute_id(nil)
coaches.each do |coach|
legal_coaches << coach if coach.qualifications.any?
end
legal_coaches
end
Is there a way to get all such records in one line ?
find_all_by_attribute_id(nil).select(&:qualification)
I think you can't do that via purely ruby syntax. I can only think of the following (ugly) way
Coach.find(:all, :conditions => "attribute_id IS NULL AND EXISTS(SELECT * FROM qualifications WHERE coach_id = coaches.id)")

Resources