Поделиться через


Начало работы с JSON — язык запросов в Cosmos DB (в Azure и Fabric)

Работа с нотацией объектов JavaScript (JSON) находится в центре языка запросов. Элементы хранятся в формате JSON, а все запросы, выражения и типы предназначены для работы с данными JSON. Дополнительные сведения о самом JSON см. в официальной спецификации JSON.

Ниже приведены некоторые основные сведения о JSON в этом контексте:

  • Объекты JSON всегда начинаются и { заканчиваются.}
  • Свойства можно вложить друг в друга.
  • Значения свойств могут быть массивами.
  • В именах свойств учитывается регистр.
  • Имена свойств могут быть любой строкой, даже с пробелами или специальными символами.

Вложенные свойства

Доступ к вложенным свойствам JSON можно получить с помощью нотации точек. Это работает так же, как доступ к свойствам на большинстве языков программирования.

Ниже приведен пример документа с вложенным JSON:

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

Затем можно проецирует те же вложенные свойства в запросах:

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

И вы получите ожидаемые выходные данные:

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

Массивы и наборы

JSON поддерживает массивы и может работать с ними в запросах. Чтобы получить доступ к конкретному элементу, используйте его положение в массиве.

Используя тот же пример из предыдущего раздела, мы можем получить доступ к элементу в массиве с помощью его индекса. Например, если мы хотим получить доступ к первому элементу в массиве, мы будем использовать индекс, так как это система индексов 0 на основе нуля для массивов на языке запросов:

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

Этот запрос приводит к следующему объекту JSON:

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

Теперь рассмотрим пример с большим массивом:

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

Часто требуется использовать вложенный запрос или самостоятельное присоединение для работы со всеми элементами в массиве. Например, чтобы получить каждый цвет в виде отдельной строки:

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

Это приведет к тому, что массив JSON выглядит следующим образом:

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

Чтобы проверить, существует ли определенное значение в массиве, можно использовать массив в фильтре после ключевого WHERE слова. В этом примере используется вложенный запрос для фильтрации элементов массива:

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

Этот запрос приводит к неструктурированному массиву строк JSON, который будет включать элемент в пример:

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

Наконец, можно создать массивы, объединив несколько свойств. В этом примере несколько свойств объединяются для формирования массива metadata :

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

Итерация

Язык запросов поддерживает итерацию массивов JSON с помощью ключевого IN слова в источнике FROM .

Рассмотрим этот пример набора данных:

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

В первом примере ключевое IN слово используется для выполнения итерации по свойству colors для каждого продукта:

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

Вы также можете фильтровать отдельные записи в массиве с помощью WHERE предложения. В этом примере sizes свойство фильтруется:

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

Используя то же IN ключевое слово, можно агрегировать результат итерации массива. В этом примере запрос возвращает количество тегов, суммированных во всех элементах в контейнере:

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

Замечание

При использовании ключевого IN слова для итерации нельзя фильтровать или проектовать какие-либо свойства за пределами массива. Вместо этого используются самосоединяемые соединения.

Значения NULL и неопределенные значения

Если свойство отсутствует в документе, его значение равно undefined. Если свойство присутствует, но задано nullзначение , это явно заданное значение. Различие между null и undefined является важным различием, которое может привести к путанице в запросах.

Например, этот объект JSON имеет значение undefined для sku свойства, так как свойство никогда не было определено:

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

Этот объект JSON имеет значение null для того же свойства, так как свойство определено, но не задано со значением:

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

Существуют встроенные функции для проверки этих случаев:

  • IS_NULL проверяет, является nullли свойство .
  • IS_DEFINED проверяет, существует ли свойство (не undefined).

Вот как можно проверить оба:

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

Нотация скобки

Хотя большинство примеров используют нотацию точек для указания свойств, вы всегда можете указать те же свойства с помощью нотации скобки .

Начнем с простого объекта с вложенным объектом в качестве значения metadata свойства:

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

Для этого объекта можно ссылаться на metadata.link свойство тремя различными способами с помощью сочетаний нотации точек и квадратных скобок :

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

Подсказка

Если имя свойства содержит пробелы, специальные символы или совпадает с зарезервированным словом, необходимо использовать нотацию с скобками для указания свойства.

Выражения JSON

Объекты JSON можно создавать непосредственно в результатах запроса. Начнем с этого массива JSON в качестве примера:

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

Используя самый простой синтаксис, можно повлиять на имена свойств относительно плоского объекта JSON с помощью угловых квадратных скобок ({/}) и внедренного синтаксиса JSON в запросе 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"
    }
  }
]

В предыдущем примере результат имел вычисленное имя $1 , так как явное имя не определено. В следующем примере результат имеет явное имя, определенное product с помощью псевдонима:

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

Кроме того, результат можно сравить с помощью ключевого VALUE слова в инструкции 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"
  }
]

Далее можно использовать синтаксис JSON для "повторного формирования" результирующий объект JSON для включения массивов, подобъектов и других конструкций JSON, которые могут не быть явно определены в исходном элементе. Этот метод полезен, если клиентское приложение ожидает данные в определенной схеме, которая не соответствует базовым данным.

Рассмотрим эту схему JSON, например:

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

Эта схема позволит объекту JSON структурированного в этом формате:

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

Этот запрос NoSQL переназначает исходный объект, который соответствует этой новой схеме:

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

Псевдонимы контейнеров

По умолчанию термин, используемый после FROM ключевого слова, ссылается на контейнер , предназначенный для запроса. Сам термин не требуется для сопоставления имени контейнера.

Например, если контейнер называется products, любой из этих запросов работает хорошо, и все ссылающиеся на products контейнер, если этот контейнер является целевым объектом запроса:

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

Чтобы сделать запрос NoSQL более кратким, обычно псевдоним имени контейнера с более коротким именем. Псевдоним можно сделать с помощью ключевого AS слова:

SELECT
  p.id
FROM
  products AS p

Язык запросов также имеет короткий синтаксис, где псевдоним можно определить сразу после ссылки целевого контейнера без ключевого AS слова. Это краткое руководство функционально эквивалентно использованию ключевого AS слова:

SELECT
  p.id
FROM
  products p

Псевдонимы свойств

Вы также можете переименовать поля в результатах с помощью псевдонимов, определяющих с тем же AS ключевым словом. Для следующих нескольких примеров рассмотрим эти примеры данных:

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

В этом первом примере metadataLink псевдоним используется для metadata.link значения свойства:

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

Это важно

Псевдоним нельзя использовать для проецирования значения в качестве имени свойства с пробелом, специальным символом или зарезервированным словом. Если вы хотите изменить проекцию значения на, например, имя свойства с пробелом, необходимо использовать выражение JSON.

Например

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

Если запрос NoSQL имеет два свойства с одинаковым именем, используйте псевдонимы для переименования одного или обоих свойств, чтобы они были дискредитированы в проецированном результате.

Рассмотрим эти примеры данных:

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

Замечание

В этом примере данных и результатах запроса было удалено несколько свойств и значений для краткости.

Этот запрос NoSQL возвращает p.sizes[].key и p.tags[].key свойства в результатах между продуктами, но будет псевдоним каждого key свойства, чтобы избежать конфликтов:

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