【问题标题】:How to extract Mongoid documents based on a field value in the first or last embedded document?如何根据第一个或最后一个嵌入文档中的字段值提取 Mongoid 文档?
【发布时间】:2013-05-10 21:15:47
【问题描述】:

我希望根据最后嵌入的Notificationdocument 中的字段查找Order 文档。

在下面的示例中,我希望找到所有嵌入了一个或多个notificationspending orders,并且最后一个notificationdatetime 存在5 到10 天。

我在这里的建议似乎没有奏效......:

Order.where(status: 'pending').gte('notifications.last.datetime' => 5.days.ago).lte('notifications.last.datetime' => 10.days.ago)

这是两个模型:

class Order
  include Mongoid::Document
  field :datetime, type: DateTime
  field :status, type: String, default: 'pending'
  embeds_many :notifications,  :inverse_of => :order
end

class Notification
  include Mongoid::Document
  field :datetime, type: DateTime
  embedded_in :order, :inverse_of => :notifications
end

【问题讨论】:

    标签: ruby mongodb mongoid


    【解决方案1】:

    问题的主要问题似乎是如何在查询中引用数组的最后一个元素。

    不幸的是,从 MongoDB 2.4 开始这是不可能的。 实现此功能的最简单方法是使用负值指向数组中的元素,如'notifications.-1.datetime',但它不起作用。 (参考[#SERVER-5565] Handle negative array offsets consistently - MongoDB。)

    更糟糕的是,使用聚合框架似乎也无法解决这个问题。没有办法 当$unwinding ([#SERVER-4588] aggregation: add option to $unwind to emit array index - MongoDB) 或 $projecting 时动态选择数组的索引。 ([#SERVER-4589] aggregation: need an array indexing operator - MongoDB)

    因此,您唯一的选择似乎是更改架构以匹配您想要的。最简单的方法是向Order 添加一个包含最后一个Notificationdatetime 的字段。

    更新:

    您可以先从服务器获取所有候选,然后在客户端缩小范围以获得最终结果集。这不涉及架构更改。如果数据库规模相对较小或性能下降是可以接受的,这可能是最好的解决方案。

    query = Order.where(status: 'pending').elem_match(
      notifications: { datetime: { '$gte' => 10.days.ago, '$lte' => 5.days.ago } })
    query.select do |order|
      # datetime = order.notifications[0].datetime
      datetime = order.notifications[order.notifications.size - 1].datetime
      10.days.ago <= datetime && datetime <= 5.days.ago
    end.each do |order|
      p order # result
    end
    

    【讨论】:

    • 感谢@Akihiro Harai 的深入研究。不幸的是,在这种情况下,并不总是需要最后一个notification,而是有时需要第一个,因此在Order 中添加一个额外的字段对我来说不是一个稳定的解决方案。你有想法用一个 mongoid 查询来解决排序问题,然后是一些 Ruby 逻辑吗?
    • 您可以使用notifications.0.datetime 引用第一个元素。但是,如果您真的不想更改架构,还有另一种方法。首先从服务器获取候选人,然后将其缩小到客户端的结果。这是你想要的吗?
    • 是的,这正是我想要的 :-) 只是更改架构似乎是合理的,但在许多其他方面,让我的关系保持嵌入是很有意义的。因此,如果您能够整理一段从服务器获取候选人的代码,然后在客户端缩小范围(或者在这种情况下,它将在我的 Rails 应用程序的控制器中),我会非常满意。
    • 更新了答案。这个怎么样?
    【解决方案2】:

    我知道它来得有点晚,但是嘿,迟到总比没有好。 :P

    您可以在以下位置使用 JavaScript:

    Order.where("this.notifications[this.notifications.length - 1].datetime > new Date('#{5.days.ago}')")
    

    刚刚发现这一点,不用改变我的模型让我松了一口气。希望对您有所帮助!

    【讨论】:

      猜你喜欢
      • 2023-03-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-02-07
      • 1970-01-01
      • 2013-02-08
      • 1970-01-01
      相关资源
      最近更新 更多