Как найти запись через ассоциацию? - PullRequest
0 голосов
/ 13 июля 2020

Всего четыре таблицы - chat_rooms, chat_messages, chat_rooms_and_users и users:

  • chat_rooms - в комнатах есть сообщения и пользователи.
  • chat_rooms_and_users - Через эту таблицу пользователи подключаются к комнатам.

В каждой комнате может быть два пользователя.

Как найти комнату, зная двух пользователей?

Пробовал вот так:

room = joins(:chat_rooms_and_users)
       .find_by(
         type: ChatRoom.types[:private],
         chat_rooms_and_users: {
           user: [user_a, user_b]
         }
       )
SELECT "chat_rooms".* FROM "chat_rooms" INNER JOIN "chat_rooms_and_users" ON "chat_rooms_and_users"."room_id" = "chat_rooms"."id" WHERE "chat_rooms"."type" = $1 AND "chat_rooms_and_users"."user_id" IN ($2, $3) LIMIT $4  [["type", 0], ["user_id", 497], ["user_id", 494], ["LIMIT", 1]]

Меня это смущает в коде SQL:

"chat_rooms_and_users"."room_id" = "chat_rooms"."id"

Если нет комнат, то первая комната создается нормально. Но тогда всегда есть только комната, ID которой стоит первым, чем остальные.

1 Ответ

1 голос
/ 13 июля 2020

Ваша проблема: "chat_rooms_and_users"."user_id" IN ($2, $3) вернет все «частные» комнаты, в которых присутствует любой из пользователей.

Вместо этого вы хотите найти чат, где присутствуют оба пользователя. Я бы порекомендовал сделать область для этого

#assumed 
class User < ApplicationRecord
  has_many :chat_rooms_and_users
end

class ChatRoom < ApplicationRecord 
 
  scope :private_by_users, ->(user_a,user_b) { 
      where(type: ChatRoom.types[:private])
      .where(id: user_a.chat_rooms_and_users.select(:chat_room_id))
      .where(id: user_b.chat_rooms_and_users.select(:chat_room_id))
   }
end 

#Then 
ChatRoom.private_by_users(user_a,user_b)

. Это вернет коллекцию «частных» комнат, в которых user_a и user_b являются участниками. SQL будет выглядеть примерно так:

SELECT "chat_rooms".* 
FROM "chat_rooms" 
WHERE "chat_rooms"."type" = 0 AND 
 "chat_rooms"."id" IN ( 
    SELECT 
      "chat_rooms_and_users"."chat_room_id" 
    FROM  
      "chat_rooms_and_users"
    WHERE 
      "chat_rooms_and_users"."user_id" = user_a_id
  ) AND "chat_rooms"."id" IN ( 
    SELECT 
      "chat_rooms_and_users"."chat_room_id" 
    FROM  
      "chat_rooms_and_users"
    WHERE 
      "chat_rooms_and_users"."user_id" = user_b_id
  )

Если вы можете гарантировать, что будет только 1 или 0 комнат этого типа с обоими участниками, тогда вы можете добавить first в конец эта цепочка.

...