Class: Atomically::QueryService

Inherits:
Object
  • Object
show all
Defined in:
lib/atomically/query_service.rb

Constant Summary collapse

DEFAULT_CONFLICT_TARGETS =
[:id].freeze

Instance Method Summary collapse

Constructor Details

#initialize(klass, relation: nil, model: nil) ⇒ QueryService

Returns a new instance of QueryService.



15
16
17
18
19
# File 'lib/atomically/query_service.rb', line 15

def initialize(klass, relation: nil, model: nil)
  @klass = klass
  @relation = relation || @klass
  @model = model
end

Instance Method Details

#create_or_plus(columns, data, update_columns, conflict_target: DEFAULT_CONFLICT_TARGETS) ⇒ Object



21
22
23
# File 'lib/atomically/query_service.rb', line 21

def create_or_plus(columns, data, update_columns, conflict_target: DEFAULT_CONFLICT_TARGETS)
  @klass.import(columns, data, on_duplicate_key_update: on_duplicate_key_plus_sql(update_columns, conflict_target))
end

#decrement_unsigned_counters(counters) ⇒ Object

Parameters

  • counters - A Hash containing the names of the fields to update as keys and the amount to update the field by as values.



70
71
72
73
74
75
76
77
# File 'lib/atomically/query_service.rb', line 70

def decrement_unsigned_counters(counters)
  result = open_update_all_scope do
    counters.each do |field, amount|
      where("#{field} >= ?", amount).update("#{field} = #{field} - ?", amount) if amount > 0
    end
  end
  return (result == 1)
end

#pay_all(hash, update_columns, primary_key: :id) ⇒ Object

{ id => pay_count }



25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# File 'lib/atomically/query_service.rb', line 25

def pay_all(hash, update_columns, primary_key: :id) # { id => pay_count }
  return 0 if hash.blank?

  update_columns = update_columns.map(&method(:quote_column))

  query = hash.inject(@klass.none) do |relation, (id, pay_count)|
    condition = @relation.where(primary_key => id)
    update_columns.each{|s| condition = condition.where("#{s} >= ?", pay_count) }
    next relation.or(condition)
  end

  raw_when_sql = hash.map{|id, pay_count| "WHEN #{sanitize(id)} THEN #{sanitize(-pay_count)}" }.join("\n")
  no_var_in_sql = true if update_columns.size == 1 or adapter_check_service.pg?
  update_sqls = update_columns.map.with_index do |column, idx|
    if no_var_in_sql
      value = "(\nCASE #{quote_column(primary_key)}\n#{raw_when_sql}\nEND)"
    else
      value = idx == 0 ? "(@change := \nCASE #{quote_column(primary_key)}\n#{raw_when_sql}\nEND)" : '@change'
    end
    next "#{column} = #{column} + #{value}"
  end

  return where_all_can_be_updated(query, hash.size).update_all(update_sqls.join(', '))
end

#update(attrs, options = {}) ⇒ Object



54
55
56
57
58
59
60
61
62
63
64
# File 'lib/atomically/query_service.rb', line 54

def update(attrs, options = {})
  from = options[:from] || :not_set
  success = update_and_return_number_of_updated_rows(attrs, from) == 1

  if success
    assign_without_changes(attrs)
    @model.send(:clear_attribute_changes, @model.changes.keys)
  end

  return success
end

#update_all(expected_size, *args) ⇒ Object



50
51
52
# File 'lib/atomically/query_service.rb', line 50

def update_all(expected_size, *args)
  where_all_can_be_updated(@relation, expected_size).update_all(*args)
end

#update_all_and_get_ids(*args) ⇒ Object



79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/atomically/query_service.rb', line 79

def update_all_and_get_ids(*args)
  if adapter_check_service.pg?
    scope = UpdateAllScope::UpdateAllScope.new(model: @model, relation: @relation.where(''))
    scope.update(*args)
    return @klass.connection.execute("#{scope.to_sql} RETURNING id", "#{@klass} Update All").map{|s| s['id'].to_i }
  end

  ids = nil
  id_column = quote_column_with_table(:id)
  @klass.transaction do
    @relation.connection.execute('SET @ids := NULL')
    @relation.where("((@ids := CONCAT_WS(',', #{id_column}, @ids)) IS NOT NULL)").update_all(*args) # 撈出有真的被更新的 id,用逗號串在一起
    ids = @klass.from(Arel.sql('DUAL')).pluck(Arel.sql('@ids')).first
  end
  return ids.try{|s| s.split(',').map(&:to_i).uniq.sort } || [] # 將 id 從字串取出來 @id 的格式範例: '1,4,12'
end