What's the best way to write a “(x AND y)OR(a AND b)” where query in Rails?
-
26-05-2021 - |
Question
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
Solution
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
OTHER TIPS
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