Self-joins in Azure Cosmos DB for NoSQL

VAN TOEPASSING OP: NoSQL

In Azure Cosmos DB voor NoSQL zijn gegevens schemaloos en doorgaans gedenormaliseerd. In plaats van gegevens over entiteiten en sets samen te voegen, zoals in een relationele database, vinden joins plaats binnen één item. Joins zijn specifiek gericht op dat item en kunnen niet worden uitgevoerd voor meerdere items en containers.

Tip

Als u merkt dat u meerdere items en containers moet samenvoegen, kunt u overwegen om uw gegevensmodel opnieuw te bewerken om dit te voorkomen.

Self-join met één item

Laten we eens kijken naar een voorbeeld van een self-join binnen een item. Overweeg een container met één item. Dit item vertegenwoordigt een product met verschillende tags:

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

Wat gebeurt er als u de kleurgroep van dit product wilt vinden? Normaal gesproken moet u een query schrijven met een filter dat elke mogelijke index in de tags matrix controleert op een waarde met het voorvoegsel 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-")

Deze techniek kan snel onhoudbaar worden. De complexiteit of lengte van de querysyntaxis verhoogt het aantal potentiële items in de matrix. Deze query is ook niet flexibel genoeg om toekomstige producten te verwerken, die mogelijk meer dan drie tags hebben.

In een traditionele relationele database worden de tags gescheiden in een afzonderlijke tabel en wordt een cross-table join uitgevoerd met een filter dat is toegepast op de resultaten. In de API voor NoSQL kunnen we een self-join-bewerking uitvoeren binnen het item met behulp van het JOIN trefwoord.

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

Deze query retourneert een eenvoudige matrix met een item voor elke waarde in de tagsmatrix.

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

Laten we de query eens opsplitsen. De query heeft nu twee aliassen: p voor elk productitem in de resultatenset en t voor de zelf-gekoppelde tags matrix. Het * trefwoord is alleen geldig voor het project van alle velden als het de invoerset kan afleiden, maar nu zijn er twee invoersets (p en t). Vanwege deze beperking moeten we de geretourneerde velden expliciet definiëren als id en sku van het product, samen met slug van de tags. Om deze query beter leesbaar en begrijpelijker te maken, kunnen we het id veld verwijderen en een alias voor het veld van name de tag gebruiken om de naam ervan te wijzigen in 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"
  }
]

Ten slotte kunnen we een filter gebruiken om de tag color-group-purplete vinden. Omdat we het JOIN trefwoord hebben gebruikt, is ons filter flexibel genoeg om elk variabel aantal tags te verwerken.

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

Meerdere items automatisch samenvoegen

We gaan verder met een voorbeeld waarin we een waarde moeten vinden in een matrix die bestaat in meerdere items. In dit voorbeeld kunt u een container met twee productitems gebruiken. Elk item bevat relevante tags voor dat item.

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

Wat als je elk item met de vorm van een mummietas moet vinden? U kunt zoeken naar de tag bag-shape-mummy, maar u moet een complexe query schrijven die rekening houdt met twee kenmerken van deze items:

  • De tag met een bag-shape- voorvoegsel vindt plaats in verschillende indexen in elke matrix. Voor de Vareno-slaapzak is het label het derde item (index: 2). Voor de Maresse-slaapzak is het label het eerste item (index: 0).

  • De tags matrix voor elk item heeft een andere lengte. De Vareno slaapzak heeft twee labels en de Maresse slaapzak heeft er drie.

Hier is het JOIN trefwoord een uitstekend hulpmiddel om een cross-product van de items en tags te maken. Joins maken een volledig cross-product van de sets die deelnemen aan de join. Het resultaat is een set tuples met elke permutatie van het item en de waarden binnen de doelmatrix.

Een join-bewerking op onze voorbeeldproducten en tags voor slaapzakken zorgt voor de volgende items:

Item Tag
Maresse Slaapzak (6') Ming Vorm van zak: Mummy
Maresse Slaapzak (6') Ming Zakisolatie: Donsopvulling
Vareno Slaapzak (6') Kurkuma Isolatie van zak: synthetische opvulling
Vareno Slaapzak (6') Kurkuma Kleurgroep: Geel
Vareno Slaapzak (6') Kurkuma Vorm van zak: Mummy

Hier ziet u de SQL-query- en JSON-resultatenset voor een join die meerdere items in de container bevat.

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

Net als bij het ene item kunt u hier een filter toepassen om alleen items te vinden die overeenkomen met een specifieke tag. Met deze query worden bijvoorbeeld alle items met een tag met de naam bag-shape-mummy gevonden om te voldoen aan de initiële vereiste die eerder in deze sectie is vermeld.

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

U kunt het filter ook wijzigen om een andere resultatenset op te halen. Met deze query vindt u bijvoorbeeld alle items met een tag met de naam 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"
  }
]