diff options
Diffstat (limited to 'app/models/concerns/relative_positioning.rb')
-rw-r--r-- | app/models/concerns/relative_positioning.rb | 46 |
1 files changed, 32 insertions, 14 deletions
diff --git a/app/models/concerns/relative_positioning.rb b/app/models/concerns/relative_positioning.rb index 045bf392ac8..46d2c345758 100644 --- a/app/models/concerns/relative_positioning.rb +++ b/app/models/concerns/relative_positioning.rb @@ -14,10 +14,12 @@ module RelativePositioning class_methods do def move_to_end(objects) - parent_ids = objects.map(&:parent_ids).flatten.uniq - max_relative_position = in_parents(parent_ids).maximum(:relative_position) || START_POSITION objects = objects.reject(&:relative_position) + return if objects.empty? + + max_relative_position = objects.first.max_relative_position + self.transaction do objects.each do |object| relative_position = position_between(max_relative_position, MAX_POSITION) @@ -55,22 +57,21 @@ module RelativePositioning end end - def min_relative_position - self.class.in_parents(parent_ids).minimum(:relative_position) + def min_relative_position(&block) + calculate_relative_position('MIN', &block) end - def max_relative_position - self.class.in_parents(parent_ids).maximum(:relative_position) + def max_relative_position(&block) + calculate_relative_position('MAX', &block) end def prev_relative_position prev_pos = nil if self.relative_position - prev_pos = self.class - .in_parents(parent_ids) - .where('relative_position < ?', self.relative_position) - .maximum(:relative_position) + prev_pos = max_relative_position do |relation| + relation.where('relative_position < ?', self.relative_position) + end end prev_pos @@ -80,10 +81,9 @@ module RelativePositioning next_pos = nil if self.relative_position - next_pos = self.class - .in_parents(parent_ids) - .where('relative_position > ?', self.relative_position) - .minimum(:relative_position) + next_pos = min_relative_position do |relation| + relation.where('relative_position > ?', self.relative_position) + end end next_pos @@ -165,4 +165,22 @@ module RelativePositioning status end # rubocop:enable Gitlab/ModuleWithInstanceVariables + + def calculate_relative_position(calculation) + # When calculating across projects, this is much more efficient than + # MAX(relative_position) without the GROUP BY, due to index usage: + # https://gitlab.com/gitlab-org/gitlab-ce/issues/54276#note_119340977 + relation = self.class + .in_parents(parent_ids) + .order(Gitlab::Database.nulls_last_order('position', 'DESC')) + .limit(1) + .group(self.class.parent_column) + + relation = yield relation if block_given? + + relation + .pluck(self.class.parent_column, "#{calculation}(relative_position) AS position") + .first&. + last + end end |