ruby-on-rails - activerecord如何查询关联的多个条件?

标签 ruby-on-rails activerecord

我正在尝试为我的 EventsLog 模型创建一个范围,它看起来类似于 EventsLog.with_values({"value_name"=> "value", "other_value_name"=> "other_value"})

其结果将是 EventsLog 记录,该记录对于散列中的每个键值对都有一个关联的 EventsLogValue。

这是我必须处理的。 定义如下所示的两个表:

--table for tracking events
CREATE TABLE events_log(
    id INT PRIMARY KEY IDENTITY(1,1),
    event_name VARCHAR(25), --name of the event
    created_at DATETIME
);

--table for tracking the values corresponding to the event
CREATE TABLE events_log_values(
    id INT PRIMARY KEY IDENTITY(1,1),
    event_id INT,
    value VARCHAR(255),
    value_name VARCHAR(25),
);

从这两个表中,两个模型看起来像:

class EventsLog < BaseAPIDatabase
    self.table_name = "events_log"
    self.primary_key = "id"

    has_many :events_log_values, :foreign_key => "event_id", :primary_key => "id", :class_name => "EventsLogValue", :autosave => true

    scope :since, ->(since){ where("created_at > ?", since)}
    scope :named, ->(event_name){ where(:event_name => event_name) }


    def values
        events_log_values.inject({}) do |hsh, v|
            hsh.merge({v.value_name => v.value})
        end
    end

end


class EventsLogValue < BaseAPIDatabase
    self.table_name = "events_log_values"
    self.primary_key = "id"

end

到目前为止,我的方法是尝试创建一个函数,该函数返回一个事件记录关系,该关系一次应用一个键值对,然后再添加一个范围(或者可能只是一个返回关系的类方法)它为我链接了它们(类似于 scope :with_values, ->(values){values.inject(self){|slf, (k, v)| slf.with_value(k, v)} } )。

最初我尝试将 with_value 实现为一个相当标准的范围,scope :with_value, ->(val_name, val){ eager_load(:events_log_values).where(:events_log_values => {:value_name => val_name, : value => val}) },它本身工作正常,但当链接时会导致单个连接,并在连接值上有多个条件。

决定通过将值表与每个条件的别名连接起来来解决这个问题;我的新方法是在我的 with_value 函数中定义一个 has_many 关联,然后 eager_load 该关联并根据每个新关联添加一个 where 条件:

def self.with_value(val_name, val)
    has_many val_name.to_sym, ->(){ where(:value_name => val_name) }, :foreign_key => "event_id", :primary_key => "id", :class_name => "EventsLogValue"
    res = eager_load(:events_log_values)
    res.eager_load(val_name.to_sym).where("#{val_name.pluralize}_events_log" => {:value => val})
end

这实际上工作得很好,但有一些问题。第一个是我很难知道在 where 条件下关联的名称是什么。第二个(也是更大的问题)是我的值函数现在只有任何 value_names 没有为它们建立关联。

这是由多个 has_manys 生成的一些 sql,可能有助于说明我正在尝试做的事情:

EventsLog.with_values("hello" => "world", "foo" => "bar").to_sql
SELECT ...
FROM [events_log] 
LEFT OUTER JOIN [events_log_values] ON [events_log_values].[event_id] = [events_log].[id] 
LEFT OUTER JOIN [events_log_values] [hellos_events_log] ON [hellos_events_log].[event_id] = [events_log].[id] AND [hellos_events_log].[value_name] = 'hello' 
LEFT OUTER JOIN [events_log_values] [foos_events_log] ON [foos_events_log].[event_id] = [events_log].[id] AND [foos_events_log].[value_name] = 'foo' 
WHERE [hellos_events_log].[value] = 'world' AND [foos_events_log].[value] = 'bar'

我怎样才能得到一条记录,该记录有多个关联记录满足多个不同的条件?

最佳答案

这是我提出问题后得出的答案。它使用 arel 为每个值生成带有别名的 sql 连接,并为每个值生成一个 where 条件。

这不是最干净的东西,但它似乎完成了工作。

def self.with_values(values)
    el = EventsLog.arel_table
    arel_joins = el
    arel_wheres = []

    values.each do |k, v|
        ev = EventsLogValue.arel_table.alias("#{k}_join")

        arel_joins = arel_joins.join(ev).on(el[:id].eq(ev[:event_id]).and(ev[:value_name].eq(k)))
        arel_wheres << ev[:value].eq(v)
    end

    arel_wheres.inject(EventsLog.joins(arel_joins.join_sources)){|rel, con| rel.where(con)}
end

附注我想我在某处读到 Model.arel_table 没有记录并且不应该使用?谨慎的做法是改用 Arel::Table.new('table_name')。

关于ruby-on-rails - activerecord如何查询关联的多个条件?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42795757/

相关文章:

ruby-on-rails - 如何使用大量 "have_link"保持 rspec 测试干燥

javascript - 付费成员(member) Stripe Ruby

ruby-on-rails - 如何在 Rails 4 中将 html 类添加到 I18n 语言环境链接?

jquery - Ruby on Rails ajax DELETE 请求未到达后端

php - 代码点火器。如何返回变量值大于数据库中字段的记录?

ruby-on-rails - Rspec:如何测试 ActiveRecord::Base.connection.execute

ruby-on-rails - 更好地理解 Rails 中的格式关键字

ruby-on-rails - Ruby,动态查询事件模型

ruby-on-rails - Rails Active Record 选择父和子作为一个结果

postgresql - rdbms 查询中语言标签(小标签集)的有效方法