具有多个ID的复杂数据库查询

时间:2013-07-01 21:00:51

标签: ruby-on-rails ruby ruby-on-rails-3 ruby-on-rails-3.2

我有3个模型连接如下:

class FooTemplate < ActiveRecord::Base
    has_many :foos
end

class Foo < ActiveRecord::Base
    belongs_to :foo_template
    belongs_to :bar
end

class Bar < ActiveRecord::Base
    has_many :foos
end

Bar模型中,我想要一个方法,找到Bars的所有Foos属于数组中的ID所引用的每个FooTemplate

def find_by_foo_templates(foo_template_ids)
    # what goes here?
end

该参数是一个始终具有以下形式的数组:

[""]
["", "1", "2"]

即使没有提交ID,数组也始终包含空字符串。

我希望你明白我要做的事。

更新

让我举个例子:

Bar: 1
  Foo: 1
    FooTemplate: 1
  Foo: 2
    FooTemplate: 2

Bar: 2
  Foo: 3
    FooTemplate: 2
  Foo: 4
    FooTemplate: 3

Bar: 3
  Foo: 5
    FooTemplate: 3
  Foo: 6
    FooTemplate: 4

这将是3个酒吧,每个有2个独立的Foos。 Foos有一些“重叠”的FooTemplates。

现在某些列表的预期结果:

["1"] schould only return Bar 1, because it's the only one whose Foos have a FooTemplate 1.
["2"] should return Bar 1 & 2, because they both have a Foo with a FooTemplate 2
["2", "3"] should return only Bar 2, because it's the only one which has a Foo with FooTemplate 2 AND a Foo with FooTemplate 3
["1", "4"] should return nothing because there is no Bar whose Foos have FooTemplates 1 AND 4

更新2

我找到了一个有效的解决方案,但它使用了拒绝,它会产生更多的数据库查询:

class Bar < ActiveRecord::Base
    has_many :foos
    has_many :foo_templates, through: :foos

    def self.find_by_foo_template_ids(foo_template_ids)
        ids = foo_template_ids.map { |id| id.to_i }

        joins(foos: :foo_template).uniq.where(foos: { foo_template_id: ids }).reject do |bar|
            !(bar.foo_template_ids & ids == ids)
        end
    end
end

这会返回一个数组,但我希望有一个ActiveRecord::Relation来执行其他查询。

2 个答案:

答案 0 :(得分:2)

def self.find_by_foo_templates(foo_template_ids)
  joins(:foos => :foo_template).where(['foo_templates.id in (?)', foo_template_ids.reject!(&:empty?)])
end

答案 1 :(得分:0)

N.B。这几乎肯定是PostGres。它也感觉有点像黑客。但我认为它会起作用。

def self.find_by_foo_templates(foo_template_ids)
  joins(:foos => :foo_template).
  group('bars.id').
  having("array_agg(foo_templates.id) @> string_to_array(?, ',')::int[]", foo_template_ids.join(','))
end

这应该聚合与每个返回的Bar关联的Foo一起使用的FooTemplates的id,并且仅返回包含所有输入id的Bars。