[Перевод] JSON и PostgreSQL 9.5: с еще более мощными инструментами

PostgreSQL 9.5 представил новый функционал, связанный с JSONB, значительно усиливающий его уже имеющиеся NoSQL характеристики. С добавлением новых операторов и функций, теперь стало возможно с легкостью изменять данные, хранящиеся в JSONB формате. В этой статье будут представлены эти новые операторы с примерами, как им можно использовать.

С добавлением типа данных 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» базы данных в Вашем случае, я рекомендую ознакомится со следующими статьями, посвященными вышеперечисленным темам (названия статей и авторы оставлены в оригинале):


Новые 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 особенностями, может дать Вам преимущество: можно использовать оба варианта (и документоориентированную, и реляционную базы данных), предоставленные одним и тем же решением, избегая всех сложностей использования двух различных продуктов.

© Habrahabr.ru