9.16. Функции и операторы JSON
В этом разделе описываются:
функции и операторы, предназначенные для работы с данными JSON
язык путей SQL/JSON
Чтобы узнать больше о стандарте SQL/JSON, обратитесь к [sqltr-19075-6]. Типы JSON, поддерживаемые в PostgreSQL, описаны в Разделе 8.14.
9.16.1. Обработка и создание данных JSON
В Таблице 9.45 показаны имеющиеся операторы для работы с данными JSON (см. Раздел 8.14). Кроме них для типа jsonb
, но не для json
, определены обычные операторы сравнения, показанные в Таблице 9.1. Они следуют правилам упорядочивания для операций B-дерева, описанным в Подразделе 8.14.4. В Разделе 9.21 вы также можете узнать об агрегатной функции json_agg
, которая агрегирует значения записи в виде JSON, и агрегатной функции json_object_agg
, агрегирующей пары значений в объект JSON, а также их аналогах для jsonb
, функциях jsonb_agg
и jsonb_object_agg
.
Таблица 9.45. Операторы для типов json
и jsonb
Оператор Описание Пример(ы) |
---|
Извлекает
|
Извлекает поле JSON-объекта по заданному ключу.
|
Извлекает
|
Извлекает поле JSON-объекта по заданному ключу, в виде значения
|
Извлекает внутренний JSON-объект по заданному пути, элементами которого могут быть индексы массивов или ключи.
|
Извлекает внутренний JSON-объект по заданному пути в виде значения
|
Примечание
Если структура входного JSON не соответствует запросу, например указанный ключ или элемент массива отсутствует, операторы извлечения поля/элемента/пути не выдают ошибку, а возвращают NULL.
Некоторые из следующих операторов существуют только для jsonb
, как показано в Таблице 9.46. В Подразделе 8.14.4 описано, как эти операторы могут использоваться для эффективного поиска в индексированных данных jsonb
.
Таблица 9.46. Дополнительные операторы jsonb
Оператор Описание Пример(ы) |
---|
Первое значение JSON содержит второе? (Что означает «содержит», подробно описывается в Подразделе 8.14.3.)
|
Первое значение JSON содержится во втором?
|
Текстовая строка присутствует в значении JSON в качестве ключа верхнего уровня или элемента массива?
|
Какие-либо текстовые строки из массива присутствуют в качестве ключей верхнего уровня или элементов массива?
|
Все текстовые строки из массива присутствуют в качестве ключей верхнего уровня или элементов массива?
|
Соединяет два значения
Чтобы вставить один массив в другой в качестве массива, поместите его в дополнительный массив, например:
|
Удаляет ключ (и его значение) из JSON-объекта или соответствующие строковые значения из JSON-массива.
|
Удаляет из левого операнда все перечисленные ключи или элементы массива.
|
Удаляет из массива элемент в заданной позиции (отрицательные номера позиций отсчитываются от конца). Выдаёт ошибку, если переданное значение JSON — не массив.
|
Удаляет поле или элемент массива с заданным путём, в составе которого могут быть индексы массивов или ключи.
|
Выдаёт ли путь JSON какой-либо элемент для заданного значения JSON?
|
Возвращает результат проверки предиката пути JSON для заданного значения JSON. При этом учитывается только первый элемент результата. Если результат не является логическим, возвращается
|
Примечание
Операторы jsonpath
@?
и @@
подавляют следующие ошибки: отсутствие поля объекта или элемента массива, несовпадение типа элемента JSON и ошибки в числах и дате/времени. Описанные ниже функции, связанные с jsonpath
, тоже могут подавлять ошибки такого рода. Это может быть полезно, когда нужно произвести поиск по набору документов JSON, имеющих различную структуру.
В Таблице 9.47 показаны функции, предназначенные для создания значений json
и jsonb
.
Таблица 9.47. Функции для создания JSON
Функция Описание Пример(ы) |
---|
Преобразует произвольное SQL-значение в
|
Преобразует массив SQL в JSON-массив. Эта функция работает так же, как
|
Преобразует составное значение SQL в JSON-объект. Эта функция работает так же, как
|
Формирует JSON-массив (возможно, разнородный) из переменного списка аргументов. Каждый аргумент преобразуется методом
|
Формирует JSON-объект из переменного списка аргументов. По соглашению в этом списке перечисляются по очереди ключи и значения. Аргументы, задающие ключи, приводятся к текстовому типу, а аргументы-значения преобразуются методом
|
Формирует объект JSON из текстового массива. Этот массив должен иметь либо одну размерность с чётным числом элементов (в этом случае они воспринимаются как чередующиеся ключи/значения), либо две размерности и при этом каждый внутренний массив содержит ровно два элемента, которые воспринимаются как пара ключ/значение. Все значения преобразуются в строки JSON.
|
Эта форма
|
В Таблице 9.48 показаны функции, предназначенные для работы со значениями json
и jsonb
.
Таблица 9.48. Функции для обработки JSON
Функция Описание Пример(ы) |
---|
Разворачивает JSON-массив верхнего уровня в набор значений JSON.
value ----------- 1 true [2,false] |
Разворачивает JSON-массив верхнего уровня в набор значений
value ----------- foo bar |
Возвращает число элементов во внешнем JSON-массиве верхнего уровня.
|
Разворачивает JSON-объект верхнего уровня в набор пар ключ/значение (key/value).
key | value -----+------- a | "foo" b | "bar" |
Разворачивает JSON-объект верхнего уровня в набор пар ключ/значение (key/value). Возвращаемые значения будут иметь тип
key | value -----+------- a | foo b | bar |
Извлекает внутренний JSON-объект по заданному пути. (То же самое делает оператор
|
Извлекает внутренний JSON-объект по заданному пути в виде значения
|
Выдаёт множество ключей в JSON-объекте верхнего уровня.
json_object_keys ----------------- f1 f2 |
Разворачивает JSON-объект верхнего уровня в строку, имеющую составной тип аргумента Для преобразования значения JSON в SQL-тип выходного столбца последовательно применяются следующие правила:
В следующем примере значение JSON фиксировано, но обычно такая функция обращается с использованием
a | b | c ---+-----------+------------- 1 | {2,"a b"} | (4,"a b c") |
Разворачивает JSON-массив верхнего уровня с объектами в набор строк, имеющих составной тип аргумента
a | b ---+--- 1 | 2 3 | 4 |
Разворачивает JSON-объект верхнего уровня в строку, имеющую составной тип, определённый в предложении
a | b | c | d | r ---+---------+---------+---+--------------- 1 | [1,2,3] | {1,2,3} | | (123,"a b c") |
Разворачивает JSON-массив верхнего уровня с объектами в набор строк, имеющих составной тип, определённый в предложении
a | b ---+----- 1 | foo 2 | |
Возвращает объект
|
Если значение
|
Возвращает объект
|
Удаляет из данного значения JSON все поля объектов, имеющие значения null, на всех уровнях вложенности. Значения null, не относящиеся к полям объектов, сохраняются без изменений.
|
Проверяет, есть ли в заданном значении JSON какой-либо элемент, соответствующий пути JSON. В случае присутствия аргумента
|
Возвращает результат проверки предиката пути JSON для заданного значения JSON. При этом учитывается только первый элемент результата. Если результат не является логическим, возвращается
|
Возвращает все элементы JSON, полученные по указанному пути для заданного значения JSON. Дополнительные аргументы
jsonb_path_query ------------------ 2 3 4 |
Возвращает все элементы JSON, полученные по указанному пути для заданного значения JSON, в виде JSON-массива. Дополнительные аргументы
|
Возвращает первый элемент JSON, полученный по указанному пути для заданного значения JSON, либо NULL, если этому пути не соответствуют никакие элементы. Дополнительные аргументы
|
Эти функции работают подобно их двойникам без суффикса
|
Преобразует данное значение JSON в визуально улучшенное текстовое представление с отступами.
[ { "f1": 1, "f2": null }, 2 ] |
Возвращает тип значения на верхнем уровне JSON в виде текстовой строки. Возможные типы:
|
9.16.2. Язык путей SQL/JSON
Выражения путей SQL/JSON определяют элементы, извлекаемые из данных JSON, подобно тому, как выражения XPath позволяют обращаться из SQL к XML. В PostgreSQL выражения путей представляются в виде типа данных jsonpath
и могут использовать любые элементы, описанные в Подразделе 8.14.7.
Операторы и функции запросов к JSON передают поступившее им выражение обработчику путей для вычисления. Если выражению соответствуют фигурирующие в запросе данные JSON, в результате выдаётся соответствующий элемент JSON или набор элементов. Выражения путей записываются на языке путей SQL/JSON и могут включать сложные арифметические выражения и функции.
Выражение пути состоит из последовательности элементов, допустимых для типа jsonpath
. Обычно оно вычисляется слева направо, но при необходимости порядок операций можно изменить, добавив скобки. В случае успешного вычисления выдаётся последовательность элементов JSON, и результат вычисления возвращается в функцию JSON-запроса, которая завершает обработку выражения.
Для обращения к поступившему в запрос значению JSON (элементу контекста) в выражении пути используется переменная $
. Затем могут следовать один или более операторов обращения, которые, опускаясь в структуре JSON с одного уровня на другой, извлекают элементы, вложенные в текущий элемент контекста. При этом каждый последующий оператор имеет дело с результатом вычисления, полученным на предыдущем шаге.
Например, предположим, что у вас есть данные JSON, полученные от GPS-трекера, которые вы хотели бы проанализировать:
{ "track": { "segments": [ { "location": [ 47.763, 13.4034 ], "start time": "2018-10-14 10:05:14", "HR": 73 }, { "location": [ 47.706, 13.2635 ], "start time": "2018-10-14 10:39:21", "HR": 135 } ] } }
Чтобы получить имеющиеся сегменты треков, воспользуйтесь оператором обращения .
, который позволяет погрузиться внутрь JSON-объектов: ключ
$.track.segments
Для получения содержимого массива обычно используется оператор [*]
. Например, следующий путь выдаст координаты концов всех имеющихся сегментов треков:
$.track.segments[*].location
Чтобы получить координаты только первого сегмента, можно задать соответствующий индекс в операторе обращения []
. Заметьте, что индексы в JSON-массивах отсчитываются с 0:
$.track.segments[0].location
Результат каждого шага вычисления выражения может быть обработан операторами и методами jsonpath
, перечисленными в Подразделе 9.16.2.2. Перед именем метода должна стоять точка. Например, так можно получить размер массива:
$.track.segments.size()
Другие примеры использования операторов и методов jsonpath
в выражениях пути приведены ниже в Подразделе 9.16.2.2.
Определяя путь, также можно использовать выражения фильтра, работающие подобно предложению WHERE
в SQL. Выражение фильтра начинается со знака вопроса и содержит условие в скобках:
? (условие
)
Выражения фильтра указываются сразу после шага вычисления пути, к которому они должны применяться. Результаты шага проходят через фильтр, и на выходе остаются только те элементы, которые удовлетворяют заданному условию. В SQL/JSON действует троичная логика, то есть результатом выражения может быть true
, false
или unknown
(неизвестность). Значение unknown
играет ту же роль, что и NULL
в SQL, и может быть проверено предикатом is unknown
. На последующих шагах вычисления пути будут обрабатываться только те элементы, для которых выражение фильтра выдало true
.
Функции и операторы, которые можно использовать в выражениях фильтра, перечислены в Таблице 9.50. Переменная @
в выражении фильтра представляет фильтруемое значение (результат предыдущего шага в пути). Для получения внутренних элементов этого значения вы можете добавить после @
операторы обращения.
Например, предположим, что вы хотите получить все показатели пульса, превышающие 130. Это можно сделать с помощью следующего выражения:
$.track.segments[*].HR ? (@ > 130)
Чтобы получить в результате время начала соответствующих сегментов, вы должны отфильтровать ненужные сегменты, а затем выбрать время, так что фильтр будет применяться к предыдущему шагу и путь окажется другим:
$.track.segments[*] ? (@.HR > 130)."start time"
Можно также использовать несколько выражений фильтра по очереди, когда это требуется. Например, следующее выражение выбирает время начала всех сегментов с определёнными координатами и высоким показателем пульса:
$.track.segments[*] ? (@.location[1] < 13.4) ? (@.HR > 130)."start time"
Также возможно использовать фильтры на разных уровнях вложенности. В следующем примере сначала сегменты фильтруются по координатам, а затем для подходящих сегментов, если они находятся, выбираются значения высокого пульса:
$.track.segments[*] ? (@.location[1] < 13.4).HR ? (@ > 130)
Можно также вкладывать выражения фильтра одно в другое:
$.track ? (exists(@.segments[*] ? (@.HR > 130))).segments.size()
Данное выражение возвращает количество сегментов в треке, если он содержит сегменты с высокими показателями пульса, или пустую последовательность, если таких сегментов нет.
Реализация языка путей SQL/JSON в PostgreSQL имеет следующие отличия от стандарта SQL/JSON:
Выражение пути может быть булевым предикатом, хотя стандарт SQL/JSON допускает предикаты только в фильтрах. Это необходимо для реализации оператора
@@
. Например, следующее выражениеjsonpath
допускается в PostgreSQL:$.track.segments[*].HR < 70
Есть небольшие различия в интерпретации шаблонов регулярных выражений, используемых в фильтрах
like_regex
; имеющиеся особенности описаны в Подразделе 9.16.2.3.
9.16.2.1. Строгий и нестрогий режимы
Когда вы обращаетесь к данным JSON, выражение пути может не соответствовать фактической структуре данных JSON. Попытка обратиться к несуществующему члену объекта или элементу массива приводит к ошибке структурного типа. Для обработки такого рода ошибок в выражениях путей SQL/JSON предусмотрены два режима:
lax (по умолчанию) — нестрогий режим, в котором обработчик путей неявно адаптирует обрабатываемые данные к указанному пути. Любые возникающие структурные ошибки подавляются и заменяются пустыми последовательностями SQL/JSON.
strict — строгий режим, в котором структурные ошибки выдаются как есть.
Нестрогий режим упрощает сопоставление структуры документа JSON с выражением пути в случаях, когда данные JSON не соответствуют ожидаемой схеме. Если операнд не удовлетворяет требованиям определённой операции, он может перед выполнением этой операции автоматически оборачиваться в массив SQL/JSON или наоборот, разворачиваться так, чтобы его элементы образовали последовательность SQL/JSON. Помимо этого, в нестрогом режиме операторы сравнения автоматически разворачивают свои операнды, что позволяет легко сравнивать массивы SQL/JSON. Массив с одним элементом в таком режиме считается равным своему элементу. Автоматическое разворачивание не выполняется только в следующих случаях:
В выражении пути фигурируют методы
size()
иtype()
, возвращающие соответственно число элементов в массиве и тип.Обрабатываемые данные JSON содержат вложенные массивы. В этом случае разворачивается только массив верхнего уровня, а внутренние массивы остаются без изменений. Таким образом, неявное разворачивание может опускаться на каждом шаге вычисления пути только на один уровень.
Например, обрабатывая данные GPS, показанные выше, в нестрогом режиме можно не обращать внимание на то, что в них содержится массив сегментов:
lax $.track.segments.location
В строгом режиме указанный путь должен в точности соответствовать структуре обрабатываемого документа JSON и выдавать элемент SQL/JSON, поэтому использование такого выражения пути приведёт к ошибке. Чтобы получить такой же результат, как в нестрогом режиме, необходимо явно развернуть массив segments
:
strict $.track.segments[*].location
Оператор обращения .**
в нестрогом режиме может выдавать несколько неожиданные результаты. Например, следующий запрос выберет каждое значение HR
дважды:
lax $.**.HR
Это происходит потому, что оператор .**
выбирает и массив segments
, и каждый из его элементов, а обращение .HR
в нестрогом режиме автоматически разворачивает массивы. Во избежание подобных сюрпризов мы рекомендуем использовать оператор обращения .**
только в строгом режиме. Следующий запрос выбирает каждое значение HR
в единственном экземпляре:
strict $.**.HR
9.16.2.2. Операторы и методы SQL/JSON
В Таблице 9.49 показаны операторы и методы, поддерживаемые в значениях jsonpath
. Заметьте, что унарные операторы и методы могут применяться к множеству значений, полученных на предыдущем шаге пути, тогда как бинарные операторы (сложение и т. п.) применяются только к отдельным значениям.
Таблица 9.49. Операторы и методы jsonpath
Оператор/Метод Описание Пример(ы) |
---|
Сложение
|
Унарный плюс (нет операции); в отличие от сложения, он может итерационно применяться к множеству значений
|
Вычитание
|
Смена знака; в отличие от вычитания, этот оператор может итерационно применяться к множеству значений
|
Умножение
|
Деление
|
Остаток от деления
|
Тип элемента JSON (см.
|
Размер элемента JSON (число элементов в массиве либо 1, если это не массив)
|
Приблизительное число с плавающей точкой, преобразованное из строки или числа JSON
|
Ближайшее целое, большее или равное заданному числу
|
Ближайшее целое, меньшее или равное заданному числу
|
Модуль заданного числа (абсолютное значение)
|
Значение даты/времени, полученное из строки
|
Значение даты/времени, преобразованное из строки по шаблону
|
Пары ключ-значение, представленные в виде массива объектов со следующими тремя полями:
|
Примечание
Результирующим типом методов datetime()
и datetime(
может быть шаблон
)date
, timetz
, time
, timestamptz
или timestamp
. Эти два метода определяют тип своего результата автоматически.
Метод datetime()
пытается последовательно сопоставить поступившую на вход строку с ISO-форматами типов date
, timetz
, time
, timestamptz
и timestamp
. Встретив первый подходящий формат, он останавливается и возвращает соответствующий тип данных.
Метод datetime(
определяет результирующий тип в соответствии с полями заданного шаблона.шаблон
)
Методы datetime()
и datetime(
применяют те же правила разбора строки, что и SQL-функция шаблон
)to_timestamp
(см. Раздел 9.8), но с тремя исключениями. Во-первых, эти методы не позволяют использовать в шаблоне поля, которым не находится соответствие. Во-вторых, в шаблоне допускаются только следующие разделители: знак минуса, точка, косая черта, запятая, апостроф, точка с запятой, запятая и пробел. В-третьих, разделители в шаблоне должны в точности соответствовать входной строке.
Если требуется сравнить значения разных типов даты/времени, применяется неявное приведение типа. Значение date
может быть приведено к типу timestamp
или timestamptz
; timestamp
— к типу timestamptz
, а time
— к timetz
. Однако все эти приведения, кроме первого, зависят от текущего значения TimeZone и поэтому не могут выполняться в функциях jsonpath
, не учитывающих часовой пояс.
В Таблице 9.50 перечислены допустимые элементы выражения фильтра.
Таблица 9.50. Элементы выражения фильтра jsonpath
Предикат/значение Описание Пример(ы) |
---|
Проверка равенства (все операторы сравнения, включая этот, работают с любыми скалярными значениями JSON)
|
Проверка неравенства
|
Проверка «меньше»
|
Проверка «меньше или равно»
|
Проверка «больше»
|
Проверка «больше или равно»
|
JSON-константа
|
JSON-константа
|
JSON-константа
|
Логическое И
|
Логическое ИЛИ
|
Логическое НЕ
|
Проверяет, является ли
|
Проверяет, соответствует ли первый операнд регулярному выражению, которое задаёт второй операнд с необязательным аргументом
|
Проверяет, является ли второй операнд начальной подстрокой первого.
|
Проверяет, соответствует ли выражению пути минимум один элемент SQL/JSON. Возвращает
|
9.16.2.3. Регулярные выражения SQL/JSON
Выражения путей SQL/JSON могут содержать фильтры like_regex
, позволяющие сопоставлять текст с регулярным выражением. Например, следующий запрос пути SQL/JSON выберет все строки в массиве, которые начинаются с английской гласной в любом регистре:
$[*] ? (@ like_regex "^[aeiou]" flag "i")
Необязательная строка flag
может содержать один или несколько следующих символов: i
, делающий поиск регистронезависимым, m
, допускающий сопоставление ^
и $
с переводами строк, s
, допускающий сопоставление .
с символом новой строки, и q
, берущий в кавычки весь шаблон (в результате производится простой поиск подстроки).
Стандарт SQL/JSON заимствует определение регулярных выражений от оператора LIKE_REGEX
, который, в свою очередь, реализуется по стандарту XQuery. Однако в PostgreSQL оператор LIKE_REGEX
в настоящее время отсутствует. Поэтому фильтр like_regex
реализован с использованием механизма регулярных выражений POSIX, который описан в Подразделе 9.7.3. Вследствие этого наблюдается ряд небольших отклонений от описанного в стандарте поведения SQL/JSON, о которых рассказывается в Подразделе 9.7.3.8. Заметьте однако, что описанная там несовместимость букв флагов не проявляется на уровне SQL/JSON, так как заданные в SQL/JSON флаги XQuery переводятся во флаги, воспринимаемые механизмом POSIX.
Помните, что аргумент, задающий шаблон для like_regex
, является строкой пути JSON и записывается по правилам, описанным в Подразделе 8.14.7. Это в частности означает, что каждую косую черту в регулярном выражении надо дублировать. Например, чтобы отобрать строковые значения, находящиеся на уровне корня документа и содержащие только цифры, нужно написать:
$.* ? (@ like_regex "^\\d+$")