名前空間
名前空間は、プロジェクトと関連リソースのコンテナです。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は
Namespacetraversal_idsその子孫のサブセットtraversal_idsNamespaceです。traversal_ids = [1,2,3]を持つNamespaceANamespaceは、すべて[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冗長です。この最適化により、そうでなければ非常に時間がかかるエッジケースのパフォーマンスが大幅に向上します。