Rails SQL query for mutual friends - sql

My User model looks like this:
has_many :relationships, foreign_key: "follower_id", dependent: :destroy
has_many :followed_users, through: :relationships, source: :followed
has_many :reverse_relationships, foreign_key: "followed_id",
class_name: "Relationship",
dependent: :destroy
has_many :followers, through: :reverse_relationships, source: :follower
What I want is the raw SQL query to get users that a given user follows and is followed back by.
Currently I've only gotten as far as getting the ids of users I'm following:
followed_user_ids = "SELECT followed_id FROM relationships
WHERE follower_id = #{user.id}"
I have a rails methods that returns mutual friends, but I want the SQL equivalent:
def mutual_friends
# interesect both arrays to find similar elements
self.followed_users & self.followers
end

Something like this should work
SELECT followed_id
FROM relationships
WHERE follower_id = #{user.id} AND followed_id IN (
SELECT follower_id FROM relationships WHERE followed_id = #{user.id}
)

Please use it like this, let me know if it works for you.
has_many :company_friendships, autosave: true
has_many :company_friends, through: :company_friendships, autosave: true
has_many :inverse_company_friendships, class_name: "CompanyFriendship", foreign_key: "company_friend_id", autosave: true
has_many :inverse_company_friends, through: :inverse_company_friendships, source: :company, autosave: true
def mutual_company_friends
Company.where(id: (company_friends | inverse_company_friends).map(&:id))
end

Related

Rails ActiveRecord query on existing collection

Suppose I have a result from a query:
allCourses = Course.all
Then I also have another set:
myCourses = current_user.courses.all
How can I get a set of items that are in allCourses and NOT in myCourses?
Here are the models:
class Student < ActiveRecord::Base
has_many :student_enrollments, dependent: :destroy
has_many :courses, through: :student_enrollments
end
class Course < ActiveRecord::Base
has_many :student_enrollments, dependent: :destroy
has_many :students, through: :student_enrollments
end
class StudentEnrollment < ActiveRecord::Base
belongs_to :student
belongs_to :course
end
I can always write raw SQL script to achieve the result, but I prefer to find a Rails way to do it.
Thanks
Assume your fk is user_id.
ohterCourses = Course.where('user_id != ?', current_user.id)

Order users based on when the relationship was created_at

I used the Railstutorials to create followers and followed_users http://ruby.railstutorial.org/chapters/following-users#top
On the page where I want to show a specific persons' followers/followed_users, I'd like to show them based on when the relationship was created.
#users = #user.followers.order("created_at DESC")
Something like this ^^ just shows when the user was created, not when the relationship was created. How can I run this query efficiently to get the proper ordering?
def following
#users = #user.followed_users
end
def followers
#users = #user.followers
end
-User Model-
has_many :relationships, foreign_key: "follower_id", :dependent => :destroy
has_many :followed_users, through: :relationships, source: :followed
has_many :reverse_relationships, foreign_key: "followed_id",
class_name: "Relationship",
dependent: :destroy
has_many :followers, through: :reverse_relationships, source: :follower
- Relationship Model -
belongs_to :follower, class_name: "User", touch: true
belongs_to :followed, class_name: "User", touch: true
validates :follower_id, presence: true
validates :followed_id, presence: true
Since your user has there two relationships, you can easily access that table with the direction you want.
has_many :relationships, foreign_key: "follower_id", dependent: :destroy
has_many :reverse_relationships, foreign_key: "followed_id"
First Answer (when they're a follower)
You have to use the relationships table because that record gets created when you get a new follower, thus you do this:
#user.relationships.order("created_at DESC").collect { |r| User.find(r.followed) }
Second Answer (when they're followed)
#user.reverse_relationships.order("created_at DESC").collect { |r| User.find(r.follower) }

Excluding results in has_many :through relation in Rails 3

I am trying to select groups which the #current_user is NOT a member of
The relevant parts of my models are as follows:
class Group < ActiveRecord::Base
belongs_to :user
has_many :group_memberships
has_many :members, :class_name => "User", :through=>:group_memberships
...
class User < ActiveRecord::Base
has_many :group_memberships, :foreign_key => 'member_id'
has_many :groups, :through => :group_memberships
...
class GroupMembership < ActiveRecord::Base
belongs_to :member, :class_name=>"User"
belongs_to :group
end
Thanks!
You could try and do something like:
#groups = Group.where("id NOT IN (?)", current_user.groups)
More information can be found in the Active Record Query.
have a method like this:
def group_ids_not_a_member_of
# get all group_ids and subtract out the ids that he is a member of
Group.pluck('id') - current_user.groups.map(&:id)
end
then
user.group_ids_not_a_member_of

Rails has_one with join table

In my app there are Athletes... athletes can have many sports.
Athlete:
has_many :sports, :through => :user_sports
has_one :primary_sport, conditions: ["user_sports.primary = ?", true], class_name: "Sport"
has_many :user_sports
UserSport:
class UserSport < ActiveRecord::Base
attr_accessible :athlete_id, :sport_id, :primary
belongs_to :athlete
belongs_to :sport
end
I am trying to be able to pull back the primary_sport as a Sport object instead of the user_sport object.
Since you pull your sports objects :through user_sports, you should pull your primary_sport object :through user_sports as well.
has_one :primary_sport, :through => :user_sports, conditions: ["user_sports.primary = ?", true], class_name: "Sport"

Find rows with same some_attribute but two different values for user_id

These are my classes & relations
class User
has_many :conversation_participants
has_many :conversations, :through => :conversation_participants
end
class ConversationParticipant
belongs_to :user
belongs_to :conversation
end
class Conversation
has_many :messages
has_many :conversation_participants
has_many :users, :through => :conversation_participants
end
So when I want to create a conversation between user_ids 12 and 15, I first want to check if a conversation between these two already exists. So what I need to find is this:
ConversationParticipants where user_id IN (12, 15) AND "both conversationparticipant rows have the same conversation id"
I lack the proper words to explain this query but I think everyone will get what I mean. "Does a conversation between these two users exist already?". I know neither how to do it in SQL nor Rails so any answer is appreciated.
EDIT
The id of the conversation would not be known. I need to find if a conversation between those two user_ids exist.
Thanks
-- Emil
class User
has_many :conversations
has_many :conversation_participants
has_many :joined_conversations, :through => :conversation_participants, :source => :conversation
end
class ConversationParticipant
belongs_to :user
belongs_to :conversation
end
class Conversation
belongs_to :user
has_many :conversation_participants
has_many :speakers, :through => :conversation_participants, :source => :user
end
You can try this
#result = ConversationParticipants.where("user_id in ? AND conversation_id=?",[1,2,3,4],2)