diff options
Diffstat (limited to 'app/models/concerns/relative_positioning.rb')
-rw-r--r-- | app/models/concerns/relative_positioning.rb | 43 |
1 files changed, 19 insertions, 24 deletions
diff --git a/app/models/concerns/relative_positioning.rb b/app/models/concerns/relative_positioning.rb index 3cbc174536c..7f559f0a7ed 100644 --- a/app/models/concerns/relative_positioning.rb +++ b/app/models/concerns/relative_positioning.rb @@ -102,33 +102,16 @@ module RelativePositioning delta = at_end ? gap : -gap indexed = (at_end ? objects : objects.reverse).each_with_index - # Some classes are polymorphic, and not all siblings are in the same table. - by_model = indexed.group_by { |pair| pair.first.class } lower_bound, upper_bound = at_end ? [position, MAX_POSITION] : [MIN_POSITION, position] - by_model.each do |model, pairs| - model.transaction do - pairs.each_slice(100) do |batch| - # These are known to be integers, one from the DB, and the other - # calculated by us, and thus safe to interpolate - values = batch.map do |obj, i| - desired_pos = position + delta * (i + 1) - pos = desired_pos.clamp(lower_bound, upper_bound) - obj.relative_position = pos - "(#{obj.id}, #{pos})" - end.join(', ') - - model.connection.exec_query(<<~SQL, "UPDATE #{model.table_name} positions") - WITH cte(cte_id, new_pos) AS ( - SELECT * - FROM (VALUES #{values}) as t (id, pos) - ) - UPDATE #{model.table_name} - SET relative_position = cte.new_pos - FROM cte - WHERE cte_id = id - SQL + representative.model_class.transaction do + indexed.each_slice(100) do |batch| + mapping = batch.to_h.transform_values! do |i| + desired_pos = position + delta * (i + 1) + { relative_position: desired_pos.clamp(lower_bound, upper_bound) } end + + ::Gitlab::Database::BulkUpdate.execute([:relative_position], mapping, &:model_class) end end @@ -200,4 +183,16 @@ module RelativePositioning # Override if you want to be notified of failures to move def could_not_move(exception) end + + # Override if the implementing class is not a simple application record, for + # example if the record is loaded from a union. + def reset_relative_position + reset.relative_position + end + + # Override if the model class needs a more complicated computation (e.g. the + # object is a member of a union). + def model_class + self.class + end end |