名前空間

名前空間は、プロジェクトと関連リソースのコンテナです。Namespace は、そのサブクラスであるGroupProjectNamespaceUserNamespace を通じてインスタンス化されます。

graph TD Namespace -.- Group Namespace -.- ProjectNamespace Namespace -.- UserNamespace

UserUserNamespace を 1 つ持ち、多数のNamespaces のメンバになることができます。

graph TD Namespace -.- Group Namespace -.- ProjectNamespace Namespace -.- UserNamespace User -- has one --- UserNamespace Namespace --- Member --- User

Group Groups は、1つのProject を親とする多くのProjectNamespaces を持ちます。

graph TD Group -- has many --- ProjectNamespace -- has one --- Project Group -- has many --- Group

名前空間のクエリ

名前空間階層をクエリするためのメソッドが用意されています。メソッドは標準的なRailsActiveRecord::Relation オブジェクトを生成します。このメソッドは2つに分けることができます。一方のメソッドはNamespaceオブジェクトに対してオペレーションを行い、もう一方のメソッドはComposer Namespaceスコープとしてオペレーションを行います。

その性質上、オブジェクトメソッドは単一のNamespace 階層内でオペレーションを行いますが、スコープは階層をまたぐことができます。

以下は、Namespace 階層をクエリするメソッドの非網羅的なリストです。

ルート名前空間

ルートは、階層の最上位Namespace です。ルートにはnil parent_id があります。

graph TD classDef active fill:#f00,color:#fff classDef sel fill:#00f,color:#fff A --- A.A --- A.A.A A.A --- A.A.B A --- A.B --- A.B.A A.B --- A.B.B class A.A.B active class A sel
Namespace.where(...).roots

namespace_object.root_ancestor

子孫名前空間

名前空間の子孫は、その子、そのまた子などです。

graph TD classDef active fill:#f00,color:#fff classDef sel fill:#00f,color:#fff A --- A.A --- A.A.A A.A --- A.A.B A --- A.B --- A.B.A A.B --- A.B.B class A.A active class A.A.A,A.A.B sel

自分自身と子孫を返すには、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

先祖ネームスペース

名前空間の祖先は、その子、その子、およびそのまた子です。

graph TD classDef active fill:#f00,color:#fff classDef sel fill:#00f,color:#fff A --- A.A --- A.A.A A.A --- A.A.B A --- A.B --- A.B.A A.B --- A.B.B class A.A active class A sel

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 、その祖先、および子孫のことです。

graph TD classDef active fill:#f00,color:#fff classDef sel fill:#00f,color:#fff A --- A.A --- A.A.A A.A --- A.A.B A --- A.B --- A.B.A A.B --- A.B.B class A.A active class A,A.A.A,A.A.B sel

ネームスペース階層をクエリできます:

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

シナリオ

graph TD classDef active fill:#f00,color:#fff classDef sel fill:#00f,color:#fff A --- A.A --- A.A.A A.A --- A.A.B A --- A.B --- A.B.A A.B --- A.B.B class A.A.B active

Namespace A.A.Btraversal_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 A Namespaceは、すべて[1,2,3,...] で始まる子孫を持ちます。
  • PostgreSQLの配列は、[1] < [1,1] < [2].

これらのプロパティを使用すると、rootancestors は既に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 クエリは重複した結果を返しがちです。例えば、AA.A の子孫を検索するクエリを考えてみましょう:

graph TD classDef active fill:#f00,color:#fff classDef sel fill:#00f,color:#fff A --- A.A --- A.A.A A.A --- A.A.B A --- A.B --- A.B.A A.B --- A.B.B class A,A.A active class A.A.A,A.A.B,A.B,A.B.A,A.B.B sel
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冗長です。この最適化により、そうでなければ非常に時間がかかるエッジケースのパフォーマンスが大幅に向上します。