С добавлением типа данных JSON в версии 9.2, PostgreSQL наконец-то начал поддерживать JSON нативно. Несмотря на то что с выходом этой версии стало возможно использовать PostgreSQL как «NoSQL» базу данных, не так много можно было сделать на самом деле в то время из-за нехватки операторов и интересных функций. С момента выхода 9.2 версии, поддержка JSON значительно улучшалась в каждой следующей версии PostgreSQL, выливаясь сегодня в полное преодоление изначальных ограничений.
Вероятно, наиболее запоминающимися изменениями были добавление типа данных JSONB в PostgreSQL 9.4 и, в нынешней версии PostgreSQL 9.5, представление новых операторов и функций, которые позволят Вам изменять и управлять JSONB данными.
В этой статье мы сосредоточим внимание на новых возможностях, принесенных Postgres 9.5. Однако, прежде чем погрузиться в эту тему, если Вы хотите узнать больше о различиях между JSON и JSONB типами данных, или если у Вас есть сомнения по поводу уместности использования «NoSQL» базы данных в Вашем случае, я рекомендую ознакомится со следующими статьями, посвященными вышеперечисленным темам (названия статей и авторы оставлены в оригинале):
- NoSQL with PostgreSQL 9.4 and JSONB by Giuseppe Broccolo
- JSONB type performance in PostgreSQL 9.4 by Marco Nenciarini
- PostgreSQL anti-patterns: Unnecessary json/hstore dynamic columns by Craig Ringer
Новые JSONB операторы
Операторы и функции, присутствовавшие в PostgreSQL до версии 9.4, позволяли только извлекать JSONB данные. Поэтому, чтобы изменить эти данные, приходилось извлекать их, изменять, затем повторно вставлять их в базу. Не слишком практично, некоторые сказали бы.
Новые операторы представленные в PostgreSQL 9.5, которые были основаны на jsonbx расширении для PostgreSQL 9.4, смогли это изменить, значительно улучшая возможности взаимодействия с JSONB данными.
Конкатинация при помощи ||
Теперь Вы можете конкатинировать два JSONB объекта используя оператор ||:
SELECT
'{"name": "Marie",
"age": 45}'::jsonb || '{"city": "Paris"}'::jsonb;
?column?
----------------------------------------------
{"age": 45, "name": "Marie", "city": "Paris"}
(1 row)
В данном примере, ключ town добавлен к первому JSONB объекту.
Кроме того, данный оператор может быть использован для перезаписи уже имеющихся значений:
SELECT
'{"city": "Niceland",
"population": 1000}'::jsonb || '{"population": 9999}'::jsonb;
?column?
-------------------------------------------
{"city": "Niceland", "population": 9999}
(1 row)
В данном случае, значение ключа population было переписано на значение из второго объекта.
Удаление при помощи -
Оператор - может удалить пару ключ/значение из JSONB объекта:
SELECT
'{"name": "Karina",
"email": "karina@localhost"}'::jsonb - 'email';
?column?
-------------------
{"name": "Karina"}
(1 row)
Как Вы можете видеть, ключ email, указанный оператором -, был удален из объекта.
Кроме того, возможно удалить элемент из массива:
SELECT
'["animal","plant","mineral"]'::jsonb - 1;
?column?
-----------------
["animal", "mineral"]
(1 row)
Вышестоящий пример показывает массив, состоящий из 3 элементов. Зная что первый элемент массива соотносится с 0 позицией (animal), оператор - указывает на элемент, расположенный на позиции 1 и удаляет plant из массива.
Удаление при помощи #-
Разница в сравнении с оператором - заключается в том, что #- оператор может удалить вложенную пару ключ/значение, если путь до нее указан:
SELECT
'{"name": "Claudia",
"contact": {
"phone": "555-5555",
"fax": "111-1111"}}'::jsonb #- '{contact,fax}'::text[];
?column?
---------------------------------------------------------
{"name": "Claudia", "contact": {"phone": "555-5555"}}
(1 row)
Здесь, ключ fax вложен в contact. Мы используем оператор #- с указанием пути до ключа fax, чтобы удалить его.
Новые JSONB функции
Для большей мощности при работе с JSONB данными, вместо только их удаления и перезаписи, мы теперь можем использовать новую JSONB функцию:
jsonb_set
Новая функция обработки jsonb_set позволяет изменять значение для специфического ключа:
SELECT
jsonb_set(
'{"name": "Mary",
"contact":
{"phone": "555-5555",
"fax": "111-1111"}}'::jsonb,
'{contact,phone}',
'"000-8888"'::jsonb,
false);
jsonb_replace
--------------------------------------------------------------------------------
{"name": "Mary", "contact": {"fax": "111-1111", "phone": "000-8888"}}
(1 row)
Гораздо проще понять вышестоящий пример зная структуру jsonb_set функции. Она имеет 4 аргумента:
- target jsonb: значение JSONB, которое должно быть изменено
- path text[]: путь до интересующего значения, представленный в виде текстового массива
- new_value jsonb: новая связка ключ/значение, которая должна быть изменена (или добавлена)
- create_missing boolean: Опционное поле, которое позволяет создание новой связки ключ/значение, если она еще не существует
Оглядываясь на предыдущий пример, на этот раз понимая его структуру, мы видим что вложенный в contact ключ phone был изменен функцией jsonb_set.
Вот еще один пример, на этот раз создающий новый ключ посредством использования логического значения true (4й аргумент в структуре функции jsonb_set). Как говорилось выше, этот аргумент имеет значение true по-умолчанию, так что не обязательно его указывать явно в следующем примере:
SELECT
jsonb_set(
'{"name": "Mary",
"contact":
{"phone": "555-5555",
"fax": "111-1111"}}'::jsonb,
'{contact,skype}',
'"maryskype"'::jsonb,
true);
jsonb_set
------------------------------------------------------------------------------------------------------
{"name": "Mary", "contact": {"fax": "111-1111", "phone": "555-5555", "skype": "maryskype"}}
(1 row)
Связка ключ/значение skype, который не присутствует в оригинальном JSONB объекте, был добавлен и находится на том уровне вложенности, который был указан во втором аргументе jsonb_set функции.
Если же вместо true в 4й аргумент функции jsonb_set поставить false, то ключ skype не будет добавлен в исходный JSONB объект.
jsonb_pretty
Чтение JSONB записи не так то уж просто, учитывая что она не хранит пробелы. jsonb_pretty функция форматирует вывод, делая его более легким для чтения:
SELECT
jsonb_pretty(
jsonb_set(
'{"name": "Joan",
"contact": {
"phone": "555-5555",
"fax": "111-1111"}}'::jsonb,
'{contact,phone}',
'"000-1234"'::jsonb));
jsonb_pretty
---------------------------------
{ +
"name": "Joan", +
"contact": { +
"fax": "111-1111", +
"phone": "000-1234" +
} +
}
(1 row)
Снова, в этом примере, значение вложенного в contact ключа phone изменено на значение, переданное в 3м аргументе функции jsonb_set. Единственная разница заключается в том, что мы использовали теперь ее вместе с функцией jsonb_pretty, вывод теперь показан в более понятном и читаемом виде.
Заключение
Вопреки тому что нам пытаются доказать, что нереляционная база данных не может быть универсальным решением, далеко не все с этим согласятся.
Поэтому, говоря о «NoSQL» базах данных, нужно иметь в голове мысли о том, подойдет ли Вам такая база лучше, чем реляционная. PostgreSQL, с его JSONB особенностями, может дать Вам преимущество: можно использовать оба варианта (и документоориентированную, и реляционную базы данных), предоставленные одним и тем же решением, избегая всех сложностей использования двух различных продуктов.
Комментарии (0)