summaryrefslogtreecommitdiff
path: root/app/models/concerns/relative_positioning.rb
diff options
context:
space:
mode:
Diffstat (limited to 'app/models/concerns/relative_positioning.rb')
-rw-r--r--app/models/concerns/relative_positioning.rb43
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