Rails find_by_ sql возвращает массив с пустым объектом - PullRequest
0 голосов
/ 09 февраля 2020

Я использую find_by_sql, чтобы выполнить запрос в моей модели Conversationalist, используя Postgres в качестве сервера БД:

Conversationalist.find_by_sql(['
  SELECT * FROM (
    SELECT * FROM conversationalists
    WHERE conversable_id = ? AND conversable_type = ?
  ) t1 
  LEFT JOIN (
    SELECT * FROM conversationalists 
    WHERE conversable_id = ? AND conversable_type = ?
  ) t2 
  ON t1.chat_id = t2.chat_id',
  recipient_id, recipient_type, sender_id, sender_type])

Это прекрасно работает, если есть результат. Но если результата нет, я получаю массив с пустым Conversationalist объектом: [#<Conversationalist id: nil, conversable_type: nil...>]

Вот что я получаю в результате выполнения прямого запроса к БД:

enter image description here

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

ДОПОЛНИТЕЛЬНЫЙ КОНТЕКСТ

По сути, я пытаюсь сделать чат. Когда кто-то отправляет сообщение другому пользователю, приведенный выше код сначала проверяет, общаются ли эти два человека уже в чате. Если они есть, сообщение добавляется в чат. Если нет, то создается новый чат и добавляется сообщение:

class MessagesController < ApplicationController
  def create
    message = new_message
    conversation = already_conversing?
    if conversation.empty? || conversation.first.id.nil?
      chat = Chat.new
      chat.messages << message
      chat.conversationalists << sender
      chat.conversationalists << recipient
      chat.save!
    else
      chat = Chat.find(conversation.first.chat_id)
      chat.messages << message
    end
    head :ok
  end

  private

  def new_message
    Message.new(
      sender_id: params[:sender_id], 
      sender_type: params[:sender_type],
      recipient_id: params[:recipient_id],
      recipient_type: params[:recipient_type],
      message: params[:message] 
    )
  end

  def already_conversing?
    Conversationalist.conversing?(
      params[:recipient_id], 
      params[:recipient_type], 
      params[:sender_id], 
      params[:sender_type]
    )
  end
end

Модель:

class Conversationalist < ApplicationRecord
  def self.conversing?(recipient_id, recipient_type, sender_id, sender_type)
    Conversationalist.find_by_sql(['
      SELECT * FROM (
        SELECT * FROM conversationalists
        WHERE conversable_id = ? AND conversable_type = ?
      ) t1 
      LEFT JOIN (
        SELECT * FROM conversationalists 
        WHERE conversable_id = ? AND conversable_type = ?
      ) t2 
      ON t1.chat_id = t2.chat_id',
      recipient_id, recipient_type, sender_id, sender_type])
  end
end

1 Ответ

1 голос
/ 09 февраля 2020

Так что я смог понять это с помощью @Beartech из комментариев выше. По сути, проблема произошла из-за левого соединения. Если есть какие-либо результаты в t1, то Rails возвращает массив с пустым объектом. Точно так же, если бы это было ПРАВИЛЬНОЕ СОЕДИНЕНИЕ и t2 имел результат, Rails сделал бы то же самое. Таким образом, чтобы получить пустой массив, нужно изменить соединение на INNER JOIN:

Conversationalist.find_by_sql(['
  SELECT * FROM (
    SELECT * FROM conversationalists
    WHERE conversable_id = ? AND conversable_type = ?
  ) t1 
  INNER JOIN (
    SELECT * FROM conversationalists 
    WHERE conversable_id = ? AND conversable_type = ?
  ) t2 
  ON t1.chat_id = t2.chat_id',
  recipient_id, recipient_type, sender_id, sender_type])
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...