Azure Cosmos DB for NoSQL での自己結合

適用対象: NoSQL

Azure Cosmos DB for NoSQL では、データはスキーマ フリーであり、通常は非正規化されます。 結合は、リレーショナル データベースのようにエンティティ間やセット間でデータを結合するのではなく、単一の項目内で行なわれます。 具体的には、結合はその項目に範囲が設定され、複数の項目間やコンテナー間では実行できません。

ヒント

項目間やコンテナー間で結合する必要性が発生した場合、これを回避するためにデータ モデルを修正することを検討してください。

1 つの項目での自己結合

項目内の自己結合の例を見てみましょう。 1 つの項目があるコンテナーを考えてみます。 この項目は、さまざまなタグを持つ製品を表します。

[
  {
    "id": "863e778d-21c9-4e2a-a984-d31f947c665c",
    "categoryId": "e592b992-d453-42ee-a74e-0de2cc97db42",
    "name": "Teapo Surfboard (6'10\") Grape",
    "sku": "teapo-surfboard-72109",
    "tags": [
      {
        "id": "556dc4f5-1dbd-41dc-9674-fda626e5d15c",
        "slug": "tail-shape-swallow",
        "name": "Tail Shape: Swallow"
      },
      {
        "id": "ac097b9a-8a30-4fd1-8cb6-69d3388ee8a2",
        "slug": "length-inches-82",
        "name": "Length: 82 inches"
      },
      {
        "id": "ce62b524-8e96-4999-b3e1-61ae7a672e2e",
        "slug": "color-group-purple",
        "name": "Color Group: Purple"
      }
    ]
  }
]

この製品の color group (色グループ) を見つける必要がある場合、どのようにしますか? 通常、tags 配列で使用される可能性のあるすべてのインデックスをチェックして、プレフィックスが color-group- である値を見つけるフィルターを含むクエリを記述する必要があります。

SELECT
  * 
FROM
  products p
WHERE
  STARTSWITH(p.tags[0].slug, "color-group-") OR
  STARTSWITH(p.tags[1].slug, "color-group-") OR
  STARTSWITH(p.tags[2].slug, "color-group-")

この方法は、すぐに使用不可になる可能性があります。 クエリ構文がより複雑になるか、より長くなると、配列で使用される可能性のある項目の数が増えます。 また、このクエリには、タグが 3 つ以上になる可能性がある将来の製品を処理できるだけの十分な柔軟性がありません。

従来のリレーショナル データベースでは、タグは別のテーブルに分割され、テーブル間結合が実行され、結果にフィルターが適用されます。 NoSQL 用 API では、JOIN キーワードを使用して項目内で自己結合操作を実行できます。

SELECT
  p.id,
  p.sku,
  t.slug
FROM
  products p
JOIN
  t IN p.tags

このクエリでは、tags 配列内の値ごとに 1 つの項目がある簡単な配列が返されます。

[
  {
    "id": "863e778d-21c9-4e2a-a984-d31f947c665c",
    "sku": "teapo-surfboard-72109",
    "slug": "tail-shape-swallow"
  },
  {
    "id": "863e778d-21c9-4e2a-a984-d31f947c665c",
    "sku": "teapo-surfboard-72109",
    "slug": "length-inches-82"
  },
  {
    "id": "863e778d-21c9-4e2a-a984-d31f947c665c",
    "sku": "teapo-surfboard-72109",
    "slug": "color-group-purple"
  }
]

このクエリを詳しく見てみましょう。 このクエリには 2 つのエイリアスがあります。p は結果セット内の各製品項目を示し、t は自己結合された tags 配列を示します。 * キーワードは、入力セットを推論できる場合に、すべてのフィールドを射影するためにのみ有効ですが、ここでは 2 つの入力セット (pt) があります。 この制約のため、タグからの slug と共に、製品から返されるフィールドを id および sku として明示的に定義する必要があります。 このクエリを読みやすく理解しやすくするために、id フィールドを削除し、タグの name フィールドのエイリアスを使用して、その名前を tag に変更できます。

SELECT
  p.sku,
  t.name AS tag
FROM
  products p
JOIN
  t IN p.tags
[
  {
    "sku": "teapo-surfboard-72109",
    "tag": "Tail Shape: Swallow"
  },
  {
    "sku": "teapo-surfboard-72109",
    "tag": "Length: 82 inches"
  },
  {
    "sku": "teapo-surfboard-72109",
    "tag": "Color Group: Purple"
  }
]

最後に、フィルターを使用してタグ color-group-purple を見つけることができます。 JOIN キーワードを使用したため、フィルターには、可変数のタグを処理できる十分な柔軟性があります。

SELECT
  p.sku,
  t.name AS tag
FROM
  products p
JOIN
  t IN p.tags
WHERE
  STARTSWITH(t.slug, "color-group-")
[
  {
    "sku": "teapo-surfboard-72109",
    "tag": "Color Group: Purple"
  }
]

複数の項目の自己結合

次のサンプルに進みましょう。このサンプルでは、複数の項目に存在する配列内の値を見つける必要があります。 この例では、2 つの製品項目があるコンテナーを考えてみます。 各項目には、その項目に関連するタグが含まれます。

[
  {
    "id": "80d62f31-9892-48e5-9b9b-5714d551b8b3",
    "categoryId": "19cd9b93-bdc5-4082-97fe-2c80c2fd77dd",
    "categoryName": "Sleeping Bags",
    "name": "Maresse Sleeping Bag (6') Ming",
    "sku": "maresse-sleeping-bag-65503",
    "tags": [
      {
        "id": "f50f3ee1-e150-4821-922b-ebe6ad82f313",
        "slug": "bag-shape-mummy",
        "name": "Bag Shape: Mummy"
      },
      {
        "id": "8564fb66-63ea-464a-872a-7598433b9479",
        "slug": "bag-insulation-down-fill",
        "name": "Bag Insulation: Down Fill"
      }
    ]
  },
  {
    "id": "6e9f51c1-6b45-440f-af5a-2abc96cd083d",
    "categoryId": "19cd9b93-bdc5-4082-97fe-2c80c2fd77dd",
    "categoryName": "Sleeping Bags",
    "name": "Vareno Sleeping Bag (6') Turmeric",
    "sku": "vareno-sleeping-bag-65508",
    "tags": [
      {
        "id": "e02502ce-367e-4fb4-940e-93d994fa6062",
        "slug": "bag-insulation-synthetic-fill",
        "name": "Bag Insulation: Synthetic Fill"
      },
      {
        "id": "c0844995-3db9-4dbb-8d9d-d2c2a6151b94",
        "slug": "color-group-yellow",
        "name": "Color Group: Yellow"
      },
      {
        "id": "f50f3ee1-e150-4821-922b-ebe6ad82f313",
        "slug": "bag-shape-mummy",
        "name": "Bag Shape: Mummy"
      }
    ]
  }
]

mummy (マミー) バッグ形状のすべての項目を見つける必要がある場合、どのようにしますか? タグ bag-shape-mummy を検索することもできますが、これらの項目の 2 つの特性を考慮した複雑なクエリを記述する必要があります。

  • プレフィックスが bag-shape- のタグは、各配列の異なるインデックスで使用されています。 Vareno スリーピング バッグでは、このタグは 3 番目の項目です (インデックス: 2)。 Maresse スリーピング バッグでは、このタグは 1 番目の項目です (インデックス: 0)。

  • 各項目の tags 配列は、さまざまな長さです。 Vareno スリーピング バッグには 2 つのタグがあり、一方 Maresse スリーピング バッグには 3 つのタグがあります。

ここで、JOIN キーワードは、項目とタグのクロス積を作成するための優れたツールです。 結合により、結合に参加するセットの完全なクロス積が作成されます。 結果は、項目のすべての順列とターゲット配列内の値を含む一連のタプルです。

サンプルのスリーピング バッグ製品とタグに対する結合操作では、次の項目が作成されます。

Item タグ
Maresse スリーピング バッグ (6') Ming Bag Shape (バッグの形状): Mummy (マミー)
Maresse スリーピング バッグ (6') Ming Bag Insulation (バッグの断熱性): Down Fill (ダウン フィル)
Vareno スリーピング バッグ (6') Turmeric Bag Insulation (バッグの断熱性): Synthetic Fill (合成フィル)
Vareno スリーピング バッグ (6') Turmeric Color Group (色グループ): Yellow (黄色)
Vareno スリーピング バッグ (6') Turmeric Bag Shape (バッグの形状): Mummy (マミー)

コンテナーに複数の項目を含める結合の SQL クエリと JSON 結果セットを次に示します。

SELECT
  p.sku,
  t.name AS tag
FROM
  products p
JOIN
  t IN p.tags
WHERE
  p.categoryName = "Sleeping Bags"
[
  {
    "sku": "maresse-sleeping-bag-65503",
    "tag": "Bag Shape: Mummy"
  },
  {
    "sku": "maresse-sleeping-bag-65503",
    "tag": "Bag Insulation: Down Fill"
  },
  {
    "sku": "vareno-sleeping-bag-65508",
    "tag": "Bag Insulation: Synthetic Fill"
  },
  {
    "sku": "vareno-sleeping-bag-65508",
    "tag": "Color Group: Yellow"
  },
  {
    "sku": "vareno-sleeping-bag-65508",
    "tag": "Bag Shape: Mummy"
  }
]

単一の項目と同様に、特定のタグに一致する項目のみを見つけるフィルターをここでは適用できます。 たとえば、このクエリは、このセクションで前述した最初の要件を満たすために、bag-shape-mummy という名前のタグを持つすべての項目を見つけます。

SELECT
  p.sku,
  t.name AS tag
FROM
  products p
JOIN
  t IN p.tags
WHERE
  p.categoryName = "Sleeping Bags" AND
  t.slug = "bag-shape-mummy"
[
  {
    "sku": "maresse-sleeping-bag-65503",
    "tag": "Bag Shape: Mummy"
  },
  {
    "sku": "vareno-sleeping-bag-65508",
    "tag": "Bag Shape: Mummy"
  }
]

フィルターを変更して、別の結果セットを取得することもできます。 たとえば、このクエリは、bag-insulation-synthetic-fill という名前のタグを持つすべての項目を見つけます。

SELECT
  p.sku,
  t.name AS tag
FROM
  products p
JOIN
  t IN p.tags
WHERE
  p.categoryName = "Sleeping Bags" AND
  t.slug = "bag-insulation-synthetic-fill"
[
  {
    "sku": "vareno-sleeping-bag-65508",
    "tag": "Bag Insulation: Synthetic Fill"
  }
]