Rails:没有通过表的自引用父/子层次结构

时间:2016-09-29 22:38:22

标签: ruby-on-rails activerecord

我有一个包含parent_iddate属性的事件模型:

Event.rb

has_many :children, :class_name => "Event"
belongs_to :parent, :class_name => "Event" 

我在调用event.parentevent.children时没有任何问题。儿童事件本身从未有过孩子。

我正在尝试为此模型添加一个范围,以便我可以为每个父项返回具有最近未来日期的子项。类似的东西:

scope :future, -> {
    where("date > ?", Date.today)
  }

scope :closest, -> {
    group('"parent_id"').having('date = MAX(date)')
 }

Event.future.closest ==> returns the closest child event from every parent

但是上面的:closest范围每个父母都会返回多个孩子。

3 个答案:

答案 0 :(得分:1)

暂时忽略Rails,你在SQL中做的是问题。这是lots of solutions。我会选择DISTINCT ONLEFT OUTER JOIN LATERAL。以下是它在Rails中的外观:

scope :closest, -> {
  select("DISTINCT ON (parent_id) events.*").
    order("parent_id, date ASC")
}

这将为您提供子对象。 (您可能还希望条件排除没有parent_id的行。)从您自己的解决方案中,听起来就像您想要的那样。如果您希望父对象,并使用可选的附加子对象,则使用横向连接。虽然转换成ActiveRecord有点麻烦。如果在两个查询中执行它是可以接受的,这看起来应该可以工作(坚持使用DISTINCT ON):

has_one :closest_child, -> {
  select("DISTINCT ON (parent_id) events.*").
    order("parent_id, date ASC")
}, class_name: Event, foreign_key: "parent_id"

然后你可以说Event.includes(:closest_child)。同样,您可能希望过滤掉所有非父母。

答案 1 :(得分:0)

我最终使用了:

  scope :closest, -> {
    where(id: Event.group(:parent_id).minimum(:date).keys)
  }

答案 2 :(得分:0)

你自己的答案看起来不错,但我会按照以下方式改进它:

scope :closest, -> {
  where.not(parent_id: nil).group(:parent_id).minimum(:date)
}

非常重要或者在生产中你总是将部署日期定为Date.today,因为它只会在开发中重新加载:

scope :future, -> {
  where("date > ?", Proc.new { Date.today })
}