2017-08-10 73 views
1

我想创建一个范围方法,只查询最近更新的记录(或其相关模型最近已更新)。我相信生成的SQL语句看起来不错,但是当应该有一些时,我没有看到任何结果。谢谢你的帮助!Rails 5 - 使用范围方法和关联的模型查询

控制器

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

这是一个很大的加入你在做!也许你应该在'belongs_to'关系上考虑'touch:true'(https://apidock.com/rails/ActiveRecord/Persistence/touch) – MrYoshiji

+0

另外,这些连接关系是强制性的吗?例如,如果一个'Encounter'记录没有(存在)相关的'Insurance',那么'INNER JOIN'将从列表中移除'Encounter'记录。尝试使用'includes'而不是'joins' – MrYoshiji

+0

我以为它们是强制性的,所以如果最近更新的相关模型(而不是Encounter记录本身),Encounter记录仍然显示在结果中。 –

回答

0

您的问题

ActiveRecord的joins做出INNER JOIN所以User.joins(:posts)只返回User记录有至少 1相关Post

我很确定,如果你做一个简单的Encounter.joins(:facility, :physician, :encounter_type, :group, :insurance)它会返回一个空的列表。

解决方案

使用includesreferences相结合,所以它不会忽略遇到的记录,如果他们有没有相关的记录:

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) } 

但我担心.references需要一个表名,而不是一个关系名称...