2017-08-10 120 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需要一個表名,而不是一個關係名稱...