Rails - Получить все записи для многодетальных отношений, включая неассоциированные записи - PullRequest
0 голосов
/ 21 февраля 2019

У меня есть следующие классы, у которых есть сквозные отношения.

class Movie < ApplicationRecord
  has_many :favourites, dependent: :destroy
  has_many :users, through: :favourites
end

class Favourite < ApplicationRecord
  belongs_to :user
  belongs_to :movies
end


class User < ApplicationRecord
  has_many :favourites, dependent: :destroy
  has_many :movies, through: :favourites
end

У меня есть страница поиска, которая позволяет пользователям искать фильм по имени.Но мне также нужно показать, нравится ли фильм текущему пользователю.Итак, в основном у меня есть запрос, который выглядит примерно так (сокращен, чтобы сохранить его релевантность):

@movies = Movie.includes(favourites: :user)
               .where(favourites: { user: [current_user, nil] })

Итак, в приведенном выше примере я получаю все фильмы и также пытаюсь получить все текущиеизбранное пользователя, но также возвращает результаты, если фильм еще не был добавлен в избранное.

Проблема, однако, теперь мне нужно получить избранное пользователя как:

@movies.each do |movie|
  movie.favourites.first # current users fav movie
end

, потому что я выполняюПо запросу на фильм я получаю коллекцию избранного (что имеет смысл) для всех пользователей, но отфильтровано до текущего пользователя.Так что не идеально, что мне нужно пройтись и проверить избранное, как movie.favourites.first - Мне немного странно.

Есть ли лучший запрос, который я мог бы выполнить, где я мог бы выполнить поиск по Favourite и получитьвсе результаты фильма, которые пользователь еще не одобрил?Или, может быть, что-то еще.

Мне пришлось сделать что-то вроде

Favourite.includes(:movie)....

Но я не уверен, как получить все результаты, включая непривлекательные результаты фильмов для пользователя.

Ответы [ 2 ]

0 голосов
/ 21 февраля 2019

Вы можете использовать один запрос с left join:

res = Movie.left_joins(:favourites).where(fauvorites:{ user_id: [current_user.id, nil]}).
       select(Movie.arel_table[Arel.star], "(favourites.id is null) as is_favourited_by_user")
res.first.is_favourited_by_user # 0/1

возвращенными Movie объектами будет сгенерировано поле is_favorited_by_user, указывающее, предпочитается ли конкретный фильм этим пользователем.

0 голосов
/ 21 февраля 2019

Я бы сначала запросил список любимых фильмов текущего пользователя и сохранил их идентификаторы в Set:

# in the controller
require `set`
@favorite_movies_ids = Set.new(Favourite.where(user: current_user).pluck(:movie_id))

С таким Set вы можете проверитьс очень эффективной O(1) операцией, если текущий пользователь предпочитает определенный фильм и показывает эту информацию на странице - например, так:

# in the view
<% @movies.each do |movie| %>
  <%= 'favorite!' if @favorite_movies_ids.include?(movie.id) %>
  # ...
<% end %>

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...