Selbstverknüpfungen in Azure Cosmos DB for NoSQL

GILT FÜR: NoSQL

In Azure Cosmos DB for NoSQL sind Daten schemalos und normalerweise denormalisiert. Anstatt Daten wie in einer relationalen Datenbank entitäts- und gruppenübergreifend zu verknüpfen, werden Verknüpfungen innerhalb eines einzelnen Elements durchgeführt. Insbesondere sind Verknüpfungen auf dieses Element begrenzt und können nicht übergreifend für mehrere Elemente und Container erfolgen.

Tipp

Wenn Sie feststellen, dass Sie element- und containerübergreifende Verknüpfungen benötigen, sollten Sie Ihr Datenmodell entsprechend überarbeiten, um dies zu vermeiden.

Selbstverknüpfung mit einem einzelnen Element

Nachfolgend sehen Sie ein Beispiel für eine Selbstverknüpfung innerhalb eines Elements. Stellen Sie sich einen Container mit einem einzelnen Element vor. Dieses Element stellt ein Produkt mit verschiedenen Tags dar:

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

Was ist, wenn Sie nach der Farbgruppe dieses Produkts suchen müssen? Normalerweise müssen Sie eine Abfrage schreiben, mit der durch einen Filter jeder potenzielle Index im tags-Array auf einen Wert mit dem Präfix color-group- überprüft wird.

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-")

Diese Vorgehensweise kann jedoch schnell nicht mehr praktikabel sein. Die Komplexität oder Länge der Abfragesyntax erhöht die Anzahl potenzieller Elemente im Array. Außerdem ist diese Abfrage nicht flexibel genug, um zukünftige Produkte zu verarbeiten, die mehr als drei Tags aufweisen könnten.

In einer herkömmlichen relationalen Datenbank werden die Tags in eine separate Tabelle unterteilt. Dann wird eine tabellenübergreifende Verknüpfung durchgeführt und ein Filter auf die Ergebnisse angewendet. In der API für NoSQL können Sie einen Selbstverknüpfungsvorgang innerhalb des Elements mithilfe des Schlüsselworts JOIN ausführen.

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

Diese Abfrage gibt ein einfaches Array mit einem Element für jeden Wert im Tagsarray zurück.

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

Nachfolgend wird die Abfrage aufgeschlüsselt. Die Abfrage hat nun zwei Aliase: p für jedes Produktelement im Resultset und t für das selbstverknüpfte tags-Array. Das Schlüsselwort * ist nur dann für das Projizieren aller Felder gültig, wenn es das Eingabeset ableiten kann, doch sind jetzt zwei Eingabesets vorhanden (p und t). Aufgrund dieser Einschränkung müssen wir die zurückgegebenen Felder explizit als id und sku vom Produkt zusammen mit slug von den Tags definieren. Damit diese Abfrage besser lesbar und verständlicher ist, können wir das Feld id löschen und einen Alias für das Feld name des Tags verwenden, um es in tag umzubenennen.

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

Zum Schluss können wir einen Filter zum Auffinden des Tags color-group-purple verwenden. Da wir das Schlüsselwort JOIN verwendet haben, ist unser Filter flexibel genug, um eine beliebige Anzahl von Tags zu verarbeiten.

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

Selbstverknüpfung mehrerer Elemente

Es folgt nun ein Beispiel, bei dem wir einen Wert innerhalb eines Arrays finden müssen, das in mehreren Elementen vorhanden ist. Bei diesem Beispiel gehen wir von einem Container mit zwei Produktelementen aus. Jedes Element enthält relevante Tags für dieses Element.

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

Was ist, wenn Sie jedes Element mit der Schlafsackform Mummy finden müssen? Sie können nach dem Tag bag-shape-mummy suchen, doch müssten Sie eine komplexe Abfrage schreiben, die zwei Merkmale dieser Elemente umfasst:

  • Das Tag mit dem Präfix bag-shape- kommt in verschiedenen Indizes in jedem Array vor. Beim Vareno-Schlafsack ist das Tag das dritte Element (Index: 2). Beim Maresse-Schlafsack ist das Tag das erste Element (Index: 0).

  • Das tags-Array für jedes Element weist eine andere Länge auf. Der Vareno-Schlafsack hat zwei Tags, der Maresse-Schlafsack hingegen drei Tags.

Hier bietet sich das Schlüsselwort JOIN als ein hervorragendes Tool an, um ein Kreuzprodukt der Elemente und Tags zu erstellen. Durch Verknüpfungen wird ein komplettes Kreuzprodukt der an der Verknüpfung beteiligten Sets erstellt. Das Ergebnis ist eine Reihe von Tupeln mit jeder Permutation des Elements und den Werten innerhalb des Zielarrays.

Ein Verknüpfungsvorgang für unsere Musterschlafsackprodukte und Tags erstellt die folgenden Elemente:

Element Tag
Maresse Sleeping Bag (6') Ming Bag Shape: Mummy
Maresse Sleeping Bag (6') Ming Bag Insulation: Down Fill
Vareno Sleeping Bag (6') Turmeric Bag Insulation: Synthetic Fill
Vareno Sleeping Bag (6') Turmeric Color Group: Yellow
Vareno Sleeping Bag (6') Turmeric Bag Shape: Mummy

Hier sehen Sie die SQL-Abfrage und das JSON-Resultset für eine Verknüpfung, die mehrere Elemente im Container umfasst.

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

Wie bei einem einzelnen Element können Sie auch hier einen Filter anwenden, um nur nach den Elementen zu suchen, die einem bestimmten Tag entsprechen. Diese Abfrage findet beispielsweise alle Elemente mit einem Tag namens bag-shape-mummy, um die weiter oben genannte Anforderung zu erfüllen.

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

Sie können den Filter auch ändern, um ein anderes Resultset zu erhalten. Mit dieser Abfrage werden beispielsweise alle Elemente gefunden, die ein Tag namens bag-insulation-synthetic-fill aufweisen.

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