Andre Zimpel
Andre Zimpel

Reputation: 2343

Rails, find by association

I'm about to build a messaging feature for a rails app.

I thought about having conversations, conversation_participants and conversation_messages.

Which means:

Conversation:

has_many :conversation_participants
has_many :conversation_messages, through: :conversation_participants

Conversation Participant:

belongs_to :conversation
belongs_to :user
has_many :conversation_messages

Conversation Message:

belongs_to :conversation_participant

So far, so good.

I'm just stuck in some scenarios:

Hope somebody could help me out on this one! Thank you!

Upvotes: 1

Views: 1358

Answers (3)

maro
maro

Reputation: 1506

You may want to look more into the merge and where methods.

The first example would look something like:

Conversation.joins(:conversation_participants).merge(User.where(:id => user1_id)).merge(User.where(:id => user2_id))

Each merge() filters the results. You wouldn't want to use merge(User.where(:id => [user1_id, user2_id])) because you would get all the conversations for both users, not just the common ones.

The second example would be similar to the first one.

In the third example you could add something like .merge(User.where.not(:id => user6_id) at the end of the query to not include conversations with User 6.

UPDATE

To chain multiple merge dynamically you could try something like:

conversations = Conversation.joins(:conversation_participants)
user_ids.each{|uid| conversations.merge!(User.where(:id => uid))}

Upvotes: 1

Matt
Matt

Reputation: 646

It sounds like you will want to set up an association between users and conversations through conversation participants.

How do I find the conversation for User 1 and User 2?

This doesn't seem to be a unique conversation given the way you've set things up. Users 1 and 2 could be in one conversation with only each other, and another conversation that includes other participants. That being said, if you join the conversation_participants table to itself on conversation_id you should be able to find the matches. There are other ways to go about this to and I'm happy to provide more info. The way you decide to approach this will factor into the answers to your other questions as well.

UPDATE:

The query would look something like this:

SELECT cp1.conversation_id 
FROM conversation_participants as cp1 
JOIN conversation_participants as cp2 ON cp1.conversation_id = cp2.conversation_id 
WHERE cp1.participant_id = 1 and cp2.participant_id =2;

Upvotes: 0

Alexei.B
Alexei.B

Reputation: 322

You can exclude converstion_messages. These are irrelevant, and need to include users.

#user 
has_many :conversations, through: converstion_participants

user_12_conv_ids = user_1.conversation_ids & user_2.conversation_ids
user_123_conv_ids = user_12_conv_ids & user_3.conversation_ids

user_123_conversations = Conversation.where(id: user_123_conv_ids) 

Now you can select conversations that include only 1, 2, and 3 (as user_ids)

conversations.select{|c| c.user_ids == user_ids}

Upvotes: 1

Related Questions