문제

What's the best way to write a "(x AND y)OR(a AND b)" where query in Rails?

I've just written the following messages method to return messages between two users. The select is to get messages between two users i.e. give me messages from me to them and them to me.

It works but it looks horrid. Is there a simpler/better way of writing this?

class Conversation
  def initialize(me, them)
    @me = me
    @them = them
  end

  def messages
    t = Message.arel_table
    results = Message.where(
      (t[:sender_id].eq(@me.id).and(t[:recipient_id].eq(@them.id))).or(
      t[:sender_id].eq(@them.id).and(t[:recipient_id].eq(@me.id)))
    )
  end
end

NOTE Thanks to Jimmy, I have ended up with:

class Conversation
  def initialize(me, them)
    @me = me
    @them = them
  end

  def messages
    me_to_them = "sender_id = :my_id AND recipient_id = :their_id"
    them_to_me = "sender_id = :their_id AND recipient_id = :my_id"
    Message.where(
      "#{me_to_them} OR #{them_to_me}",
      {:my_id => @me.id, :their_id => @them.id}
    )
  end
end
도움이 되었습니까?

해결책

You can clean it up a little by using a SQL string and the array syntax for value interpolation:

class Conversation < ActiveRecord::Base
  def initialize(me, them)
    @me = me
    @them = them
  end

  def messages
    Message.where(["(sender_id = ? AND recipient_id = ?) OR (sender_id = ? AND recipient_id = ?)", @me.id, @them.id, @them.id, @me.id])
  end
end

다른 팁

I have built a similar system to this before. My database was structured a little bit differently however to make the queries simpler, and allow for a group conversation (more then 2 people)

private_message_groups 
id | subject

private_message
id | group_id | private_message_group_id | user_id | message

private_message_follow
group_id | user_id | visible 

This way you can simply point the user to a private_message controller. If you want me to go in more depth I can, but the biggest advantage of this system is now you have a forum system.

I had the same problem. I was searching the web for some hours and finally found a method named grouping in Arel::FactoryMethods which simply adds brackets around an expression.

With this your method should look like this:

def messages
  t = Message.arel_table
  results = Message.where(
    t.grouping(t[:sender_id].eq(@me.id).and(t[:recipient_id].eq(@them.id))).or(
    t.grouping(t[:sender_id].eq(@them.id).and(t[:recipient_id].eq(@me.id))))
  )
end
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top