Rails 5 - 使用范围方法和关联模型查询

时间:2017-08-10 17:39:55

标签: ruby-on-rails scope rails-activerecord ruby-on-rails-5

我正在尝试创建一个范围方法,该方法仅查询最近更新的记录(或者最近更新了它们的相关模型)。我相信生成的SQL语句看起来没问题,但是当应该有一些时,我没有看到任何结果。谢谢你的帮助!

控制器

class Api::V1::EncountersController < ApplicationController
  respond_to :json

  def index
    if params.has_key?(:datestart)
      ...
    elsif params.has_key?(:updated_since)
      date = Date.parse(params[:updated_since])
      respond_with(Encounter.by_updated_since(date),
                  :include => [:facility, :physician, :encounter_type, :group, :provider, :insurance],
                  :except => [:facility_id, :physician_id, :encounter_type_id, :group_id, :provider_id, :insurance_id])
    else
      ...
    end
  end

模型

    class Encounter < ApplicationRecord
      validates :encounter_type, :physician, :facility, :datetime_start_utc_scheduled, :procedures, :presence => true
      belongs_to :encounter_type
      belongs_to :physician
      belongs_to :facility
      belongs_to :insurance
      belongs_to :group, optional: true
      has_many :encounter_procedures, :dependent => :destroy
      has_many :procedures, through: :encounter_procedures
      has_many :actuals, :dependent => :destroy
      has_many :providers, through: :actuals

      ...

      scope :by_updated_since, -> updated_at {
        joins(:facility)
        .joins(:physician)
        .joins(:encounter_type)
        .joins(:group)
        .joins(:insurance)
        .where("encounters.updated_at >= ? OR
               facilities.updated_at >= ? OR
               physicians.updated_at >= ? OR
               encounter_types.updated_at >= ? OR
               groups.updated_at >= ? OR
               insurances.updated_at >= ?",
               updated_at, updated_at, updated_at, updated_at, updated_at, updated_at)}
   ...
   end

使用生成SQL登录

app/controllers/api/v1/encounters_controller.rb:12:in `index'
Started GET "/api/v1/encounters?updated_since=2017-08-01" for 127.0.0.1 at 2017-08-10 11:55:01 -0500
Processing by Api::V1::EncountersController#index as JSON
  Parameters: {"updated_since"=>"2017-08-01"}
  Encounter Load (32.2ms)  SELECT "encounters".* FROM "encounters" INNER JOIN "facilities" ON "facilities"."id" = "encounters"."facility_id" INNER JOIN "physicians" ON "physicians"."id" = "encounters"."physician_id" INNER JOIN "encounter_types" ON "encounter_types"."id" = "encounters"."encounter_type_id" INNER JOIN "groups" ON "groups"."id" = "encounters"."group_id" INNER JOIN "insurances" ON "insurances"."id" = "encounters"."insurance_id" WHERE (encounters.updated_at >= '2017-08-01' OR
           facilities.updated_at >= '2017-08-01' OR
           physicians.updated_at >= '2017-08-01' OR
           encounter_types.updated_at >= '2017-08-01' OR
           groups.updated_at >= '2017-08-01' OR
           insurances.updated_at >= '2017-08-01')
[active_model_serializers] Rendered ActiveModel::Serializer::CollectionSerializer with ActiveModelSerializers::Adapter::Attributes (0.12ms)
Completed 200 OK in 81ms (Views: 31.4ms | ActiveRecord: 32.2ms)

1 个答案:

答案 0 :(得分:0)

您的问题

ActiveRecord joins生成INNER JOIN,因此User.joins(:posts)仅返回至少 1关联User的{​​{1}}条记录。

我很确定如果你做一个简单的Post,它将返回一个空列表。

<强>解决方案

使用Encounter.joins(:facility, :physician, :encounter_type, :group, :insurance)includes结合使用,如果没有关联记录,它将不会忽略Encounter记录:

references

在你的范围内:

Encounter.includes(:facility, :physician, :encounter_type, :group, :insurance).references(:facility, :physician, :encounter_type, :group, :insurance).where([...])

改进建议

scope :by_updated_since, -> updated_at {
        includes(:facility, :physician, :encounter_type, :group, :insurance)
        .references(:facility, :physician, :encounter_type, :group, :insurance)
        .where("encounters.updated_at >= ? OR
               facilities.updated_at >= ? OR
               physicians.updated_at >= ? OR
               encounter_types.updated_at >= ? OR
               groups.updated_at >= ? OR
               insurances.updated_at >= ?",
               updated_at, updated_at, updated_at, updated_at, updated_at, updated_at)}

但我担心scope :by_updated_since, ->(updated_at) { relations = %i(facility physician encounter_type group insurance) sql_cond = relations.map(&:pluralize).map { |table_name| "#{table_name}.updated_at >= :datetime" }.join(' OR ') includes(*relations) .references(*relations) .where(sql_cond, datetime: updated_at) } 需要table_name而不是关系名称......