O Hologres oferece suporte aos tipos de dados semiestruturados JSON e JSONB para armazenar e consultar dados flexíveis e sem esquema. Este tópico aborda as diferenças entre os dois tipos, operadores e funções compatíveis, indexação GIN e integração com o Realtime Compute for Apache Flink.
JSON vs. JSONB: como escolher o tipo adequado
Ambos os tipos armazenam dados formatados em JSON, mas diferem no formato de armazenamento e nas características de desempenho.
|
JSON |
JSONB |
|
|
Formato de armazenamento |
Texto simples |
Binário decomposto |
|
Velocidade de escrita |
Rápida |
Mais lenta (conversão adicional na escrita) |
|
Velocidade de leitura |
Mais lenta (reanálise a cada consulta) |
Mais rápida (sem necessidade de reanálise) |
|
Preservação exata do texto de entrada |
Sim — espaços, ordem das chaves e chaves duplicadas são mantidos |
Não — espaços removidos, chaves desduplicadas (prevalece o último valor), ordem das chaves não preservada |
|
Suporte a índices GIN |
Não |
Sim (Hologres V1.1+) |
|
Suporte a armazenamento colunar |
Não |
Sim (Hologres V1.3+) |
Recomendação: utilize JSONB para a maioria das cargas de trabalho. O JSONB oferece consultas mais rápidas, suporte a índices GIN e armazenamento colunar. Use JSON apenas quando for necessário preservar o texto exato de entrada, incluindo a ordem das chaves, espaços e chaves duplicadas.
Comportamento de chaves duplicadas: quando a mesma chave aparece múltiplas vezes em um valor de entrada, o JSON retém todos os pares chave-valor (funções de processamento consideram o último valor como autoritativo), enquanto o JSONB mantém apenas o último valor.
Limites
|
Restrição |
Detalhe |
|
Tipo de dado JSON |
Requer Hologres V0.9 ou posterior. |
|
Índices GIN para JSONB |
Requer Hologres V1.1 ou posterior. |
|
Armazenamento colunar para JSONB |
Requer Hologres V1.3 ou posterior. Compatível apenas com tabelas orientadas a colunas; tabelas orientadas a linhas não têm suporte. O armazenamento colunar é ativado somente quando a tabela possui 1.000 registros ou mais. |
|
Índices GIN + armazenamento colunar |
Mutuamente exclusivos a partir do Hologres V3.0.42 e V3.1.10. Quando ambos estão habilitados, os índices GIN não têm efeito. Utilize armazenamento colunar para campos não esparsos. |
|
Funções sem suporte |
|
Alternativa para json_extract_path e jsonb_extract_path: utilize o operador de caminho #>.
-- json_extract_path equivalent
SELECT '{"key":{"key1":"key1","key2":"key2"}}'::json #> '{"key","key1"}';
-- jsonb_extract_path equivalent
SELECT '{"key":{"key1":"key1","key2":"key2"}}'::jsonb #> '{"key","key1"}';
Para obter mais informações sobre como atualizar sua instância, consulte Atualizações de instância.
Operadores
Operadores de uso comum
Os seis operadores a seguir funcionam tanto com JSON quanto com JSONB.
|
**Operador** |
**Tipo do operando direito** |
**Descrição** |
**Exemplo** |
**Resultado** |
|
|
int |
Obtém um elemento de array JSON por índice (base 0; valores negativos contam a partir do final). |
|
|
|
|
text |
Obtém um campo de objeto JSON pela chave. |
|
|
|
|
int |
Obtém um elemento de array JSON como texto. |
|
|
|
|
text |
Obtém um campo de objeto JSON como texto. |
|
|
|
|
text[] |
Obtém um objeto JSON no caminho especificado. |
|
|
|
|
text[] |
Obtém um objeto JSON no caminho especificado como texto. |
|
|
Operadores exclusivos do JSONB
Os operadores abaixo aplicam-se exclusivamente a dados JSONB e permitem verificações de contenção, testes de existência de chave, concatenação e exclusão.
|
**Operador** |
**Tipo do operando direito** |
**Descrição** |
**Exemplo** |
**Resultado** |
||||
|
|
jsonb |
Verifica se o valor à esquerda contém o caminho ou valor JSON à direita. |
|
|
||||
|
|
jsonb |
Verifica se o caminho ou valor JSON à esquerda está contido no valor à direita. |
|
|
||||
|
|
text |
Verifica se uma string de chave ou elemento existe no valor JSONB. |
|
|
||||
|
|
` |
text[] |
Verifica se alguma string de chave ou elemento no array existe no valor JSONB. |
|
array['b', 'c']` |
|
||
|
|
text[] |
Verifica se todas as strings do array existem no valor JSONB. |
|
|
||||
|
|
` |
jsonb |
Concatena dois valores JSONB. Para objetos com a mesma chave, o valor do operando direito é utilizado. Este operador não é recursivo. |
|
'["c", "d"]'::jsonb` |
|
||
|
|
text |
Exclui a chave ou valor correspondente do operando esquerdo. |
|
|
||||
|
|
text[] |
Exclui múltiplas chaves ou valores correspondentes do operando esquerdo. |
|
|
||||
|
|
integer |
Exclui o elemento do array na posição especificada (valores negativos contam a partir do final). Retorna um erro se o valor não for um array. |
|
|
||||
|
|
text[] |
Exclui o elemento no caminho especificado. Para arrays, inteiros negativos contam a partir do final. |
|
|
Funções
Funções de processamento
|
**Função** |
**Tipo de retorno** |
**Descrição** |
**Exemplo** |
**Resultado** |
||
|
|
int |
Retorna o número de elementos no array JSON mais externo. |
|
|
||
|
|
setof text |
Retorna o conjunto de chaves no objeto JSON mais externo. |
|
|
||
|
|
anyelement |
Expande o objeto em |
|
|
["2", "a b"] |
{"d": 4, "e": "a b c"}` |
|
|
setof anyelement |
Expande o array de objetos mais externo em |
|
|
2` |
4` |
|
|
setof json / setof jsonb |
Expande um array JSON em um conjunto de valores JSON. |
|
|
||
|
|
setof text |
Expande um array JSON em um conjunto de valores de texto. |
|
|
||
|
|
text |
Retorna o tipo de dado do valor JSON mais externo como texto. Valores possíveis: |
|
|
||
|
|
json / jsonb |
Remove todos os campos de objeto com valores nulos. Outros valores nulos são preservados. |
|
|
||
|
|
jsonb |
Substitui o valor em |
|
|
||
|
|
jsonb |
Insere |
|
|
||
|
|
text |
Retorna |
|
|
||
|
|
jsonb |
Agrega valores (incluindo nulos) em um array JSON. |
Veja o exemplo abaixo. |
— |
||
|
|
jsonb |
Agrega pares chave-valor em um objeto JSON. As chaves não podem ser nulas; os valores podem ser nulos. |
Veja o exemplo abaixo. |
— |
||
|
|
boolean |
Retorna |
Veja o exemplo abaixo. |
— |
Exemplo de jsonb_agg e jsonb_object_agg:
DROP TABLE IF EXISTS t;
CREATE TABLE t (
k int PRIMARY KEY,
class int NOT NULL,
v text NOT NULL
);
INSERT INTO t (k, class, v)
SELECT
(1 + s.v),
CASE (s.v) < 3
WHEN TRUE THEN 1
ELSE 2
END,
chr(97 + s.v)
FROM generate_series(0, 5) AS s (v);
SELECT
class,
jsonb_agg(v ORDER BY v DESC) FILTER (WHERE v <> 'b') AS "jsonb_agg",
jsonb_object_agg(v, k ORDER BY v DESC) FILTER (WHERE v <> 'e') AS "jsonb_object_agg(v, k)"
FROM t
GROUP BY class;
Resultado:
class | jsonb_agg | jsonb_object_agg(v, k)
-------+-----------------+--------------------------
1 | ["c", "a"] | {"a": 1, "b": 2, "c": 3}
2 | ["f", "e", "d"] | {"d": 4, "f": 6}
Exemplo de is_valid_json:
DROP TABLE IF EXISTS test_json;
CREATE TABLE test_json (
id int,
json_strings text
);
INSERT INTO test_json
VALUES (1, '{"a":2}'), (2, '{"a":{"b":{"c":1}}}'), (3, '{"a": [1,2,"b"]}');
INSERT INTO test_json
VALUES (4, '{{}}'), (5, '{1:"a"}'), (6, '[1,2,3]');
SELECT
id,
json_strings,
is_valid_json(json_strings)
FROM test_json
ORDER BY id;
Resultado:
id | json_strings | is_valid_json
----+---------------------+--------------
1 | {"a":2} | true
2 | {"a":{"b":{"c":1}}} | true
3 | {"a": [1,2,"b"]} | true
4 | {{}} | false
5 | {1:"a"} | false
6 | [1,2,3] | true
Funções de análise (parsing)
|
**Função** |
**Descrição** |
**Exemplo** |
**Resultado** |
|
|
Converte um valor de texto para JSONB. Retorna nulo se a entrada não for um JSONB válido. Requer Hologres V2.0.24 ou posterior. |
|
|
|
|
Retorna o valor como um objeto JSON. Arrays e tipos compostos são convertidos recursivamente. Valores escalares utilizam uma função de conversão (cast) se disponível; caso contrário, são representados como texto JSON. |
|
|
|
|
Retorna um array como um array JSON. Arrays multidimensionais produzem arrays JSON aninhados. Quando |
|
|
|
|
Constrói um array JSON a partir de uma lista variável de argumentos. Os argumentos podem ser de tipos diferentes. |
|
|
|
|
Constrói um objeto JSON a partir de uma lista variável de argumentos alternados de chave e valor. |
|
|
|
|
Constrói um objeto JSON a partir de um array de texto. Aceita um array unidimensional com um número par de membros (pares chave-valor alternados) ou um array bidimensional onde cada array interno é um par chave-valor. |
|
|
|
|
Constrói um objeto JSON a partir de dois arrays separados de chaves e valores. |
|
|
Indexação de campos JSONB
O Hologres V1.1 e versões posteriores suportam índices GIN e índices B-tree em campos JSONB para acelerar consultas. Crie índices em campos JSONB em vez de campos JSON, pois índices GIN não estão disponíveis para JSON.
Índices GIN para JSONB são adequados para campos JSONB esparsos. Para campos não esparsos, prefira a otimização de armazenamento colunar do JSONB. A partir do Hologres V3.0.42 e V3.1.10, índices GIN e armazenamento colunar não podem ser usados simultaneamente — quando ambos estiverem habilitados, os índices GIN não terão efeito.
Duas classes de operadores de índice GIN estão disponíveis:
jsonb_ops(padrão): cria uma entrada de índice para cada chave e valor. Suporta os operadores?,?|,?&e@>.jsonb_path_ops: cria uma entrada de índice apenas para cada valor. Suporta somente o operador@>.
-- Create a GIN index using jsonb_ops (default)
CREATE INDEX idx_name ON table_name USING gin (idx_col);
-- Create a GIN index using jsonb_path_ops
CREATE INDEX idx_name ON table_name USING gin (idx_col jsonb_path_ops);
Operadores nativos do PostgreSQL
Índices GIN nativos do PostgreSQL exigem uma reverificação de dados após a recuperação, o que pode limitar as melhorias no desempenho da consulta. Os exemplos abaixo demonstram ambas as classes de operadores.
Exemplo: índice jsonb_ops com consulta de existência de chave
-- 1. Create a table.
BEGIN;
DROP TABLE IF EXISTS json_table;
CREATE TABLE IF NOT EXISTS json_table
(
id INT,
j jsonb
);
COMMIT;
-- 2. Create a GIN index using jsonb_ops.
CREATE INDEX index_json ON json_table USING GIN(j);
-- 3. Insert data.
INSERT INTO json_table VALUES
(1, '{"key1": 1, "key2": [1, 2], "key3": {"a": "b"}}'),
(1, '{"key1": 1}'),
(2, '{"key2": [1, 2], "key3": {"a": "b"}}');
-- 4. Query rows where key1 exists.
SELECT * FROM json_table WHERE j ? 'key1';
Resultado:
id | j
----+-------------------------------------------------
1 | {"key1": 1, "key2": [1, 2], "key3": {"a": "b"}}
1 | {"key1": 1}
Execute EXPLAIN para confirmar o uso do índice:
EXPLAIN SELECT * FROM json_table WHERE j ? 'key1';
QUERY PLAN
Gather (cost=0.00..0.26 rows=1000 width=12)
-> Local Gather (cost=0.00..0.23 rows=1000 width=12)
-> Decode (cost=0.00..0.23 rows=1000 width=12)
-> Bitmap Heap Scan on json_table (cost=0.00..0.13 rows=1000 width=12)
Recheck Cond: (j ? 'key1'::text)
-> Bitmap Index Scan on index_json (cost=0.00..0.00 rows=0 width=0)
Index Cond: (j ? 'key1'::text)
Optimizer: HQO version 1.3.0
A etapa Bitmap Index Scan confirma que o índice foi utilizado.
Exemplo: índice jsonb_path_ops com consulta de contenção
-- 1. Create a table.
BEGIN;
DROP TABLE IF EXISTS json_table;
CREATE TABLE IF NOT EXISTS json_table
(
id INT,
j jsonb
);
COMMIT;
-- 2. Create a GIN index using jsonb_path_ops.
CREATE INDEX index_json ON json_table USING GIN(j jsonb_path_ops);
-- 3. Insert 1,000,000 rows.
INSERT INTO json_table (
SELECT
i,
('{
"key1": "'||i||'"
,"key2": "'||i%100||'"
,"key3": "'||i%1000||'"
,"key4": "'||i%10000||'"
,"key5": "'||i%100000||'"
}')::jsonb
FROM generate_series(1, 1000000) i
);
-- 4. Query rows containing '{"key1": "10"}'.
SELECT * FROM json_table WHERE j @> '{"key1": "10"}'::JSONB;
Resultado:
id | j
----+------------------------------------------------------------------------
10 | {"key1": "10", "key2": "10", "key3": "10", "key4": "10", "key5": "10"}
(1 row)
Execute EXPLAIN para confirmar o uso do índice:
EXPLAIN SELECT * FROM json_table WHERE j @> '{"key1": "10"}'::JSONB;
QUERY PLAN
-------------------------------------------------------------------------------------------
Gather (cost=0.00..0.26 rows=1000 width=12)
-> Local Gather (cost=0.00..0.23 rows=1000 width=12)
-> Decode (cost=0.00..0.23 rows=1000 width=12)
-> Bitmap Heap Scan on json_table (cost=0.00..0.13 rows=1000 width=12)
Recheck Cond: (j @> '{"key1": "10"}'::jsonb)
-> Bitmap Index Scan on index_json (cost=0.00..0.00 rows=0 width=0)
Index Cond: (j @> '{"key1": "10"}'::jsonb)
Optimizer: HQO version 1.3.0
(8 rows)
Operadores do Hologres
O Hologres fornece duas classes adicionais de operadores — jsonb_holo_ops e jsonb_holo_path_ops — que eliminam a etapa de reverificação de dados exigida pelos índices GIN nativos do PostgreSQL, melhorando o desempenho das consultas.
jsonb_holo_opsejsonb_holo_path_opssuportam comprimentos de índice de 1 a 127 bytes. Valores de índice superiores a 127 bytes são truncados, e campos truncados exigem uma reverificação de dados. ExecuteEXPLAIN ANALYZEpara verificar se ocorre uma reverificação.
jsonb_holo_ops: equivalente ajsonb_ops. Suporta os operadores?,?|,?&e@>.jsonb_holo_path_ops: equivalente ajsonb_path_ops. Suporta somente o operador@>.
Exemplo: índice jsonb_holo_ops
-- 1. Create a table.
BEGIN;
DROP TABLE IF EXISTS json_table;
CREATE TABLE IF NOT EXISTS json_table
(
id INT,
j jsonb
);
COMMIT;
-- 2. Create a GIN index using jsonb_holo_ops.
CREATE INDEX index_json ON json_table USING GIN(j jsonb_holo_ops);
-- 3. Insert data.
INSERT INTO json_table VALUES
(1, '{"key1": 1, "key2": [1, 2], "key3": {"a": "b"}}'),
(1, '{"key1": 1}'),
(2, '{"key2": [1, 2], "key3": {"a": "b"}}');
-- 4. Query rows where key1 exists.
SELECT * FROM json_table WHERE j ? 'key1';
Resultado:
id | j
----+-------------------------------------------------
1 | {"key1": 1}
1 | {"key1": 1, "key2": [1, 2], "key3": {"a": "b"}}
(2 rows)
Exemplo: índice jsonb_holo_path_ops
-- 1. Create a table.
BEGIN;
DROP TABLE IF EXISTS json_table;
CREATE TABLE IF NOT EXISTS json_table
(
id INT,
j jsonb
);
-- 2. Create a GIN index using jsonb_holo_path_ops.
CREATE INDEX index_json ON json_table USING GIN(j jsonb_holo_path_ops);
-- 3. Insert 1,000,000 rows.
INSERT INTO json_table (
SELECT
i,
('{
"key1": "'||i||'"
,"key2": "'||i%100||'"
,"key3": "'||i%1000||'"
,"key4": "'||i%10000||'"
,"key5": "'||i%100000||'"
}')::jsonb
FROM generate_series(1, 1000000) i
);
-- 4. Query rows containing '{"key1": "10"}'.
SELECT * FROM json_table WHERE j @> '{"key1": "10"}'::JSONB;
Resultado:
id | j
----+------------------------------------------------------------------------
10 | {"key1": "10", "key2": "10", "key3": "10", "key4": "10", "key5": "10"}
(1 row)
Importar dados JSONB do Realtime Compute for Apache Flink
Ao importar dados JSON do Realtime Compute for Apache Flink para o Hologres, os tipos de campo devem corresponder aos tipos suportados por cada sistema:
Tabelas de origem e resultado do Flink: defina campos JSON como
VARCHAR.Tabelas internas do Hologres: defina campos JSON como
JSONB.
Para obter o mapeamento completo de tipos de dados, consulte Mapeamentos de tipos de dados entre Realtime Compute for Apache Flink e Hologres.
Etapa 1: Crie a tabela interna do Hologres com uma coluna JSONB.
BEGIN;
DROP TABLE IF EXISTS holo_internal_table;
CREATE TABLE IF NOT EXISTS holo_internal_table
(
id BIGINT NOT NULL,
message JSONB NOT NULL
);
CALL set_table_property('holo_internal_table', 'distribution_key', 'id');
COMMIT;
Etapa 2: Crie as tabelas de origem e resultado do Flink usando VARCHAR para o campo JSON e, em seguida, grave os dados no Hologres.
CREATE TEMPORARY TABLE randomSource (
id BIGINT,
message VARCHAR
)
WITH ('connector' = 'datagen');
CREATE TEMPORARY TABLE sink_holo (
id BIGINT,
message VARCHAR
)
WITH (
'connector' = 'hologres',
'dbname' = '<yourDBname>', -- Hologres database name
'tablename' = '<holo_internal_table>', -- Target Hologres table
'username' = '<yourUsername>', -- Alibaba Cloud AccessKey ID
'password' = '<yourPassword>', -- Alibaba Cloud AccessKey secret
'endpoint' = '<yourEndpoint>' -- VPC endpoint of your Hologres instance
);
INSERT INTO sink_holo
SELECT
1,
'{"k":"v"}'
FROM randomSource;
Armazenamento colunar para dados JSONB
Índices GIN otimizam o desempenho da consulta na camada de computação, mas ainda exigem a varredura completa do conteúdo JSON durante a execução. O Hologres V1.3 e versões posteriores oferecem armazenamento colunar para dados JSONB, realizando a otimização na camada de armazenamento. Os dados JSONB são armazenados em colunas, assim como dados estruturados, o que melhora a eficiência de compactação e acelera as consultas.
Para detalhes de configuração, consulte Acelerar consultas JSONB.