Autocombinaciones en Azure Cosmos DB for NoSQL

SE APLICA A: NoSQL

En Azure Cosmos DB for NoSQL, los datos no tienen esquema y normalmente se desnormalizan. En lugar de combinar datos entre entidades y conjuntos, como lo haría en una base de datos relacional, las combinaciones se producen dentro de un solo elemento. En concreto, las combinaciones se limitan a ese elemento y no se pueden producir en varios elementos y contenedores.

Sugerencia

Si necesita unirse a elementos y contenedores, considere la posibilidad de volver a trabajar el modelo de datos para evitarlo.

Autocombinación con un solo elemento

Veamos un ejemplo de autocombinación dentro de un elemento. Considere un contenedor con un solo elemento. Este elemento representa un producto con varias etiquetas:

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

¿Qué ocurre si necesita encontrar el grupo de colores de este producto? Normalmente, tendría que escribir una consulta que tenga un filtro que comprueba cada índice potencial de la matriz tags para un valor con un prefijo de 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-")

Esta técnica puede volverse insostenible rápidamente. La complejidad o la longitud de la sintaxis de consulta aumenta el número de posibles elementos de la matriz. Además, esta consulta no es lo suficientemente flexible como para controlar los productos futuros, que pueden tener más de tres etiquetas.

En una base de datos relacional tradicional, las etiquetas se separarían en una tabla independiente y se realiza una combinación entre tablas con un filtro aplicado a los resultados. En la API para NoSQL, podemos realizar una operación de autocombinación dentro del elemento mediante la palabra clave JOIN.

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

Esta consulta devuelve una simple matriz con un elemento para cada valor de la matriz de etiquetas.

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

Vamos a desglosar la consulta. La consulta ahora tiene dos alias: p para cada elemento de producto del conjunto de resultados y t para la matriz de autocombinación tags. La palabra clave * solo es válida para proyectar todos los campos si puede deducir el conjunto de entrada, pero ahora hay dos conjuntos de entrada (p y t). Debido a esta restricción, debemos definir explícitamente los campos devueltos como id y sku desde el producto junto con slug desde las etiquetas. Para facilitar la lectura y comprensión de esta consulta, podemos quitar el campo id y usar un alias para el campo de la etiqueta name para cambiar su nombre a 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"
  }
]

Por último, podemos usar un filtro para buscar la etiqueta color-group-purple. Dado que hemos usado la palabra clave JOIN, nuestro filtro es lo suficientemente flexible como para administrar cualquier número variable de etiquetas.

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

Autocombinación de varios elementos

Vamos a pasar a un ejemplo en el que necesitamos encontrar un valor dentro de una matriz que existe en varios elementos. En este ejemplo, considere un contenedor con dos elementos de producto. Cada elemento contiene etiquetas relevantes para ese elemento.

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

¿Qué ocurre si necesitaras encontrar cada artículo con forma de saco de momia? Podría buscar la etiqueta bag-shape-mummy, pero tendría que escribir una consulta compleja que tenga en cuenta dos características de estos elementos:

  • La etiqueta con el prefijo bag-shape- aparece en distintos índices en las matrices. Para saco de dormir Vareno, la etiqueta es el tercer elemento (índice: 2). Para el saco de dormir Maresse, la etiqueta es el primer elemento (índice: 0).

  • Cada elemento tiene una matriz tags de una longitud diferente. El saco de dormir Vareno tiene dos etiquetas mientras que el saco de dormir Maresse tiene tres.

Aquí, la palabra clave JOIN es una excelente herramienta para crear un producto cruzado de elementos y etiquetas. Las combinaciones crean un producto cruzado completo de los conjuntos que participan en la combinación. El resultado es un conjunto de tuplas con cada permutación del elemento y los valores de la matriz de destino.

Una operación de combinación en nuestros productos y etiquetas de saco de dormir de ejemplo crea los siguientes elementos:

Elemento Etiqueta
Saco de dormir de Maresse (6') Ming Forma del saco: Momia
Saco de dormir de Maresse (6') Ming Aislamiento del saco: Relleno de plumón
Saco de dormir Vareno (6') Cúrcuma Aislamiento del saco: relleno sintético
Saco de dormir Vareno (6') Cúrcuma Grupo de colores: amarillo
Saco de dormir Vareno (6') Cúrcuma Forma del saco: Momia

Esta es la consulta SQL y el conjunto de resultados JSON para una combinación que incluye varios elementos en el contenedor.

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

Al igual que con el elemento único, puede aplicar un filtro aquí para localizar solo los elementos que coincidan con una etiqueta específica. Por ejemplo, esta consulta busca todos los elementos con una etiqueta denominada bag-shape-mummy para cumplir el requisito inicial mencionado anteriormente en esta sección.

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

También puede cambiar el filtro para obtener un conjunto de resultados diferente. Por ejemplo, esta consulta localiza todos los elementos que tienen una etiqueta denominada 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"
  }
]