使用参数化从原始 SQL 查询返回 ActiveRecord 关系

问题描述 投票:0回答:1

我有一个名为

VideoEventBundle
的 Rails 模型,其中包含各个事件。这通过外键与
VideoMeasurement
模型关联,并且
VideoMeasurement
模型本身是
Measurement
可能的多态子模型之一。

SQL 架构:

CREATE TABLE public.video_event_bundles (
    id bigint NOT NULL,
    video_measurement_id bigint,
    events jsonb DEFAULT '[]'::jsonb
);

CREATE TABLE public.video_measurements (
    id bigint NOT NULL,
);

CREATE TABLE public.measurements (
    id bigint NOT NULL,
    customer_id bigint NOT NULL,
    actable_type character varying,
    actable_id bigint
);

现在,在主要返回测量值的 API 上下文中,我还想导出属于用户已选择/过滤的测量值的所有视频事件包。为此,我有一个通用方法,根据用户的过滤器将

@measurements
设置为 ActiveRecord 关系。例如:

@measurements = Measurement.where(customer_id: params[:customer_id])

为了获取实际的事件包,我曾经有过这样的查询:

    VideoEventBundle.find_by_sql([
      "SELECT video_event_bundles.*
        FROM video_event_bundles
          JOIN video_measurements ON video_event_bundles.video_measurement_id = video_measurements.id
          JOIN measurements ON video_measurements.id = measurements.actable_id
        WHERE measurements.actable_type = 'VideoMeasurement'
        AND measurements.id IN (?)
      ",
      @measurements.select(:id)
    ])

问题是这返回一个对象数组,而不是 ActiveRecord 关系。问题是,在未经过滤的测量情况下,该数组将会很大。由于 API 是分页的,我需要能够将限制/偏移量传递给该方法,因此,我必须实际返回一个 ActiveRecord 关系。

现在我在这里看到了多个与从原始 SQL 查询返回相关的问题:

但我无法设法使用

(?)
参数来实际替换 ID:

sql = <<-SQL
      SELECT video_event_bundles.*
        FROM video_event_bundles
          JOIN video_measurements ON video_event_bundles.video_measurement_id = video_measurements.id
          JOIN measurements ON video_measurements.id = measurements.actable_id
        AND measurements.id IN (?)
    SQL

    VideoEventBundle.select("*").from("(#{sql}) AS video_event_bundles", @measurements.select(:id))

这会引发错误:

ActiveRecord::StatementInvalid: PG::SyntaxError: ERROR:  syntax error at or near ")"
LINE 5:         AND measurements.id IN (?)

我想我无法通过使用

@measurements.join(…)
开始查询来解决这个问题?

ruby-on-rails activerecord
1个回答
0
投票

我们可以使用 AR 的内置机制和一点 Arel 以更简单的方式完成此任务:

measurement_table = Measurement.arel_table
vm_table = VideoMeasurement.arel_table
measurement_join = measurement_table.join(vm_table)
   .on(vm_table[:id].eq(measurement_table[:actionable_id])
         .and(measurement_table[:actable_type].eq('VideoMeasurement'))
VideoEventBundle
  .joins(:video_measurements)
  .joins(measurement_join)
  .where(measurements: {id: @measurements.select(:id))

我已经有一段时间没有使用rails了,所以也许可以只使用:

VideoEventBundle
  .joins(video_measurements: :measurements)
  .where(measurements: {id: @measurements.select(:id))

我只是不能 100% 确定这是否适用于多态性

© www.soinside.com 2019 - 2024. All rights reserved.