Commit 0a2296f4 authored by Fabio Pitino's avatar Fabio Pitino

Merge branch '346700-ancestor-group-scopes-with-limits' into 'master'

Resolve "Ancestor group scopes with limits"

See merge request gitlab-org/gitlab!76293
parents 21e16c9b 06a9974e
...@@ -25,15 +25,20 @@ module Namespaces ...@@ -25,15 +25,20 @@ module Namespaces
def self_and_ancestors(include_self: true, hierarchy_order: nil) def self_and_ancestors(include_self: true, hierarchy_order: nil)
return super unless use_traversal_ids_for_ancestor_scopes? return super unless use_traversal_ids_for_ancestor_scopes?
ancestors_cte, base_cte = ancestor_ctes
namespaces = Arel::Table.new(:namespaces)
records = unscoped records = unscoped
.where(id: select('unnest(traversal_ids)')) .with(base_cte.to_arel, ancestors_cte.to_arel)
.distinct
.from([ancestors_cte.table, namespaces])
.where(namespaces[:id].eq(ancestors_cte.table[:ancestor_id]))
.order_by_depth(hierarchy_order) .order_by_depth(hierarchy_order)
.normal_select
if include_self if include_self
records records
else else
records.where.not(id: all.as_ids) records.where(ancestors_cte.table[:base_id].not_eq(ancestors_cte.table[:ancestor_id]))
end end
end end
...@@ -150,6 +155,20 @@ module Namespaces ...@@ -150,6 +155,20 @@ module Namespaces
records.where('namespaces.id <> base.id') records.where('namespaces.id <> base.id')
end end
end end
def ancestor_ctes
base_scope = all.select('namespaces.id', 'namespaces.traversal_ids')
base_cte = Gitlab::SQL::CTE.new(:base_ancestors_cte, base_scope)
# We have to alias id with 'AS' to avoid ambiguous column references by calling methods.
ancestors_scope = unscoped
.unscope(where: [:type])
.select('id as base_id', 'unnest(traversal_ids) as ancestor_id')
.from(base_cte.table)
ancestors_cte = Gitlab::SQL::CTE.new(:ancestors_cte, ancestors_scope)
[ancestors_cte, base_cte]
end
end end
end end
end end
......
...@@ -21,6 +21,9 @@ RSpec.describe Gitlab::GroupPlansPreloader, :saas do ...@@ -21,6 +21,9 @@ RSpec.describe Gitlab::GroupPlansPreloader, :saas do
shared_examples 'preloading cases' do shared_examples 'preloading cases' do
it 'only executes three SQL queries to preload the data' do it 'only executes three SQL queries to preload the data' do
# Pre-cache `select VERSION()` query to avoid counting.
Gitlab::Database::AsWithMaterialized.materialized_supported?
amount = ActiveRecord::QueryRecorder amount = ActiveRecord::QueryRecorder
.new { preloaded_groups } .new { preloaded_groups }
.count .count
......
...@@ -124,6 +124,12 @@ RSpec.shared_examples 'namespace traversal scopes' do ...@@ -124,6 +124,12 @@ RSpec.shared_examples 'namespace traversal scopes' do
it { expect(subject[0, 2]).to contain_exactly(group_1, group_2) } it { expect(subject[0, 2]).to contain_exactly(group_1, group_2) }
it { expect(subject[2, 2]).to contain_exactly(nested_group_1, nested_group_2) } it { expect(subject[2, 2]).to contain_exactly(nested_group_1, nested_group_2) }
end end
context 'with offset and limit' do
subject { described_class.where(id: [deep_nested_group_1, deep_nested_group_2]).offset(1).limit(1).self_and_ancestors }
it { is_expected.to contain_exactly(group_2, nested_group_2, deep_nested_group_2) }
end
end end
describe '.self_and_ancestors' do describe '.self_and_ancestors' do
...@@ -168,6 +174,19 @@ RSpec.shared_examples 'namespace traversal scopes' do ...@@ -168,6 +174,19 @@ RSpec.shared_examples 'namespace traversal scopes' do
it { is_expected.to contain_exactly(group_1.id, group_2.id) } it { is_expected.to contain_exactly(group_1.id, group_2.id) }
end end
context 'with offset and limit' do
subject do
described_class
.where(id: [deep_nested_group_1, deep_nested_group_2])
.limit(1)
.offset(1)
.self_and_ancestor_ids
.pluck(:id)
end
it { is_expected.to contain_exactly(group_2.id, nested_group_2.id, deep_nested_group_2.id) }
end
end end
describe '.self_and_ancestor_ids' do describe '.self_and_ancestor_ids' do
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment