Associações automáticas no Azure Cosmos DB para NoSQL

APLICA-SE A: NoSQL

No Azure Cosmos DB para NoSQL, os dados não têm esquemas 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 associar entre itens e contentores, considere reformular o modelo de dados para evitar esta situação.

Associação automática com 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 cada índice potencial 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 do comprimento da sintaxe da consulta é aumentada pelo 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 uma associação entre tabelas é efetuada 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 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. Uma vez que 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 ? Poderia procurar a etiqueta bag-shape-mummy, mas teria de escrever uma consulta complexa que representasse 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 do saco de dormir de exemplo irá criar os seguintes itens:

Item Etiqueta
Saco De Dormir Maresse (6') Ming Forma do Saco: Múmia
Saco De Dormir Maresse (6') Ming Isolamento do Saco: Preenchimento 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 que têm uma etiqueta chamada 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"
  }
]

Passos seguintes