Вопрос

Я новичок в рельсах и пытаюсь выполнить левое соединение в MySQL.

есть два объекта - пользователь и сообщение.

пользователь has_and_belongs_to_many сообщения, сообщение has_and_belongs_to_many пользователей

в настоящее время, просто написав user.messages, я получаю следующий запрос в консоли

SELECT * FROM `messages` INNER JOIN `messages_users` ON `messages`.id = `messages_users`.message_id WHERE (`users_messages`.group_id = 1 )

Сообщение с ограниченным==false не связано ни с одним пользователем, но доступно любому пользователю, и мне нужно добавить коллекцию Message.all(restricted=>false) в user.messages

запрос, который решит мою проблему, будет:

select * from messages left join messages_users on messages_users.message_id=messages.id and messages_users.user_id=1 where (messages_users.user_id is NULL and messages.restricted=false) OR (messages_users.user_id=1 and messages.restricted=true);

как мне написать это в рельсах как можно элегантнее?

было бы это что-то вроде

Message.find(:all,:conditions => "(messages_users.user_id is NULL and messages.restricted=false) OR (messages_users.user_id=1 and messages.restricted=true)", :joins => "left join messages_groups on messages_users.message_id=messages.id and messages_users.user_id=1 " )

или может быть лучше?

я использую рельсы 2.3.2

Спасибо, Павел

Это было полезно?

Решение

Мне кажется, что вы пытаетесь извлечь из этого запроса две вещи:1) все сообщения, не привязанные к пользователю с ограниченным = false и 2) все сообщения, привязанные к текущему пользователю с ограниченным = true.

Если я правильно понимаю, я не вижу лучшего способа сделать это, если вы хотите, чтобы это было выполнено в виде одного запроса.Однако, если вы готовы к идее создания двух запросов, вы можете немного очистить код (возможно, замедлив выполнение).Вот альтернативная установка:

class Message < ActiveRecord:Base
  has_and_belongs_to_many :users

  named_scope :restricted, :conditions => {:restricted => true}
  named_scope :unrestricted, :conditions => {:restricted => false}
  named_scope :public, :conditions => "id NOT IN (SELECT DISTINCT message_id FROM messages_users)"
end

class User < ActiveRecord:Base
  has_and_belongs_to_many :messages
end

Чтобы получить список с меньшим количеством кода, но требуя двух обращений к базе данных, вы можете сделать:

@current_user.messages.restricted + Message.unrestricted.public

В любом случае, если в этих таблицах содержится значительный объем данных, вам необходимо убедиться, что они правильно проиндексированы, иначе это будет замедляться при любой нагрузке.Если это приложение, в котором отправляется много сообщений, вам, вероятно, лучше использовать один запрос.Если это просто побочная функция, которая не будет часто использоваться, я бы, наверное, взял более чистую версию.

С точки зрения модели, вероятно, было бы лучше избавиться от отношений HABTM и смоделировать их явно.Тогда у вас будет удобное место для отслеживания других данных о процессе отправки/доставки/получения сообщения (например, отслеживание времени отправки, времени чтения и т. д.).Это не меняет ничего из вышеизложенного, я просто предпочитаю, чтобы многие дошли до HABTM.

class Message < ActiveRecord:Base

  has_many :subscriptions
  has_many :users, :through => :subscriptions

  named_scope :restricted,   :conditions => {:restricted => true}
  named_scope :unrestricted, :conditions => {:restricted => false}
  named_scope :public, :conditions => "id NOT IN (SELECT DISTINCT message_id FROM subscriptions)"
end

class User < ActiveRecord:Base

  has_many :subscriptions
  has_many :messages, :through => :subscriptions

end

class Subscription < ActiveRecord:Base

  belongs_to :message
  belongs_to :user

end

Другие советы

почему бы не использовать :include?

Я думаю, что Named_scopes может быть вашим ответом.В вашей модели добавьте что-то вроде:

named_scope :restricted,   :conditions => {:restricted => true}
named_scope :unrestricted, :conditions => {:restricted => false}

Затем вы можете вызывать такие вещи, как:

Message.restricted
=> All restricted messages

User.first.messages.unrestricted
=> All unrestricted messages belonging to the first user.

Я считаю, что они работают через ассоциации HABTM.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top