Jointures réflexives Azure Cosmos DB for NoSQL

S’APPLIQUE À : NoSQL

Dans Azure Cosmos DB for NoSQL, les données sont sans schéma et généralement dénormalisées. Au lieu de joindre des données entre des entités et des ensembles, comme vous le feriez dans une base de données relationnelle, les jointures se produisent au sein d’un seul article. Plus précisément, les jointures se délimitent à cet article et ne peuvent pas se produire sur plusieurs articles et conteneurs.

Conseil

Si vous avez besoin de joindre des articles et des conteneurs, envisagez de retravailler votre modèle de données pour éviter cette situation.

Jointure réflexive avec un seul article

Examinons un exemple de jointure réflexive au sein d’un article. Considérez un conteneur avec un seul article. Cet article représente un produit avec différentes étiquettes :

[
  {
    "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"
      }
    ]
  }
]

Que se passe-t-il si vous avez besoin de trouver le groupe de couleurs de ce produit ? En règle générale, vous avez besoin d’écrire une requête qui a un filtre qui vérifie chaque index potentiel dans le tableau tags pour trouver une valeur avec un préfixe 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-")

Cette technique peut rapidement devenir intenable. La complexité ou la longueur de la syntaxe de requête augmente le nombre potentiel d’articles dans le tableau. De plus, cette requête n’est pas suffisamment flexible pour gérer les produits futurs, qui peuvent avoir plus de trois étiquettes.

Dans une base de données relationnelle traditionnelle, les étiquettes sont séparées dans une table distincte et une jointure entre tables est effectuée avec un filtre appliqué aux résultats. Dans l’API pour NoSQL, nous pouvons effectuer une opération de jointure réflexive au sein de l’article à l’aide du mot clé JOIN.

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

Cette requête retourne un tableau simple avec un article pour chaque valeur incluse dans le tableau des étiquettes.

[
  {
    "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"
  }
]

Décomposons la requête. La requête a désormais deux alias : p pour chaque article dans le jeu de résultats et t pour le tableau tags à jointure réflexive. Le mot clé * est valide uniquement pour projeter tous les champs s’il peut déduire le jeu de données d’entrée, mais il existe maintenant deux jeux de données d’entrée (p et t). En raison de cette contrainte, nous devons définir explicitement nos champs retournés en tant que id et sku à partir du produit, ainsi que slug à partir des étiquettes. Pour faciliter la lecture et la compréhension de cette requête, nous pouvons supprimer le champ id et utiliser un alias pour le champ name de l’étiquette afin de le renommer 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"
  }
]

Enfin, nous pouvons utiliser un filtre pour rechercher l’étiquette color-group-purple. Étant donné que nous avons utilisé le mot clé JOIN, notre filtre est suffisamment flexible pour gérer n’importe quel nombre variable d’étiquettes.

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"
  }
]

Jointure réflexive de plusieurs articles

Passons à un exemple où nous devons trouver une valeur, au sein d’un tableau, qui existe dans plusieurs articles. Pour cet exemple, considérez un conteneur avec deux articles. Chaque article contient les étiquettes pertinentes qui le concernent.

[
  {
    "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"
      }
    ]
  }
]

Que se passe-t-il si vous avez besoin de trouver chaque article dont la forme est celle d’un sac à langer ? Vous pouvez rechercher l’étiquette bag-shape-mummy, mais vous avez besoin d’écrire une requête complexe qui tient compte des deux caractéristiques de ces articles :

  • L’étiquette avec un préfixe bag-shape- se produit à différents index dans chaque tableau. Pour le sac de couchage Vareno, l’étiquette correspond au troisième article (index : 2). Pour le sac de couchage Maresse, l’étiquette correspond au premier article (index : 0).

  • Le tableau tags de chaque article est d’une longueur différente. Le sac de couchage Vareno a deux étiquettes tandis que le sac de couchage Maresse en a trois.

Ici, le mot clé JOIN est un excellent outil pour créer un produit croisé des articles et étiquettes. Les jointures créent un produit croisé complet des ensembles participant à la jointure. Le résultat est un ensemble de tuples avec chaque permutation de l’article et des valeurs au sein du tableau ciblé.

Une opération de jointure sur nos exemples de produits et étiquettes de sac de couchage va créer les articles suivants :

Élément Tag
Sac de couchage Maresse (6’) Ming Forme de sac : Sac à langer
Sac de couchage Maresse (6’) Ming Isolant du sac : Garnissage en duvet
Sac de couchage Vareno (6’) Turmeric Isolant du sac : Garnissage synthétique
Sac de couchage Vareno (6’) Turmeric Groupe de couleurs : Jaune
Sac de couchage Vareno (6’) Turmeric Forme de sac : Sac à langer

Voici la requête SQL et le jeu de résultats JSON pour une jointure qui inclut plusieurs articles dans le conteneur.

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"
  }
]

Tout comme pour l’article unique, vous pouvez appliquer un filtre ici pour rechercher uniquement les articles qui correspondent à une étiquette spécifique. Par exemple, cette requête recherche tous les articles avec une étiquette nommée bag-shape-mummy pour répondre à l’exigence initiale mentionnée plus haut dans cette section.

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"
  }
]

Vous pouvez également modifier le filtre pour obtenir un autre jeu de résultats. Par exemple, cette requête recherche tous les articles qui ont une étiquette nommée 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"
  }
]