Associações autónomas no Azure Cosmos DB para NoSQL

APLICA-SE A: NoSQL

No Azure Cosmos DB para NoSQL, os dados não têm esquema e normalmente são desnormalizados. Em vez de associar dados entre entidades e conjuntos, como faria numa base de dados relacional, as associações ocorrem num único item. Especificamente, as associações são confinadas a esse item e não podem ocorrer em vários itens e contentores.

Dica

Se precisar de se associar a itens e contentores, considere reformular o modelo de dados para evitar isto.

Associar-se automaticmente a um único item

Vejamos um exemplo de uma associação automática num item. Considere um contentor com um único item. Este item representa um produto com várias 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"
      }
    ]
  }
]

E se precisar de encontrar o grupo de cores deste produto? Normalmente, teria de escrever uma consulta com um filtro a verificar todos os índices potenciais na tags matriz para obter um valor com um prefixo 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 pode tornar-se insustenível rapidamente. A complexidade ou comprimento da sintaxe da consulta aumenta o número de itens potenciais na matriz. Além disso, esta consulta não é suficientemente flexível para processar produtos futuros, que podem ter mais de três etiquetas.

Numa base de dados relacional tradicional, as etiquetas seriam separadas numa tabela separada e é efetuada uma associação entre tabelas com um filtro aplicado aos resultados. Na API para NoSQL, podemos realizar uma operação de associação automática no item com a JOIN palavra-chave.

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

Esta consulta devolve uma matriz simples com um item para cada valor na 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 dividir a consulta. A consulta tem agora dois aliases: p para cada item de produto no conjunto de resultados e t para a matriz auto-associada tags . A * palavra-chave só é válida para projetar todos os campos se conseguir inferir o conjunto de entrada, mas agora existem dois conjuntos de entrada (p e t). Devido a esta restrição, temos de definir explicitamente os nossos campos devolvidos como id e sku a partir do produto, juntamente com slug as etiquetas. Para facilitar a leitura e compreensão desta consulta, podemos remover o id campo e utilizar um alias para o campo da name etiqueta para mudar o nome para 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 fim, podemos utilizar um filtro para localizar a etiqueta color-group-purple. Como utilizámos a palavra-chave, o JOIN nosso filtro é suficientemente flexível para processar qualquer número variável 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"
  }
]

Associação automática de vários itens

Vamos avançar para um exemplo em que precisamos de encontrar um valor dentro de uma matriz que existe em vários itens. Para este exemplo, considere um contentor com dois itens de produto. Cada item contém etiquetas relevantes para esse 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"
      }
    ]
  }
]

E se precisasse de encontrar todos os itens com uma forma de saco de múmia ? Pode procurar a etiqueta bag-shape-mummy, mas teria de escrever uma consulta complexa que contabilize duas características destes itens:

  • A etiqueta com um bag-shape- prefixo ocorre em índices diferentes em cada matriz. Para o saco de dormir vareno , a etiqueta é o terceiro item (índice: 2). Para o saco de dormir Maresse , a etiqueta é o primeiro item (índice: 0).

  • A tags matriz para cada item tem um comprimento diferente. O saco de dormir vareno tem duas etiquetas enquanto o saco de dormir Maresse tem três.

Aqui, a JOIN palavra-chave é uma ótima ferramenta para criar um produto cruzado dos itens e etiquetas. As associações criam um produto cruzado completo dos conjuntos que participam na associação. O resultado é um conjunto de cadeias de identificação com cada permutação do item e os valores dentro da matriz de destino.

Uma operação de associação nos nossos produtos e etiquetas de sacos de dormir de exemplo cria os seguintes itens:

Item Etiqueta
Maresse Sleeping Bag (6') Ming Forma do Saco: Múmia
Maresse Sleeping Bag (6') Ming Isolamento do Saco: Preenchimento Para Baixo
Vareno Sleeping Bag (6') Cúrcuma Isolamento do Saco: Preenchimento Sintético
Vareno Sleeping Bag (6') Cúrcuma Grupo de Cores: Amarelo
Vareno Sleeping Bag (6') Cúrcuma Forma do Saco: Múmia

Eis a consulta SQL e o conjunto de resultados JSON para uma associação que inclui vários itens no contentor.

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

Tal como acontece com o item único, pode aplicar um filtro aqui para localizar apenas itens que correspondam a uma etiqueta específica. Por exemplo, esta consulta localiza todos os itens com uma etiqueta com o nome bag-shape-mummy para cumprir o requisito inicial mencionado anteriormente nesta secção.

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

Também pode alterar o filtro para obter um conjunto de resultados diferente. Por exemplo, esta consulta localiza todos os itens com uma etiqueta com o nome 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"
  }
]