Condividi tramite


Introduzione a JSON - Linguaggio di query in Cosmos DB (in Azure e Infrastruttura)

L'uso di JavaScript Object Notation (JSON) è al centro del linguaggio di query. Gli elementi vengono archiviati come JSON e tutte le query, le espressioni e i tipi sono progettati per funzionare con i dati JSON. Per altre informazioni su JSON stesso, vedere la specifica JSON formale.

Ecco alcuni aspetti chiave da conoscere su JSON in questo contesto:

  • Gli oggetti JSON iniziano sempre con e terminano con {}.
  • Le proprietà possono essere annidate tra loro.
  • I valori delle proprietà possono essere matrici.
  • I nomi delle proprietà sono sensibili alle maiuscole.
  • I nomi delle proprietà possono essere qualsiasi stringa, anche con spazi o caratteri speciali.

Proprietà annidate

È possibile accedere alle proprietà JSON annidate usando la notazione del punto. Questo funziona esattamente come l'accesso alle proprietà nella maggior parte dei linguaggi di programmazione.

Ecco un documento di esempio con JSON annidato:

[
  {
    "name": "Heatker Women's Jacket",
    "category": "apparel",
    "slug": "heatker-women-s-jacket",
    "sizes": [
      {
        "key": "s",
        "description": "Small"
      }
    ],
    "metadata": {
      "link": "https://www.adventure-works.com/heatker-women-s-jacket/68719520138.p"
    }
  }
]

È quindi possibile proiettare le stesse proprietà annidate nelle query:

SELECT
  p.name,
  p.category,
  p.metadata.link
FROM
  products p
WHERE
  p.name = "Heatker Women's Jacket"

E si otterrà questo output previsto:

[
  {
    "name": "Heatker Women's Jacket",
    "category": "apparel",
    "link": "https://www.adventure-works.com/heatker-women-s-jacket/68719520138.p"
  }
]

Matrici e set

JSON supporta matrici ed è possibile usarle nelle query. Per accedere a un elemento specifico, usare la relativa posizione nella matrice.

Usando lo stesso esempio della sezione precedente, è possibile accedere a un elemento nella matrice usando il relativo indice. Ad esempio, se si vuole accedere al primo elemento della matrice, si userà un indice di 0 poiché si tratta di un sistema di indici in base zero per le matrici nel linguaggio di query:

SELECT
  p.name,
  p.sizes[0].description AS defaultSize
FROM
  products p
WHERE
  p.name = "Heatker Women's Jacket"

Questa query restituisce l'oggetto JSON seguente:

[
  {
    "name": "Heatker Women's Jacket",
    "defaultSize": "Small"
  }
]

Si consideri ora un esempio con una matrice più grande:

[
  {
    "name": "Vencon Kid's Coat",
    "category": "apparel",
    "slug": "vencon-kid-s-coat",
    "colors": [
      "cardinal",
      "disco"
    ],
    "sizes": [
      {
        "key": "m",
        "description": "Medium"
      },
      {
        "key": "l",
        "description": "Large"
      },
      {
        "key": "xl",
        "description": "Extra Large"
      }
    ]
  }
]

Spesso si vuole usare una sottoquery o un self-join per lavorare con tutti gli elementi di una matrice. Ad esempio, per ottenere ogni colore come riga separata:

SELECT
  p.name,
  c AS color
FROM
  products p
JOIN
  c IN p.colors
WHERE
  p.name = "Vencon Kid's Coat"

Il risultato sarebbe una matrice JSON simile alla seguente:

[
  {
    "name": "Vencon Kid's Coat",
    "color": "cardinal"
  },
  {
    "name": "Vencon Kid's Coat",
    "color": "disco"
  }
]

Per verificare se un determinato valore esiste in una matrice, è possibile usare la matrice nel filtro dopo la WHERE parola chiave . In questo esempio viene usata una sottoquery per filtrare gli elementi della matrice:

SELECT VALUE
  p.name
FROM
  products p
WHERE
  EXISTS(SELECT VALUE
    c
  FROM
    c IN p.sizes
  WHERE
    c.description LIKE "%Large")

Questa query restituisce una matrice JSON flat di stringhe, che include l'elemento nell'esempio:

[
  ...,
  "Vencon Kid's Coat"
  ...
]

Infine, è possibile costruire matrici combinando più proprietà. In questo esempio più proprietà vengono combinate per formare una metadata matrice:

SELECT
  p.name,
  [
    p.category,
    p.slug,
    p.metadata.link
  ] AS metadata
FROM
  products p
WHERE
  p.name = "Heatker Women's Jacket"
[
  {
    "name": "Heatker Women's Jacket",
    "metadata": [
      "apparel",
      "heatker-women-s-jacket",
      "https://www.adventure-works.com/heatker-women-s-jacket/68719520138.p"
    ]
  }
]

Iterazione

Il linguaggio di query supporta l'iterazione su matrici JSON usando la IN parola chiave nell'origine FROM .

Si consideri questo set di dati di esempio:

[
  {
    "name": "Pila Swimsuit",
    "colors": [
      "regal-blue",
      "rose-bud-cherry"
    ],
    "sizes": [
      {
        "key": "m",
        "description": "Medium"
      },
      {
        "key": "l",
        "description": "Large"
      },
      {
        "key": "xl",
        "description": "Extra Large"
      }
    ]
  },
  {
    "name": "Makay Bikini",
    "colors": [
      "starship"
    ],
    "sizes": [
      {
        "key": "s",
        "description": "Small"
      },
      {
        "key": "m",
        "description": "Medium"
      },
      {
        "key": "l",
        "description": "Large"
      }
    ]
  }
]

Questo primo esempio usa la IN parola chiave per eseguire l'iterazione sulla colors proprietà per ogni prodotto:

SELECT
  *
FROM
  p IN p.colors
[
  "regal-blue",
  "rose-bud-cherry",
  "starship"
]

È anche possibile filtrare singole voci nella matrice usando la WHERE clausola . In questo esempio la sizes proprietà viene filtrata:

SELECT
  p.key
FROM
  p IN p.sizes
WHERE
  p.description LIKE "%Large"
[
  {
    "key": "l"
  },
  {
    "key": "xl"
  },
  {
    "key": "l"
  }
]

Usando la stessa IN parola chiave, è possibile aggregare il risultato di un'iterazione di matrice. In questo esempio, la query restituisce il conteggio del numero di tag sommati in tutti gli elementi del contenitore:

SELECT VALUE
  COUNT(1)
FROM
  p IN p.sizes

Annotazioni

Quando si usa la parola chiave per l'iterazione IN , non è possibile filtrare o proiettare proprietà esterne alla matrice. Si usano invece i self-join.

Valori Null e non definiti

Se una proprietà non è presente in un documento, il relativo valore è undefined. Se una proprietà è presente ma impostata su null, si tratta di un valore impostato in modo esplicito. La distinzione tra null e undefined è una distinzione importante che può causare confusione nelle query.

Ad esempio, questo oggetto JSON avrà un valore per undefined la sku proprietà perché la proprietà non è mai stata definita:

[
  {
    "name": "Witalica helmet",
    "category": "gear",
  }
]

Questo oggetto JSON avrà un valore per null la stessa proprietà perché la proprietà è ancora definita non impostata con un valore:

[
  {
    "name": "Witalica helmet",
    "category": "gear",
    "sku": null
  }
]

Esistono funzioni predefinite per verificare la presenza di questi casi:

  • IS_NULL controlla se una proprietà è null.
  • IS_DEFINED controlla se esiste una proprietà (non undefinedè ).

Ecco come è possibile verificare la presenza di entrambe le opzioni:

SELECT
  IS_DEFINED(p.sku) AS isSkuDefined,
  IS_NULL(p.sku) AS isSkuDefinedButNull
FROM
  products p

Notazione tra parentesi quadre

Anche se la maggior parte degli esempi usa la notazione punto per specificare le proprietà, è sempre possibile specificare le stesse proprietà usando la notazione tra parentesi quadre .

Si inizierà con un oggetto semplice con un oggetto annidato come valore della metadata proprietà :

[
  {
    "name": "Hikomo Sandals",
    "metadata": {
      "link": "https://www.adventure-works.com/hikomo-sandals/68719519305.p"
    }
  }
]

Per tale oggetto, è possibile fare riferimento alla metadata.link proprietà in tre modi distinti usando combinazioni di notazione punto e parentesi quadre :

SELECT
  p.metadata.link AS metadataLinkDotNotation,
  p["metadata"]["link"] AS metadataLinkBracketNotation,
  p.metadata["link"] AS metadataLinkMixedNotation
FROM
  products p
WHERE
  p.name = "Hikomo Sandals"
[
  {
    "metadataLinkDotNotation": "https://www.adventure-works.com/hikomo-sandals/68719519305.p",
    "metadataLinkBracketNotation": "https://www.adventure-works.com/hikomo-sandals/68719519305.p",
    "metadataLinkMixedNotation": "https://www.adventure-works.com/hikomo-sandals/68719519305.p"
  }
]

Suggerimento

Se un nome di proprietà ha spazi, caratteri speciali o corrisponde a una parola riservata, è necessario utilizzare la notazione tra parentesi quadre per specificare la proprietà.

Espressioni JSON

È possibile creare oggetti JSON direttamente nei risultati della query. Si inizierà con questa matrice JSON come esempio:

[
  {
    "name": "Diannis Watch",
    "category": "apparel",
    "detailCategory": "apparel-accessories-watches",
    "slug": "diannis-watch",
    "sku": "64801",
    "price": 98,
    "quantity": 159
  },
  {
    "name": "Confira Watch",
    "category": "apparel",
    "detailCategory": "apparel-accessories-watches",
    "slug": "confira-watch",
    "sku": "64800",
    "price": 105,
    "quantity": 193
  }
]

Usando la sintassi più semplice, è possibile influenzare i nomi delle proprietà di un oggetto JSON relativamente flat usando parentesi angolari ({/}) e la sintassi JSON incorporata in una query NoSQL:

SELECT {
  "brandName": p.name,
  "department": p.category
}
FROM
  products p
WHERE
  p.detailCategory = "apparel-accessories-watches"
[
  {
    "$1": {
      "brandName": "Diannis Watch",
      "department": "apparel"
    }
  },
  {
    "$1": {
      "brandName": "Confira Watch",
      "department": "apparel"
    }
  }
]

Nell'esempio precedente il risultato ha un nome dedotto perché $1 non è stato definito un nome esplicito. In questo esempio successivo, il risultato ha un nome esplicito di product definito usando un alias:

SELECT {
  "brandName": p.name,
  "department": p.category
} AS product
FROM
  products p
WHERE
  p.detailCategory = "apparel-accessories-watches"
[
  {
    "product": {
      "brandName": "Diannis Watch",
      "department": "apparel"
    }
  },
  {
    "product": {
      "brandName": "Confira Watch",
      "department": "apparel"
    }
  }
]

In alternativa, il risultato può essere appiattito usando la VALUE parola chiave in un'istruzione SELECT VALUE :

SELECT VALUE {
  "brandName": p.name,
  "department": p.category
}
FROM
  products p
WHERE
  p.detailCategory = "apparel-accessories-watches"
[
  {
    "brandName": "Diannis Watch",
    "department": "apparel"
  },
  {
    "brandName": "Confira Watch",
    "department": "apparel"
  }
]

Ancora più avanti, è possibile usare la sintassi JSON per "rimodellare" l'oggetto JSON del risultato per includere matrici, sottooggetti e altri costrutti JSON che potrebbero non essere definiti in modo esplicito nell'elemento originale. Questa tecnica è utile se l'applicazione client prevede dati in uno schema specifico che non corrisponde ai dati sottostanti.

Si consideri questo schema JSON, ad esempio:

{
  "$schema": "http://json-schema.org/draft-04/schema#",
  "type": "object",
  "required": [
    "id",
    "category",
    "financial"
  ],
  "properties": {
    "id": {
      "type": "string"
    },
    "name": {
      "type": "string"
    },
    "category": {
      "type": "object",
      "properties": {
        "department": {
          "type": "string"
        },
        "section": {
          "type": "string"
        }
      },
      "required": [
        "department"
      ]
    },
    "inventory": {
      "type": "object",
      "properties": {
        "stock": {
          "type": "number"
        }
      }
    },
    "financial": {
      "type": "object",
      "properties": {
        "listPrice": {
          "type": "number"
        }
      },
      "required": [
        "listPrice"
      ]
    }
  }
}

Questo schema consentirà un oggetto JSON strutturato in questo formato:

[
  {
    "id": "[string]",
    "name": "[string]",
    "category": {
      "department": "[string]",
      "section": "[string]"
    },
    "inventory": {
      "stock": [number]
    },
    "financial": {
      "listPrice": [number]
    }
  }
]

Questa query NoSQL esegue nuovamente il mapping dell'oggetto originale[s] per essere conforme a questo nuovo schema:

SELECT VALUE {
  "id": p.sku,
  "name": p.name,
  "category": {
    "department": p.category,
    "section": p.detailCategory
  },
  "inventory": {
    "stock": p.quantity
  },
  "financial": {
    "listPrice": p.price
  }
}
FROM
  products p
WHERE
  p.detailCategory = "apparel-accessories-watches"
[
  {
    "id": "64801",
    "name": "Diannis Watch",
    "category": {
      "department": "apparel",
      "section": "apparel-accessories-watches"
    },
    "inventory": {
      "stock": 159
    },
    "financial": {
      "listPrice": 98
    }
  },
  {
    "id": "64800",
    "name": "Confira Watch",
    "category": {
      "department": "apparel",
      "section": "apparel-accessories-watches"
    },
    "inventory": {
      "stock": 193
    },
    "financial": {
      "listPrice": 105
    }
  }
]

Alias del contenitore

Per impostazione predefinita, il termine usato dopo la FROM parola chiave fa riferimento al contenitore che è la destinazione della query. Il termine stesso non è obbligatorio per trovare la corrispondenza con il nome del contenitore.

Ad esempio, se il contenitore è denominato products, una di queste query funziona correttamente e tutti fanno riferimento al products contenitore, purché tale contenitore sia la destinazione della query:

SELECT
  products.id
FROM
  products
SELECT
  p.id
FROM
  p
SELECT
  items.id
FROM
  items
SELECT
  targetContainer.id
FROM
  targetContainer

Per rendere la query NoSQL più concisa, è comune eseguire l'alias del nome del contenitore con un nome più breve. L'aliasing può essere eseguito usando la AS parola chiave :

SELECT
  p.id
FROM
  products AS p

Il linguaggio di query ha anche una sintassi abbreviata in cui l'alias può essere definito immediatamente dopo il riferimento del contenitore di destinazione senza la AS parola chiave . Questa abbreviata è funzionalmente equivalente all'uso della AS parola chiave :

SELECT
  p.id
FROM
  products p

Alias delle proprietà

È anche possibile rinominare i campi nei risultati usando alias definiti con la stessa AS parola chiave. Per gli esempi successivi, considerare questi dati di esempio:

[
  {
    "name": "Oceabelle Scarf",
    "detailCategory": "apparel-accessories-scarfs-and-socks",
    "metadata": {
      "link": "https://www.adventure-works.com/oceabelle-scarf/68719522190.p"
    }
  },
  {
    "name": "Shinity Socks",
    "detailCategory": "apparel-accessories-scarfs-and-socks",
    "metadata": {
      "link": "https://www.adventure-works.com/shinity-socks/68719522161.p"
    }
  },
  {
    "name": "Horric Socks",
    "detailCategory": "apparel-accessories-scarfs-and-socks",
    "metadata": {
      "link": "https://www.adventure-works.com/horric-socks/68719522177.p"
    }
  }
]

In questo primo esempio, l'alias metadataLink viene usato per il metadata.link valore della proprietà:

SELECT
  p.name,
  p.metadata.link AS metadataLink
FROM
  products p
[
  {
    "name": "Oceabelle Scarf",
    "metadataLink": "https://www.adventure-works.com/oceabelle-scarf/68719522190.p"
  },
  {
    "name": "Shinity Socks",
    "metadataLink": "https://www.adventure-works.com/shinity-socks/68719522161.p"
  },
  {
    "name": "Horric Socks",
    "metadataLink": "https://www.adventure-works.com/horric-socks/68719522177.p"
  }
]

Importante

Non è possibile usare l'aliasing per proiettare un valore come nome di proprietà con uno spazio, un carattere speciale o una parola riservata. Se si vuole modificare la proiezione di un valore in, ad esempio, avere un nome di proprietà con uno spazio, è necessario usare un'espressione JSON.

Ad esempio:

SELECT VALUE {
  "product name": p.name,
  "from": p.metadata.link,
  "detail/category": p.detailCategory
}
FROM
  products p
WHERE
  p.detailCategory = "apparel-accessories-scarfs-and-socks"
[
  {
    "product name": "Oceabelle Scarf",
    "from": "https://www.adventure-works.com/oceabelle-scarf/68719522190.p",
    "detail/category": "apparel-accessories-scarfs-and-socks"
  },
  {
    "product name": "Shinity Socks",
    "from": "https://www.adventure-works.com/shinity-socks/68719522161.p",
    "detail/category": "apparel-accessories-scarfs-and-socks"
  },
  {
    "product name": "Horric Socks",
    "from": "https://www.adventure-works.com/horric-socks/68719522177.p",
    "detail/category": "apparel-accessories-scarfs-and-socks"
  }
]

Se una query NoSQL ha due proprietà con lo stesso nome, usare gli alias per rinominare una o entrambe le proprietà in modo che vengano disambiguate nel risultato proiettato.

Si considerino questi dati di esempio:

[
  {
    "name": "Oceabelle Scarf",
    "detailCategory": "apparel-accessories-scarfs-and-socks",
    "sizes": [
      {
        "key": "s"
      },
      ...
    ],
    "tags": [
      ...
    ]
  },
  {
    "name": "Shinity Socks",
    "detailCategory": "apparel-accessories-scarfs-and-socks",
    "sizes": [
      ...
      {
        "key": "10"
      },
      ...
    ],
    "tags": [
      ...
      {
        "key": "length"
      }
    ]
  },
  {
    "name": "Horric Socks",
    "detailCategory": "apparel-accessories-scarfs-and-socks",
    "sizes": [
      ...
      {
        "key": "7"
      },
      ...
    ],
    "tags": [
      {
        "key": "fabric"
      },
      ...
    ]
  }
]

Annotazioni

In questi dati di esempio e nel risultato della query sono state rimosse più proprietà e valori per brevità.

Questa query NoSQL restituisce le p.sizes[].key proprietà e p.tags[].key nel risultato del prodotto incrociato, ma eseguirà l'alias di ogni key proprietà per evitare conflitti:

SELECT
  p.name,
  s.key AS sizeKey,
  t.key AS tagKey
FROM
  products p
JOIN
  s IN p.sizes
JOIN
  t in p.tags
WHERE
  p.detailCategory = "apparel-accessories-scarfs-and-socks"
[
  {
    "name": "Oceabelle Scarf",
    "sizeKey": "s",
    "tagKey": "fabric"
  },
  ...
  {
    "name": "Shinity Socks",
    "sizeKey": "10",
    "tagKey": "length"
  },
  ...
  {
    "name": "Horric Socks",
    "sizeKey": "7",
    "tagKey": "fabric"
  }
]