ActiveRecord:如何找到所有孩子符合条件的父母?

时间:2015-05-29 17:04:57

标签: ruby-on-rails postgresql activerecord

假设我的Parent模型有很多ChildChild也属于OtherParent

如何找到所有Parent所有Child属于OtherParent的所有Parent.find_by_sql(<<SQL) SELECT * FROM parents p WHERE NOT EXISTS ( SELECT * FROM children WHERE parent_id = p.id AND other_parent_id IS NULL ) SQL

在纯SQL中,我可以做到

function create_dwb_menu() {
    global $wp_admin_bar;

    $menu_id = 'dwb';
    $wp_admin_bar->add_menu(array('id' => $menu_id, 'title' => __('DWB'), 'href' => '/'));
    $wp_admin_bar->add_menu(array('parent' => $menu_id, 'title' => __('Homepage'), 'id' => 'dwb-home', 'href' => '/', 'meta' => array('target' => '_blank')));
    $wp_admin_bar->add_menu(array('parent' => $menu_id, 'title' => __('Drafts'), 'id' => 'dwb-drafts', 'href' => 'edit.php?post_status=draft&post_type=post'));
    $wp_admin_bar->add_menu(array('parent' => $menu_id, 'title' => __('Pending Comments'), 'id' => 'dwb-pending', 'href' => 'edit-comments.php?comment_status=moderated'));
}
add_action('admin_bar_menu', 'create_dwb_menu', 2000);

(来自here),但如果可能的话,我更愿意利用ActiveRecord。

谢谢!

我使用的是Rails 4.2.1和PostgreSQL 9.3

3 个答案:

答案 0 :(得分:3)

使用arel可以让你走得更远。棘手的部分是如何使用arel自己的查询语法编写整个查询?

以下是一个技巧:使用where构建查询时,如果使用arel条件,则可以免费获得一些额外的方法。例如,您可以使用.exists.not来拖动您在那里的子查询,这将为您提供一个(NOT ( EXISTS (subquery)))折腾到父项的where - 子句中并且您已设置。

问题是,您如何参考所涉及的表格?你需要Arel。您可以使用Arel的where及其丑陋的条件,例如a.eq b。但为什么?由于它是一个相等的条件,你可以使用Rails的条件!您可以使用散列键引用要查询的表,但对于另一个表(在外部查询中),您可以使用其arel_table。看这个:

parents = Parent.arel_table
Parent.where(
  Child.where(other_parent_id: nil, parent_id: parents[:id]).exists.not
)

您甚至可以通过稍微使用字符串来减少Arel的使用,并依赖于您可以将子查询作为参数提供给Rails'where。它没有多大用处,但它并没有强迫你过多地挖掘Arel的方法,所以你可以使用那个技巧或其他带有子查询的SQL运算符(还有其他的吗?):

parents = Parent.arel_table
Parent.where('NOT EXISTS (?)',
  Child.where(parent_id: parents[:id], other_parent_id: nil)
)

这里的两个要点是:

  • 您可以使用与构建常规查询相同的方式构建子查询,并使用Arel引用外部查询的表。它甚至可能不是真正的表,它可能是别名!疯狂的东西。
  • 您可以使用子查询作为Rails'where方法的参数。

答案 1 :(得分:2)

使用例外scuttle,您可以将任意SQL转换为ruby(ActiveRecord和ARel查询)

通过该工具,您的查询将转换为

Parent.select(Arel.star).where(
  Child.select(Arel.star).where(
    Child.arel_table[:parent_id].eq(Parent.arel_table[:id]).and(Child.arel_table[:other_parent_id].eq(nil))
  ).ast
)

拆分查询 -

  • Parent.select(Arel.star)将查询Parent表中的所有列。
  • Child.arel_table带您进入Arel-world,让您在从ruby生成查询时获得更多功能。具体来说,Child.arel_table[:parent_id]为您提供了一个Arel :: Attributes :: Attribute的句柄,您可以在构建查询时继续使用它。
  • .eq.and方法完全符合您的预期,让您构建任意深度和复杂度的查询。

不一定“更干净”,但完全在红宝石中,这很不错。

答案 2 :(得分:-1)

给定ParentChild,并且子项与belongs_to处于OtherParent关系(假定为Rails默认值):

Parent.joins(:childs).where('other_parent_id = ?', other_parent_id)