【问题标题】:Filter the children of a has_many/belongs_to relation, and get all their parents as well过滤 has_many/belongs_to 关系的孩子,并得到他们所有的父母
【发布时间】:2016-04-29 04:40:55
【问题描述】:

我有两个模型,我们称它们为 Parent 和 Child,其中

class Parent < ActiveRecord::Base
    has_many :child
end

class Child < ActiveRecord::Base
    belongs_to :parent
end

对于一个 API,我有兴趣返回所有匹配特定条件的子对象的列表,以及父 ID 及其对应的父对象(但只有过滤后的子对象的父对象!)而不重复。

类似:

{children: [{id: 1, parent_id: 20, attr1: x, attr2: y},...], parents: [{id: 20, attr3: z, attr4: w},...]}

最有效的方法是什么?

我已经尝试过:

children = Child.eager_load(:parent).where(condition).all
parents = children.map(&:parent).uniq{|p| p.id}

但是使用 map 和 uniq 似乎又慢又浪费?

或者,我可以使用两个 SQL 查询分别查询它们,即

children = Child.where(condition).all
parent_ids = children.map(&:parent_id)
parents = Parent.where(id: parents_ids).all

但是,由于可能有成百上千个不同的父母,这似乎仍然有些低效。

有没有更好的办法?

【问题讨论】:

    标签: ruby-on-rails activerecord


    【解决方案1】:

    尝试使用pluck将其推送到数据库

    children = Children.eager_load(:parent).where(condition).all
    parents = children.pluck('distinct parent_id')
    

    编辑

    我误解了这个问题。还有另一种选择,但我不确定它是否比运行所有这些(就像您在问题中显示的那样)或运行多个查询(就像您需要使用 pluck 一样)好得多。您可以使用 Set 来确保父对象的唯一性:

    parents = Set.new
    children = Children.eager_load(:parent).where(condition).all
    children.each {|child| parents << child.parent }
    

    【讨论】:

    • 这会返回一个包含所有父 ID 的数组,类似于我提到的第二种可能性,对吧?如果我想要真正的父母自己怎么办?我应该只做Parent.where(id: parents).all吗?
    • 是的,它返回一个父 ID 数组。它替换了parents = children.map(&amp;:parent).uniq{|p| p.id} 行,因此如果您还想要父对象,则必须重新查询数据库。在 Ruby 中枚举数以千计的对象对于大多数用例来说应该足够快,因此如果您需要它们的 ID 和父对象(看起来就像你已经知道该怎么做一样:)
    • parents = children.map(&amp;:parent).uniq{|p| p.id} 实际上返回父对象本身(注意它的 &:parent 而不是 &:parent_id),无需第二个 SQL 查询(因为他们已经迫不及待地加载了)。我不确定它是否非常有效(由于同时使用了 map 和 uniq),因此是我的问题。
    • 噢噢噢噢。我懂了。一秒钟,我会更新答案
    • 谢谢,设置方法似乎更好。我会等一会儿,看看其他人是否有更好的解决方案,如果没有,我会将其标记为正确。
    猜你喜欢
    • 1970-01-01
    • 2015-02-23
    • 1970-01-01
    • 2011-08-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多