クエリ言語では、データはスキーマフリーであり、通常は非正規化されます。 リレーショナル データベースの場合と同様に、エンティティとセット間でデータを結合する代わりに、結合は 1 つの項目内で行われます。 具体的には、結合はその項目を対象としており、複数の項目とコンテナーにまたがって行うことはできません。
ヒント
アイテムとコンテナー間で結合する必要がある場合は、このアンチパターンを回避するためにデータ モデルの再作業を検討してください。
1 つの項目で自己結合する
アイテム内の自己結合の例を見てみましょう。 1 つの項目を持つコンテナーについて考えてみましょう。 この項目は、さまざまなサイズの製品を表します。
[
{
"name": "Raiot Jacket",
"sizes": [
{
"key": "s",
"description": "Small"
},
{
"key": "m",
"description": "Medium"
},
{
"key": "l",
"description": "Large"
},
{
"key": "xl",
"description": "Extra Large"
}
]
}
]
特定のサイズの製品を見つける必要がある場合はどうなりますか? 通常は、 sizes 配列内のすべての潜在的なインデックスにプレフィックスを持つ値をチェックするフィルターを含むクエリを記述する必要があります。 この例では、クエリは、 Largeで終わるサイズを持つすべての製品を検索します。
SELECT
*
FROM
products p
WHERE
p.sizes[0].description LIKE "%Large" OR
p.sizes[1].description LIKE "%Large" OR
p.sizes[2].description LIKE "%Large" OR
p.sizes[3].description LIKE "%Large"
この手法は、すぐに実行できなくなる可能性があります。 クエリ構文の複雑さまたは長さは、配列内の潜在的な項目の数を増やします。 また、このクエリは、3 つ以上のサイズを持つ可能性がある将来の製品を処理するのに十分な柔軟性がありません。
従来のリレーショナル データベースでは、サイズは別のテーブルに分割され、結果にフィルターを適用してテーブル間結合が実行されます。 クエリ言語では、 JOIN キーワードを使用して項目内で自己結合操作を実行できます。
SELECT
p.name,
s.key,
s.description
FROM
products p
JOIN
s in p.sizes
このクエリは、タグ配列内の値ごとに項目を含む単純な配列を返します。
[
{
"name": "Raiot Jacket",
"key": "s",
"description": "Small"
},
{
"name": "Raiot Jacket",
"key": "m",
"description": "Medium"
},
{
"name": "Raiot Jacket",
"key": "l",
"description": "Large"
},
{
"name": "Raiot Jacket",
"key": "xl",
"description": "Extra Large"
}
]
クエリを分割しましょう。 クエリに 2 つのエイリアスが追加されました。結果セット内の各製品項目のpと、自己結合sizes配列のs。
* キーワードは、入力セットを推論できる場合にすべてのフィールドを射写する場合にのみ有効ですが、現在は 2 つの入力セット (pとt) があります。 この制約により、返されるフィールドを製品から name として明示的に定義し、 keyと共に、サイズから description する必要があります。
最後に、フィルターを使用して、 Largeで終わるサイズを見つけることができます。
JOINキーワードを使用したため、フィルターは可変数のタグを処理するのに十分な柔軟性があります。
SELECT
p.name,
s.key AS size
FROM
products p
JOIN
s in p.sizes
WHERE
s.description LIKE "%Large"
[
{
"name": "Raiot Jacket",
"size": "l"
},
{
"name": "Raiot Jacket",
"size": "xl"
}
]
複数の項目を自己結合する
複数の項目に存在する配列内の値を見つける必要があるサンプルに進みましょう。 この例では、2 つの製品項目を含むコンテナーについて考えます。 各項目には、そのアイテムに関連する colors が含まれています。
[
{
"name": "Gremon Fins",
"colors": [
"science-blue",
"turbo"
]
},
{
"name": "Elecy Jacket",
"colors": [
"indigo-shark",
"jordy-blue-shark"
]
},
{
"name": "Tresko Pack",
"colors": [
"golden-dream"
]
}
]
名前に blue を含む色を持つすべての項目を検索する必要がある場合はどうしますか? 文字列 blueを手動で検索することもできますが、これらの項目の 2 つの特性を考慮した複雑なクエリを記述する必要があります。
blue部分文字列を持つ色は、各配列内の異なるインデックスで発生します。Elecy Jacket製品の場合、色は 2 番目の項目 (インデックス:1) です。Gremon Fins製品の場合、タグは最初の項目 (インデックス:0) です。Tresko Pack製品には、この部分文字列を含むものがありません。各項目の
colors配列の長さが異なります。Gremon Fins製品とElecy Jacket製品の両方に 2 つの色があり、Tresko Pack製品には 1 つだけがあります。
ここでは、 JOIN キーワードは、項目と色のクロス積を作成するための優れたツールです。 結合は、結合に参加しているセットの 完全な クロス積を作成します。 結果は、項目のすべての順列とターゲット配列内の値を持つタプルのセットです。
サンプル製品と色に対する結合操作では、次の項目が作成されます。
| Product | 色 |
|---|---|
Gremon Fins |
science-blue |
Gremon Fins |
turbo |
Elecy Jacket |
indigo-shark |
Elecy Jacket |
jordy-blue-shark |
Tresko Pack |
golden-dream |
次の NoSQl クエリの例では、 JOIN キーワードを使用してクロス積を作成し、すべての順列を返します。
SELECT
p.name,
c AS color
FROM
products p
JOIN
c in p.colors
[
{
"name": "Elecy Jacket",
"color": "indigo-shark"
},
{
"name": "Elecy Jacket",
"color": "jordy-blue-shark"
},
{
"name": "Gremon Fins",
"color": "science-blue"
},
{
"name": "Gremon Fins",
"color": "turbo"
},
{
"name": "Tresko Pack",
"color": "golden-dream"
}
]
1 つの項目と同様に、ここでフィルターを適用して、特定のタグに一致する項目のみを検索できます。 たとえば、このクエリは、このセクションで前述した最初の要件を満たすために、 blue を含む部分文字列を持つすべての項目を検索します。
SELECT
p.name,
c AS color
FROM
products p
JOIN
c in p.colors
WHERE
c LIKE "%blue%"
[
{
"name": "Elecy Jacket",
"color": "jordy-blue-shark"
},
{
"name": "Gremon Fins",
"color": "science-blue"
}
]
このクエリをさらに絞り込んで、フィルターを満たす製品の名前を返すことができます。 この例では色の値は投影されませんが、フィルターは引き続き期待どおりに動作します。
SELECT VALUE
p.name
FROM
products p
JOIN
c in p.colors
WHERE
c LIKE "%blue%"
[
"Elecy Jacket",
"Gremon Fins"
]