【问题标题】:Rails find_by_sql returning array with empty objectRails find_by_sql 返回带有空对象的数组
【发布时间】:2020-02-09 17:35:05
【问题描述】:

我正在使用 find_by_sql 使用 Postgres 作为数据库服务器对我的 Conversationalist 模型进行查询:

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...>]

这是我在数据库上直接查询得到的结果:

我期待的是一个空数组,因为不应该返回任何行,而是得到一个结果。如果没有返回结果,我将如何获得一个空数组?

附加上下文

我想要做的基本上是聊天。当有人向另一个用户发送消息时,上面的代码首先检查这两个人是否已经在聊天。如果是,则将消息添加到聊天中。如果没有,则会创建一个新的聊天并添加消息:

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

【问题讨论】:

  • 我不明白你在这里的用法。您将整个想法封装在一个数组中。您可以尝试只调用find_by_sql('Select... ') 而不使用封闭数组或末尾的参数吗?
  • 我需要数组来绑定参数(recipient_id,recipient_type...)(api.rubyonrails.org/classes/ActiveRecord/Querying.html)。如果没有数组,你会得到一个错误:“参数数量错误(给定 5,预期 1..2)”
  • 我明白你对参数的意思,我没有使用find_by_sql 我需要传递参数的地方,所以让我愣了一秒。感觉就像您的查询返回某种空行而不是 nil。这就是为什么我会在psql 中运行它来看看你得到了什么。也很难可视化,因为我们不知道您的整个数据结构。你能发布一个完整的空对象inspect吗?
  • 很奇怪,我可以在我的一个项目上对其进行测试,并且 0 行总是给出一个空数组,就像您期望的那样。我认为这也是预期的行为,因为这是我一直得到的。您能否展示您最初发布的代码的一些上下文?它在 Rails 控制台上做同样的事情吗?
  • 这有帮助。如果你只在 Rails 控制台上运行原始的 find_by_sql 查询,它仍然返回一个空对象?

标签: ruby-on-rails postgresql activerecord


【解决方案1】:

所以我能够在上面 cmets 的 @Beartech 的帮助下解决这个问题。本质上,问题是由于 LEFT JOIN 而发生的。如果t1 中有任何结果,Rails 将返回一个包含空对象的数组。同样,如果它是一个 RIGHT JOIN 并且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])

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-07-13
    • 1970-01-01
    • 1970-01-01
    • 2014-04-28
    • 2022-01-01
    • 2020-02-11
    • 1970-01-01
    相关资源
    最近更新 更多