diff --git a/app/helpers/groups_helper.rb b/app/helpers/groups_helper.rb
index 77dc9e7d538dab25c78f13c502b0c934fcee6482..926c9703628158e1027666936133faa1da3469f3 100644
--- a/app/helpers/groups_helper.rb
+++ b/app/helpers/groups_helper.rb
@@ -14,7 +14,7 @@ module GroupsHelper
   def group_title(group, name = nil, url = nil)
     full_title = ''
 
-    group.parents.each do |parent|
+    group.ancestors.each do |parent|
       full_title += link_to(simple_sanitize(parent.name), group_path(parent))
       full_title += ' / '.html_safe
     end
diff --git a/app/models/group.rb b/app/models/group.rb
index 99675ddb3661ff5c9f6ca515f689d0b5d3cd263d..4cdfd022094290857a4a272bad8bc1ebdb02e172 100644
--- a/app/models/group.rb
+++ b/app/models/group.rb
@@ -201,7 +201,7 @@ class Group < Namespace
   end
 
   def members_with_parents
-    GroupMember.where(requested_at: nil, source_id: parents.map(&:id).push(id))
+    GroupMember.where(requested_at: nil, source_id: ancestors.map(&:id).push(id))
   end
 
   def users_with_parents
diff --git a/app/models/namespace.rb b/app/models/namespace.rb
index d41833de66f2cd0325092fc5f51227baf703a77f..d3a4ddbb3bfdb0b59e8f8d866013648c2e300ece 100644
--- a/app/models/namespace.rb
+++ b/app/models/namespace.rb
@@ -183,8 +183,26 @@ class Namespace < ActiveRecord::Base
       end
   end
 
-  def parents
-    @parents ||= parent ? parent.parents + [parent] : []
+  # Scopes the model on ancestors of the record
+  def ancestors
+    if parent_id
+      path = route.path
+      paths = []
+
+      until path.blank?
+        path = path.rpartition('/').first
+        paths << path
+      end
+
+      self.class.joins(:route).where('routes.path IN (?)', paths).order_id_asc
+    else
+      self.class.none
+    end
+  end
+
+  # Scopes the model on direct and indirect children of the record
+  def descendants
+    self.class.joins(:route).where('routes.path LIKE ?', "#{route.path}/%").order_id_asc
   end
 
   private
diff --git a/app/models/route.rb b/app/models/route.rb
index caf596efa79627bca90455150e15dca27be730e2..ebd18dce737521b6c0fe58f534f2fd135e498ee9 100644
--- a/app/models/route.rb
+++ b/app/models/route.rb
@@ -8,9 +8,9 @@ class Route < ActiveRecord::Base
     presence: true,
     uniqueness: { case_sensitive: false }
 
-  after_update :rename_children, if: :path_changed?
+  after_update :rename_descendants, if: :path_changed?
 
-  def rename_children
+  def rename_descendants
     # We update each row separately because MySQL does not have regexp_replace.
     # rubocop:disable Rails/FindEach
     Route.where('path LIKE ?', "#{path_was}/%").each do |route|
diff --git a/spec/models/namespace_spec.rb b/spec/models/namespace_spec.rb
index 600538ff5f435f01e6b2d9b6bbe858efeaa200eb..42e8a48abe955cf0f51bdd62a76164af3ed1275a 100644
--- a/spec/models/namespace_spec.rb
+++ b/spec/models/namespace_spec.rb
@@ -5,6 +5,8 @@ describe Namespace, models: true do
 
   it { is_expected.to have_many :projects }
   it { is_expected.to have_many :project_statistics }
+  it { is_expected.to belong_to :parent }
+  it { is_expected.to have_many :children }
 
   it { is_expected.to validate_presence_of(:name) }
   it { is_expected.to validate_uniqueness_of(:name).scoped_to(:parent_id) }
@@ -182,17 +184,31 @@ describe Namespace, models: true do
     it { expect(nested_group.full_name).to eq("#{group.name} / #{nested_group.name}") }
   end
 
-  describe '#parents' do
+  describe '#ancestors' do
     let(:group) { create(:group) }
     let(:nested_group) { create(:group, parent: group) }
     let(:deep_nested_group) { create(:group, parent: nested_group) }
     let(:very_deep_nested_group) { create(:group, parent: deep_nested_group) }
 
-    it 'returns the correct parents' do
-      expect(very_deep_nested_group.parents).to eq([group, nested_group, deep_nested_group])
-      expect(deep_nested_group.parents).to eq([group, nested_group])
-      expect(nested_group.parents).to eq([group])
-      expect(group.parents).to eq([])
+    it 'returns the correct ancestors' do
+      expect(very_deep_nested_group.ancestors).to eq([group, nested_group, deep_nested_group])
+      expect(deep_nested_group.ancestors).to eq([group, nested_group])
+      expect(nested_group.ancestors).to eq([group])
+      expect(group.ancestors).to eq([])
+    end
+  end
+
+  describe '#descendants' do
+    let!(:group) { create(:group) }
+    let!(:nested_group) { create(:group, parent: group) }
+    let!(:deep_nested_group) { create(:group, parent: nested_group) }
+    let!(:very_deep_nested_group) { create(:group, parent: deep_nested_group) }
+
+    it 'returns the correct descendants' do
+      expect(very_deep_nested_group.descendants.to_a).to eq([])
+      expect(deep_nested_group.descendants.to_a).to eq([very_deep_nested_group])
+      expect(nested_group.descendants.to_a).to eq([deep_nested_group, very_deep_nested_group])
+      expect(group.descendants.to_a).to eq([nested_group, deep_nested_group, very_deep_nested_group])
     end
   end
 end
diff --git a/spec/models/route_spec.rb b/spec/models/route_spec.rb
index 8481a9bef1632a81f6126b9de20798fb700ef043..dd2a5109abcca7c2dabd0878e6daf1a7c6dade68 100644
--- a/spec/models/route_spec.rb
+++ b/spec/models/route_spec.rb
@@ -14,7 +14,7 @@ describe Route, models: true do
     it { is_expected.to validate_uniqueness_of(:path) }
   end
 
-  describe '#rename_children' do
+  describe '#rename_descendants' do
     let!(:nested_group) { create(:group, path: "test", parent: group) }
     let!(:deep_nested_group) { create(:group, path: "foo", parent: nested_group) }
     let!(:similar_group) { create(:group, path: 'gitlab-org') }