PostgreSQL
 sql >> Base de Dados >  >> RDS >> PostgreSQL

Uma visão geral dos recursos JSON no PostgreSQL

O que é JSON?


JSON significa “JavaScript Object Notation”, que é um tipo de formato de dados popularmente usado por aplicativos da web. Isso significa que os dados seriam transmitidos entre aplicativos da Web e servidores nesse formato. O JSON foi introduzido como uma alternativa ao formato XML. Nos “bons velhos tempos”, os dados costumavam ser transmitidos no formato XML, que é um tipo de dados pesado comparado ao JSON. Abaixo está um exemplo de string formatada em JSON:
{ "ID":"001","name": "Ven", "Country": "Australia",  "city": "Sydney", "Job Title":"Database Consultant"}

Uma string JSON pode conter outro objeto JSON dentro de si, como mostrado abaixo:
{ "ID":"001", "name": "Ven", "Job Title":"Database Consultant", "Location":{"Suburb":"Dee Why","city": "Sydney","State":"NSW","Country": "Australia"}}

Os aplicativos móveis e da Web modernos geram principalmente os dados no formato JSON, também denominados “JSON Bytes”, que são coletados pelos servidores de aplicativos e enviados para o banco de dados. Os bytes JSON são processados ​​por sua vez, divididos em valores de coluna separados e inseridos em uma tabela RDBMS.
Exemplo:
{ "ID":"001","name": "Ven", "Country": "Australia",  "city": "Sydney", "Job Title":"Database Consultant"}

Os dados JSON acima são convertidos em um SQL como abaixo.
Insert into test (id, name, country,city,job_title) values  (001,'Ven','Australia','Sydney','Database Consultant');

Quando se trata de armazenar e processar os dados JSON, existem vários bancos de dados NoSQL que o suportam e o mais popular é o MongoDB. Quando se trata de bancos de dados RDBMS, até recentemente, as strings JSON eram tratadas como texto normal e não havia tipos de dados que reconhecessem, armazenassem ou processassem especificamente as strings do formato JSON. PostgreSQL, o banco de dados RDBMS de código aberto mais popular, surgiu com o tipo de dados JSON, que se mostrou altamente benéfico para desempenho, funcionalidade e escalabilidade quando se trata de lidar com dados JSON.

PostgreSQL + JSON


O banco de dados PostgreSQL tornou-se cada vez mais popular desde que o tipo de dados JSON foi introduzido. Na verdade, o PostgreSQL tem superado o MongoDB quando se trata de processar uma grande quantidade de dados JSON. Os aplicativos podem armazenar strings JSON no banco de dados PostgreSQL no formato JSON padrão. Os desenvolvedores só precisam dizer ao aplicativo para enviar as strings JSON para o banco de dados como um tipo de dados json e recuperar no formato JSON. O armazenamento de string JSON no tipo de dados JSON tem várias vantagens em comparação com o armazenamento do mesmo no tipo de dados TEXT. O tipo de dados JSON pode aceitar apenas strings formatadas em JSON válidas, se a string não estiver no formato JSON correto, um erro será gerado. O tipo de dados JSON ajuda o aplicativo a realizar pesquisas eficientes e baseadas em índice que veremos em detalhes em breve.

O tipo de dados JSON foi introduzido no post do PostgreSQL-9.2, onde foram feitas melhorias significativas. A maior adição surgiu no PostgreSQL-9.4 com a adição do tipo de dados JSONB. JSONB é uma versão avançada do tipo de dados JSON que armazena os dados JSON em formato binário. Este é o principal aprimoramento que fez uma grande diferença na maneira como os dados JSON eram pesquisados ​​e processados ​​no PostgreSQL. Vamos dar uma olhada detalhada nas vantagens dos tipos de dados JSON.

Tipos de dados JSON e JSONB


O tipo de dados JSON armazena strings formatadas em json como um texto que não é muito poderoso e não suporta muitas funções relacionadas a JSON usadas para pesquisas. Ele oferece suporte apenas à indexação B-TREE tradicional e não oferece suporte a outros tipos de índice que são imperativos para operações de pesquisa mais rápidas e eficientes em dados JSON.

JSONB, a versão avançada do tipo de dados JSON, é altamente recomendado para armazenar e processar documentos JSON. Ele suporta uma ampla variedade de operadores json e tem inúmeras vantagens sobre o JSON, como armazenar strings formatadas em JSON em formato binário e suportar funções e indexação JSON para realizar pesquisas eficientes.

Vejamos as diferenças.
  JSON JSONB
1 Praticamente parecido com um tipo de dados TEXT que armazena apenas documentos JSON válidos. Armazena os documentos JSON em formato binário.
2 Armazena os documentos JSON como estão, incluindo espaços em branco. Recorta os espaços em branco e armazena em um formato propício para pesquisas mais rápidas e eficientes
3 Não suporta indexação FULL-TEXT-SEARCH Suporta indexação de PESQUISA DE TEXTO COMPLETO
4 Não suporta uma ampla variedade de funções e operadores JSON Suporta todas as funções e operadores JSON

Exemplo do nº 4 listado acima

JSON


Abaixo está uma tabela com o tipo de dados JSON
dbt3=# \d product
                   Table "dbt3.product"
     Column     |  Type  | Collation | Nullable | Default
----------------+--------+-----------+----------+---------
 item_code      | bigint |           | not null |
 productdetails | json   |           |          |
Indexes:
    "product_pkey" PRIMARY KEY, btree (item_code)

Não suporta operadores JSON tradicionais (como “@>” ou “#>”). A pesquisa de texto completo por meio de dados JSON é feita usando “@>” ou “#>” em um SQL que não é suportado pelo tipo de dados JSON
dbt3=# select * from product where productdetails @> '{"l_shipmode":"AIR"}' and productdetails @> '{"l_quantity":"27"}';
ERROR:  operator does not exist: json @> unknown
LINE 1: select * from product where productdetails @> '{"l_shipmode"...
                                                   ^
HINT:  No operator matches the given name and argument types. You might need to add explicit type casts.
dbt3=#

JSONB


Abaixo está uma tabela com o tipo de dados JSONB
dbt3=# \d products
                  Table "dbt3.products"
    Column     |  Type  | Collation | Nullable | Default
---------------+--------+-----------+----------+---------
 item_code     | bigint |           | not null |
 order_details | jsonb  |           |          |
Indexes:
    "products_pkey" PRIMARY KEY, btree (item_code)

Suporta FULL-TEXT-SEARCHING através de dados JSON usando operadores (como “@>”)
dbt3=# select * from products where order_details @> '{"l_shipmode" : "AIR"}' limit 2;
 item_code |                                                                                        order_details
-----------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
         4 | {"l_partkey": 21315, "l_orderkey": 1, "l_quantity": 28, "l_shipdate": "1996-04-21", "l_shipmode": "AIR", "l_commitdate": "1996-03-30", "l_shipinstruct": "NONE", "l_extendedprice": 34616.7}
         8 | {"l_partkey": 42970, "l_orderkey": 3, "l_quantity": 45, "l_shipdate": "1994-02-02", "l_shipmode": "AIR", "l_commitdate": "1994-01-04", "l_shipinstruct": "NONE", "l_extendedprice": 86083.6}
(2 rows)
Baixe o whitepaper hoje PostgreSQL Management &Automation with ClusterControlSaiba o que você precisa saber para implantar, monitorar, gerenciar e dimensionar o PostgreSQLBaixe o whitepaper

Como consultar dados JSON


Vamos dar uma olhada em alguns recursos JSON do PostgreSQL relacionados às operações de dados Abaixo está a aparência dos dados JSON em uma tabela. A coluna “order_details” é do tipo JSONB
dbt3=# select * from product_details ;
 item_code |                                                                                                 order_details
-----------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
         1 | {"l_partkey": 1551894, "l_orderkey": 1, "l_quantity": 17, "l_shipdate": "1996-03-13", "l_shipmode": "TRUCK", "l_commitdate": "1996-02-12", "l_shipinstruct": "DELIVER IN PERSON", "l_extendedprice": 33078.9}
         2 | {"l_partkey": 673091, "l_orderkey": 1, "l_quantity": 36, "l_shipdate": "1996-04-12", "l_shipmode": "MAIL", "l_commitdate": "1996-02-28", "l_shipinstruct": "TAKE BACK RETURN", "l_extendedprice": 38306.2}
         3 | {"l_partkey": 636998, "l_orderkey": 1, "l_quantity": 8, "l_shipdate": "1996-01-29", "l_shipmode": "REG AIR", "l_commitdate": "1996-03-05", "l_shipinstruct": "TAKE BACK RETURN", "l_extendedprice": 15479.7}
         4 | {"l_partkey": 21315, "l_orderkey": 1, "l_quantity": 28, "l_shipdate": "1996-04-21", "l_shipmode": "AIR", "l_commitdate": "1996-03-30", "l_shipinstruct": "NONE", "l_extendedprice": 34616.7}
         5 | {"l_partkey": 240267, "l_orderkey": 1, "l_quantity": 24, "l_shipdate": "1996-03-30", "l_shipmode": "FOB", "l_commitdate": "1996-03-14", "l_shipinstruct": "NONE", "l_extendedprice": 28974}
         6 | {"l_partkey": 156345, "l_orderkey": 1, "l_quantity": 32, "l_shipdate": "1996-01-30", "l_shipmode": "MAIL", "l_commitdate": "1996-02-07", "l_shipinstruct": "DELIVER IN PERSON", "l_extendedprice": 44842.9}
         7 | {"l_partkey": 1061698, "l_orderkey": 2, "l_quantity": 38, "l_shipdate": "1997-01-28", "l_shipmode": "RAIL", "l_commitdate": "1997-01-14", "l_shipinstruct": "TAKE BACK RETURN", "l_extendedprice": 63066.3}
         8 | {"l_partkey": 42970, "l_orderkey": 3, "l_quantity": 45, "l_shipdate": "1994-02-02", "l_shipmode": "AIR", "l_commitdate": "1994-01-04", "l_shipinstruct": "NONE", "l_extendedprice": 86083.6}
         9 | {"l_partkey": 190355, "l_orderkey": 3, "l_quantity": 49, "l_shipdate": "1993-11-09", "l_shipmode": "RAIL", "l_commitdate": "1993-12-20", "l_shipinstruct": "TAKE BACK RETURN", "l_extendedprice": 70822.1}
        10 | {"l_partkey": 1284483, "l_orderkey": 3, "l_quantity": 27, "l_shipdate": "1994-01-16", "l_shipmode": "SHIP", "l_commitdate": "1993-11-22", "l_shipinstruct": "DELIVER IN PERSON", "l_extendedprice": 39620.3}
(10 rows)

Selecione todos os códigos de item, incluindo as datas de envio
dbt3=# select item_code, order_details->'l_shipdate' as shipment_date from product_details ;

 item_code | shipment_date
-----------+---------------
         1 | "1996-03-13"
         2 | "1996-04-12"
         3 | "1996-01-29"
         4 | "1996-04-21"
         5 | "1996-03-30"
         6 | "1996-01-30"
         7 | "1997-01-28"
         8 | "1994-02-02"
         9 | "1993-11-09"
        10 | "1994-01-16"
(10 rows)

Obtenha o item_code, quantidade e preço de todos os pedidos que chegaram por via aérea
dbt3=# select item_code, order_details->'l_quantity' as quantity, order_details->'l_extendedprice' as price, order_details->'l_shipmode' as price from product_details where order_details->>'l_shipmode'='AIR';

 item_code | quantity |  price  | price
-----------+----------+---------+-------
         4 | 28       | 34616.7 | "AIR"
         8 | 45       | 86083.6 | "AIR"
(2 rows)

Os operadores JSON “->” e “->>” são usados ​​para seleções e comparações na consulta SQL. O operador “->” retorna o campo Objeto JSON como um campo entre aspas e o operador “->>” retorna o campo objeto JSON como TEXTO. Os dois SQLs acima são exemplos de exibição de valores de campo JSON no estado em que se encontram. Abaixo está o exemplo de extração do campo JSON no formulário TEXT.
Abaixo está um exemplo de busca do campo JSON no formulário TEXT
dbt3=# select item_code, order_details->>'l_shipdate' as shipment_date from product_details ;
 item_code | shipment_date
-----------+---------------
         1 | 1996-03-13
         2 | 1996-04-12
         3 | 1996-01-29
         4 | 1996-04-21
         5 | 1996-03-30
         6 | 1996-01-30
         7 | 1997-01-28
         8 | 1994-02-02
         9 | 1993-11-09
        10 | 1994-01-16
(10 rows)

Existe outro operador chamado “#>” que é usado para consultar a parte de dados de um elemento JSON que, por sua vez, é parte de uma string JSON. Vejamos um exemplo.
Abaixo estão os dados da tabela.
dbt3=# select * from test_json ;
  id   |                                                                                                details
-------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 10000 | {"Job": "Database Consultant", "name": "Venkata", "Location": {"city": "Sydney", "State": "NSW", "Suburb": "Dee Why", "Country": "Australia"}}
 20000 | {"Job": "Database Consultant", "name": "Smith", "Location": {"city": "Sydney", "State": "NSW", "Suburb": "Manly", "Country": "Australia"}}
 30000 | {"Job": "Developer", "name": "John", "Location": {"city": "Sydney", "State": "NSW", "Suburb": "Brookvale", "Country": "Australia"}}
 50000 | {"cars": {"Ford": [{"doors": 4, "model": "Taurus"}, {"doors": 4, "model": "Escort"}], "Nissan": [{"doors": 4, "model": "Sentra"}, {"doors": 4, "model": "Maxima"}, {"doors": 2, "model": "Skyline"}]}}
 40000 | {"Job": "Architect", "name": "James", "Location": {"city": "Melbourne", "State": "NSW", "Suburb": "Trugnania", "Country": "Australia"}}

Quero ver todos os detalhes com “State” “NSW” e “State” é a chave do objeto JSON que faz parte da chave “Location”. Abaixo está como consultar o mesmo.
dbt3=# select * from test_json where details #> '{Location,State}'='"NSW"';
  id   |                                                                    details
-------+------------------------------------------------------------------------------------------------------------------------------------------------
 10000 | {"Job": "Database Consultant", "name": "Venkata", "Location": {"city": "Sydney", "State": "NSW", "Suburb": "Dee Why", "Country": "Australia"}}
 20000 | {"Job": "Database Consultant", "name": "Smith", "Location": {"city": "Sydney", "State": "NSW", "Suburb": "Manly", "Country": "Australia"}}
 30000 | {"Job": "Developer", "name": "John", "Location": {"city": "Sydney", "State": "NSW", "Suburb": "Brookvale", "Country": "Australia"}}
 30000 | {"Job": "Architect", "name": "James", "Location": {"city": "Melbourne", "State": "NSW", "Suburb": "Trugnania", "Country": "Australia"}}
(4 rows)

As operações aritméticas podem ser executadas em dados JSON. A conversão de tipo é necessária, pois a parte de dados da coluna JSON é TEXT.
dbt3=# select item_code, order_details->'l_quantity' as quantity, order_details->'l_extendedprice' as price, order_details->'l_shipmode' as price from product_details where (order_details->'l_quantity')::int > 10;
 item_code | quantity |  price  |  price
-----------+----------+---------+---------
         1 | 17       | 33078.9 | "TRUCK"
         2 | 36       | 38306.2 | "MAIL"
         4 | 28       | 34616.7 | "AIR"
         5 | 24       | 28974   | "FOB"
         6 | 32       | 44842.9 | "MAIL"
         7 | 38       | 63066.3 | "RAIL"
         8 | 45       | 86083.6 | "AIR"
         9 | 49       | 70822.1 | "RAIL"
        10 | 27       | 39620.3 | "SHIP"
(9 rows)

Além de todos os itens acima, as seguintes operações também podem ser executadas em JSON usando SQLs, incluindo JOINs
  1. Classificando os dados usando a cláusula ORDER BY
  2. Agregação usando funções agregadas como SUM, AVG, MIN, MAX etc
  3. Agrupe os dados usando a cláusula GROUP BY

E quanto ao desempenho?


Os dados nas colunas JSON serão de natureza textual e, com base no tamanho dos dados, podem ser esperados problemas de desempenho. As pesquisas por meio de dados JSON podem levar tempo e poder de computação, resultando em respostas lentas ao(s) aplicativo(s). É imperativo que os DBAs garantam que os SQLs que atingem as colunas JSON estejam respondendo com rapidez suficiente e apresentando um bom desempenho. Como a extração de dados é feita via SQL, a opção que os DBAs procurariam é a possibilidade de indexação e sim, os tipos de dados JSON suportam opções de indexação.

Vamos dar uma olhada nas opções de indexação que o JSON nos traz.

Indexação de JSONB


O tipo de dados JSONB suporta indexação FULL-TEXT-SEARCH. Esse é o recurso mais importante do JSONB que os DBAs esperam ao usar tipos de dados JSONB. Um índice normal em uma chave de objeto JSON pode não ajudar ao usar operadores específicos de JSON nas consultas de pesquisa. Abaixo está uma consulta TEXT SEARCH que vai para um FULL-TABLE-SCAN
dbt3=# explain select * from products where order_details @> '{"l_shipmode" : "AIR"}';
                             QUERY PLAN
--------------------------------------------------------------------
 Seq Scan on products  (cost=0.00..4205822.65 rows=59986 width=252)
   Filter: (order_details @> '{"l_shipmode": "AIR"}'::jsonb)
(2 rows)

JSONB suporta o tipo de índice FULL-TEXT-SEARCH chamado GIN, que ajuda nas consultas acima.
Agora, deixe-me criar um índice GIN e ver se isso ajuda
dbt3=# create index od_gin_idx on products using gin(order_details jsonb_path_ops);
CREATE INDEX

Se você pode observar abaixo, a consulta capta o índice GIN
dbt3=# explain select * from products where order_details @> '{"l_shipmode" : "AIR"}';
                                  QUERY PLAN
-------------------------------------------------------------------------------
 Bitmap Heap Scan on products  (cost=576.89..215803.18 rows=59986 width=252)
   Recheck Cond: (order_details @> '{"l_shipmode": "AIR"}'::jsonb)
   ->  Bitmap Index Scan on od_gin_idx  (cost=0.00..561.90 rows=59986 width=0)
         Index Cond: (order_details @> '{"l_shipmode": "AIR"}'::jsonb)

E um índice B-TREE em vez de GIN NÃO ajudaria
dbt3=# create index idx on products((order_details->>'l_shipmode'));
CREATE INDEX

dbt3=# \d products
                  Table "dbt3.products"
    Column     |  Type  | Collation | Nullable | Default
---------------+--------+-----------+----------+---------
 item_code     | bigint |           | not null |
 order_details | jsonb  |           |          |
Indexes:
    "products_pkey" PRIMARY KEY, btree (item_code)
    "idx" btree ((order_details ->> 'l_shipmode'::text))

Você pode ver abaixo, a consulta prefere FULL-TABLE-SCAN
dbt3=# explain select * from products where order_details @> '{"l_shipmode" : "AIR"}';
                             QUERY PLAN
--------------------------------------------------------------------
 Seq Scan on products  (cost=0.00..4205822.65 rows=59986 width=252)
   Filter: (order_details @> '{"l_shipmode": "AIR"}'::jsonb)

O que é Índice GIN?


GIN significa Índice Invertido Generalizado. A principal capacidade do GIN Index é acelerar pesquisas de texto completo. Ao realizar pesquisas com base em Chaves ou elementos específicos em um TEXTO ou documento, o Índice GIN é o caminho a percorrer. O índice GIN armazena os pares “Chave” (ou um elemento ou valor) e a “lista de posições”. A lista de posições é o rowID da chave. Isso significa que, se a “Chave” ocorrer em vários lugares no documento, o Índice GIN armazena a Chave apenas uma vez junto com sua posição de ocorrências, o que não apenas mantém o Índice GIN compacto em tamanho, como também ajuda a acelerar as pesquisas em um ótimo maneira. Este é o aprimoramento no Postgres-9.4.

Desafios com o índice GIN


Dependendo da complexidade dos dados, a manutenção de índices GIN pode ser cara. A criação de Índices GIN consome tempo e recursos, pois o Índice precisa pesquisar todo o documento para encontrar as Chaves e seus IDs de linha. Pode ser ainda mais desafiador se o índice GIN estiver inchado. Além disso, o tamanho do índice GIN pode ser muito grande com base no tamanho e na complexidade dos dados.

Indexação de JSON


JSON não suporta pesquisa de texto e índices como GIN
dbt3=# create index pd_gin_idx on product using gin(productdetails jsonb_path_ops);
ERROR:  operator class "jsonb_path_ops" does not accept data type json

A indexação normal como B-TREE é compatível com JSON e JSONB


Sim, índices normais como o Índice B-TREE são suportados pelos tipos de dados JSON e JSONB e não são propícios para operações de pesquisa de texto. Cada chave de objeto JSON pode ser indexada individualmente, o que realmente ajudaria APENAS quando a mesma chave de objeto for usada na cláusula WHERE.
Deixe-me criar um índice B-TREE em JSONB e ver como funciona
dbt3=# create index idx on products((order_details->>'l_shipmode'));
CREATE INDEX

dbt3=# \d products
                  Table "dbt3.products"
    Column     |  Type  | Collation | Nullable | Default
---------------+--------+-----------+----------+---------
 item_code     | bigint |           | not null |
 order_details | jsonb  |           |          |
Indexes:
    "products_pkey" PRIMARY KEY, btree (item_code)
    "idx" btree ((order_details ->> 'l_shipmode'::text))

Já aprendemos acima que um índice B-TREE NÃO é útil para acelerar SQLs fazendo FULL-TEXT-SEARCHING nos dados JSON usando operadores (como “@>”) , e esses índices SOMENTE ajudariam a acelerar as consultas como o abaixo, que são SQLs típicos do tipo RDBMS (que não são consultas de pesquisa). Cada chave de objeto JSON pode ser indexada individualmente, o que ajudaria a acelerar as consultas quando essas chaves de objeto JSON indexadas são usadas na cláusula WHERE.
O exemplo abaixo da consulta usa a chave de objeto “l_shipmode” na cláusula WHERE e desde é Indexado a consulta está indo para uma verificação de índice. Se você deseja pesquisar usando uma chave de objeto diferente, a consulta escolherá fazer um FULL-TABLE-SCAN.
dbt3=# explain select * from products where order_details->>'l_shipmode'='AIR';
                                   QUERY PLAN
---------------------------------------------------------------------------------
 Index Scan using idx on products  (cost=0.56..1158369.34 rows=299930 width=252)
   Index Cond: ((order_details ->> 'l_shipmode'::text) = 'AIR'::text)

O mesmo funciona com o tipo de dados JSON também
dbt3=# create index idx on products((order_details->>'l_shipmode'));
CREATE INDEX

dbt3=# \d products
                  Table "dbt3.products"
    Column     |  Type  | Collation | Nullable | Default
---------------+--------+-----------+----------+---------
 item_code     | bigint |           | not null |
 order_details | json  |           |          |
Indexes:
    "products_pkey" PRIMARY KEY, btree (item_code)
    "idx" btree ((order_details ->> 'l_shipmode'::text))

Se você puder observar, a consulta está usando o índice
dbt3=# explain select * from products where order_details->>'l_shipmode'='AIR';
                                   QUERY PLAN
---------------------------------------------------------------------------------
 Index Scan using idx on products  (cost=0.56..1158369.34 rows=299930 width=252)
   Index Cond: ((order_details ->> 'l_shipmode'::text) = 'AIR'::text)

Conclusão


Aqui estão algumas coisas a serem lembradas ao usar dados JSON do PostgreSQL...
  • O PostgreSQL é uma das melhores opções para armazenar e processar dados JSON
  • Com todos os recursos poderosos, o PostgreSQL pode ser seu banco de dados de documentos
  • Vi arquiteturas onde dois ou mais armazenamentos de dados são escolhidos, com uma mistura de bancos de dados PostgreSQL e NoSQL como MongoDB ou banco de dados Couchbase. Uma API REST ajudaria os aplicativos a enviar os dados para diferentes armazenamentos de dados. Com o PostgreSQL suportando JSON, essa complexidade na arquitetura pode ser evitada escolhendo apenas um armazenamento de dados.
  • Os dados JSON no PostgreSQL podem ser consultados e indexados, o que resulta em desempenho e escalabilidade incríveis
  • O tipo de dados JSONB é a opção preferida, pois é bom em armazenamento e desempenho. Totalmente suporta FULL-TEXT-SEARCHING e Indexação. Apresenta bom desempenho
  • Use o tipo de dados JSON apenas se quiser armazenar strings JSON como JSON e não estiver realizando pesquisas de texto muito complexas
  • A maior vantagem de ter JSON no PostgreSQL é que a busca pode ser feita usando SQLs
  • O desempenho da pesquisa JSON no PostgreSQL está no mesmo nível dos melhores bancos de dados NoSQL, como o MongoDB