Rails 4.2.5
,Mongoid 5.1.0
我有三个模型 - Mailbox
,Communication
和Message
。
mailbox.rb
class Mailbox
include Mongoid::Document
belongs_to :user
has_many :communications
end
communication.rb
class Communication
include Mongoid::Document
include Mongoid::Timestamps
include AASM
belongs_to :mailbox
has_and_belongs_to_many :messages, autosave: true
field :read_at, type: DateTime
field :box, type: String
field :touched_at, type: DateTime
field :import_thread_id, type: Integer
scope :inbox, -> { where(:box => 'inbox') }
end
message.rb
class Message
include Mongoid::Document
include Mongoid::Timestamps
attr_accessor :communication_id
has_and_belongs_to_many :communications, autosave: true
belongs_to :from_user, class_name: 'User'
belongs_to :to_user, class_name: 'User'
field :subject, type: String
field :body, type: String
field :sent_at, type: DateTime
end
我正在使用身份验证gem devise
,它可以访问current_user
帮助程序,该帮助程序指向当前登录的用户。
我为满足以下条件的控制器构建了一个查询:
获取current_user
的{{1}},其mailbox
已被communication
字段过滤,box
。
它是这样构建的(并且正在工作):
box == 'inbox'
当我尝试构建此查询时,我的问题就出现了。我希望将查询链接起来,以便我只获取current_user.mailbox.communications.where(:box => 'inbox')
消息不是来自messages
的{{1}}。我知道.last方法,它返回最新的记录。我已经提出了以下查询,但无法理解需要调整哪些才能使其正常工作:
last
此查询产生以下结果:
current_user
我目前能够通过执行以下操作来完成此操作,我知道效率非常低,并希望立即更改:
current_user.mailbox.communications.where(:box => 'inbox').where(:messages.last.from_user => {'$ne' => current_user})
undefined method 'from_user' for #<Origin::Key:0x007fd2295ff6d8>
我希望将这个逻辑从ruby转移到实际的数据库查询。提前感谢任何帮助我的人,如果有更多信息对我有帮助,请告诉我。
答案 0 :(得分:0)
好的,所以这里发生的事情有点混乱,并且与做关联时Mongoid实际上能够有多聪明有关。
具体说明如何在跨越&#39;在两个协会之间。
如果是您的第一个查询:
current_user.mailbox.communications.where(:box => 'inbox')
对于mongoid而言很酷,因为实际上这只是真正的2 db调用:
现在,当我们进入您的下一个查询时,
current_user.mailbox.communications.where(:box => 'inbox').where(:messages.last.from_user => {'$ne' => current_user})
当Mongoid开始混淆时。
以下是主要问题:当您使用&#39;其中&#39;你正在查询你所在的系列。你不会过关联。
where(:messages.last.from_user =&gt; {&#39; $ ne&#39; =&gt; current_user})实际上做的是不检查消息关联。 Mongoid实际上正在做的是在通信文档中搜索具有类似于JSON路径的属性:communication [&#39; messages&#39;] [&#39; last&#39;] [&#39; ; FROM_USER&#39;。] 强>
既然你知道为什么,你可以得到你想要的东西,但它需要比同等的ActiveRecord工作更多的汗水。
以下是您可以获得所需内容的更多方式:
user_id = current_user.id
communication_ids = current_user.mailbox.communications.where(:box => 'inbox').pluck(:_id)
# We're going to need to work around the fact there is no 'group by' in
# Mongoid, so there's really no way to get the 'last' entry in a set
messages_for_communications = Messages.where(:communications_ids => {"$in" => communications_ids}).pluck(
[:_id, :communications_ids, :from_user_id, :sent_at]
)
# Now that we've got a hash, we need to expand it per-communication,
# And we will throw out communications that don't involve the user
messages_with_communication_ids = messages_for_communications.flat_map do |mesg|
message_set = []
mesg["communications_ids"].each do |c_id|
if communication_ids.include?(c_id)
message_set << ({:id => mesg["_id"],
:communication_id => c_id,
:from_user => mesg["from_user_id"],
:sent_at => mesg["sent_at"]})
end
message_set
end
# Group by communication_id
grouped_messages = messages_with_communication_ids.group_by { |msg| mesg[:communication_id] }
communications_and_message_ids = {}
grouped_messages.each_pair do |k,v|
sorted_messages = v.sort_by { |msg| msg[:sent_at] }
if sorted_messages.last[:from_user] != user_id
communications_and_message_ids[k] = sorted_messages.last[:id]
end
end
# This is now a hash of {:communication_id => :last_message_id}
communications_and_message_ids
我不确定我的代码是100%(您可能需要检查文档中的字段名称以确保我正在搜索正确的代码),但我认为您获得了一般模式