名前空間
名前空間は、プロジェクトと関連リソースのコンテナです。Namespace
は、そのサブクラスであるGroup
、ProjectNamespace
、UserNamespace
を通じてインスタンス化されます。
User
はUserNamespace
を 1 つ持ち、多数のNamespaces
のメンバになることができます。
Group
Groups
は、1つのProject
を親とする多くのProjectNamespaces
を持ちます。
名前空間のクエリ
名前空間階層をクエリするためのメソッドが用意されています。メソッドは標準的なRailsActiveRecord::Relation
オブジェクトを生成します。このメソッドは2つに分けることができます。一方のメソッドはNamespaceオブジェクトに対してオペレーションを行い、もう一方のメソッドはComposer Namespaceスコープとしてオペレーションを行います。
その性質上、オブジェクトメソッドは単一のNamespace
階層内でオペレーションを行いますが、スコープは階層をまたぐことができます。
以下は、Namespace
階層をクエリするメソッドの非網羅的なリストです。
ルート名前空間
ルートは、階層の最上位Namespace
です。ルートにはnil
parent_id
があります。
Namespace.where(...).roots
namespace_object.root_ancestor
子孫名前空間
名前空間の子孫は、その子、そのまた子などです。
自分自身と子孫を返すには、self_and_descendants
を使用します。
Namespace.where(...).self_and_descendants
namespace_object.self_and_descendants
自分自身を除いた子孫だけを返すことができます:
Namespace.where(...).self_and_descendants(include_self: false)
namespace_object.descendants
同じ名前のObject
メソッドをオーバーライドすることになるので、スコープメソッドに.descendants
という名前をつけることはできません。
レコード全体を返すのではなく、子孫 ID を返す方が効率的です:
Namespace.where(...).self_and_descendant_ids
Namespace.where(...).self_and_descendant_ids(include_self: false)
namespace_object.self_and_descendant_ids
namespace_object.descendant_ids
先祖ネームスペース
名前空間の祖先は、その子、その子、およびそのまた子です。
self_and_ancestors
を使用して、自分自身と祖先を返すことができます。
Namespace.where(...).self_and_ancestors
namespace_object.self_and_ancestors
私たちは、私たち自身を除いた先祖だけを返すことができます:
Namespace.where(...).self_and_ancestors(include_self: false)
namespace_object.ancestors
同じ名前のModule
メソッドをオーバーライドすることになるので、スコープメソッドに.ancestors
という名前をつけることはできません。
レコード全体を返すのではなく、祖先の ID を返す方が効率的です:
Namespace.where(...).self_and_ancstor_ids
Namespace.where(...).self_and_ancestor_ids(include_self: false)
namespace_object.self_and_ancestor_ids
namespace_object.ancestor_ids
階層
名前空間の階層とは、Namespace
、その祖先、および子孫のことです。
ネームスペース階層をクエリできます:
Namespace.where(...).self_and_hierarchy
namespace_object.self_and_hierarchy
再帰クエリ
上記のクエリは、再帰的な CTE クエリではなく、namespaces.traversal_ids
列を使用して標準的な SQL クエリを実行するため、リニア・クエリとして知られています。
必要に応じて、レガシーな再帰クエリのセットにもアクセスできます:
Namespace.where(...).recursive_self_and_descendants
Namespace.where(...).recursive_self_and_descendants(include_self: false)
Namespace.where(...).recursive_self_and_descendant_ids
Namespace.where(...).recursive_self_and_descendant_ids(include_self: false)
Namespace.where(...).recursive_self_and_ancestors
Namespace.where(...).recursive_self_and_ancestors(include_self: false)
Namespace.where(...).recursive_self_and_ancstor_ids
Namespace.where(...).recursive_self_and_ancestor_ids(include_self: false)
Namespace.where(...).recursive_self_and_hierarchy
namespace_object.recursive_root_ancestor
namespace_object.recursive_self_and_descendants
namespace_object.recursive_descendants
namespace_object.recursive_self_and_descendant_ids
namespace_object.recursive_descendant_ids
namespace_object.recursive_self_and_ancestors
namespace_object.recursive_ancestors
namespace_object.recursive_self_and_ancestor_ids
namespace_object.recursive_ancestor_ids
namespace_object.recursive_self_and_hierarchy
名前空間クエリの実装
リニアクエリは、namespaces.traversal_ids
の配列カラムを使用して実行されます。Namespace
各配列は、Namespace
ルートから現在の . Namespace
シナリオ
Namespace
A.A.B
のtraversal_ids
は[A, A.A, A.A.B]
になります。
traversal_ids
には、この領域で作業する場合に覚えておくと便利な特性がいくつかあります:
- 全ての
Namespace
のルートはtraversal_ids[1]
によって提供されます。PostgreSQLの配列インデックスは1
から始まることに注意してください。 - 現在の
Namespace
の ID はtraversal_ids[array_length(traversal_ids, 1)]
で提供されます。 -
Namespace
先祖はtraversal_ids
で表されます。 - Aは
Namespace
traversal_ids
その子孫のサブセットtraversal_ids
Namespace
です。traversal_ids = [1,2,3]
を持つNamespace
ANamespace
は、すべて[1,2,3,...]
で始まる子孫を持ちます。 - PostgreSQLの配列は、
[1] < [1,1] < [2]
.
これらのプロパティを使用すると、root
とancestors
は既にtraversal_ids
によって提供されていることがわかります。
オブジェクトの子孫クエリでは、配列が@>
別の配列の中に含まれることをテストする配列演算 @>
子を使用します。@>
この @>
演算子は、検索空間が大きくなるにつれてかなり遅くなることが分かっています。もう一つの方法は、検索空間が大きくなりがちなスコープクエリに使われます。スコープクエリでは、比較演算子と配列順序プロパティを組み合わせます。
traversal_ids = [1,2,3]
を持つNamespace
のすべての子孫は、[1,2,3]
より大きく、[1,2,4]
より小さいtraversal_ids
を持ちます。 この例では、[1,2,3]
and[1,2,4]
は兄弟であり、and [1,2,4]
は .の次の兄弟 [1,2,3]
です。next_traversal_ids_sibling
というtraversal_ids
の次の兄弟を見つける SQL 関数が提供されています。
gitlabhq_development=# select next_traversal_ids_sibling(ARRAY[1,2,3]);
next_traversal_ids_sibling
----------------------------
{1,2,4}
(1 row)
次に、比較演算子を使用して、子孫の線形クエリスコープを構築します:
WHERE namespaces.traversal_ids > ARRAY[1,2,3]
AND namespaces.traversal_ids < next_traversal_ids_sibling(ARRAY[1,2,3])
スーパーセット
Namespace
クエリは重複した結果を返しがちです。例えば、A
とA.A
の子孫を検索するクエリを考えてみましょう:
namespaces = Namespace.where(name: ['A', 'A.A'])
namespaces.self_and_descendants
=> A.A, A.A.A, A.A.B, A.B, A.B.A, A.B.B
の子孫でA
あるA.A
ため A.A
A
、両方の子孫を検索することはA
A.A
不要 A.A
A
です。 極端な場合、これはパフォーマンス低下につながる過剰なI/Oを作成する可能性があります。
冗長なNamespaces
は、traversal_ids
属性内のNamespace
ID
が、Namespace
Namespaces
クエリ Namespaces
Namespace
対象のNamespace
集合内の Namespace
別のものに属するID
と一致する場合、クエリから除外されます。Namespace
Namespaces
この条件のマッチは、クエリ対象の集合に祖先が存在することを意味 Namespaces
し、 Namespace
したがってNamespace
現在のものは Namespace
冗長です。この最適化により、そうでなければ非常に時間がかかるエッジケースのパフォーマンスが大幅に向上します。