O operador @> faz uma unica pergunta a um valor JSONB: "voce contem este fragmento?". Nao e igualdade nem extracao de campo, e sim um teste de contencao: o documento da esquerda deve incluir tudo o que esta descrito a direita, em qualquer profundidade. Isso e mais compacto que uma cadeia de condicoes ->> e, o mais importante, pode se apoiar em um indice GIN.
O que significa "contem"
payload @> '{"plan":"pro"}' e verdadeiro quando payload tem uma chave plan com o valor "pro" -- mesmo que dezenas de outras chaves estejam ao lado. O operando direito e um fragmento modelo, nao um documento completo.
Suponha que users tenha uma coluna payload jsonb com configuracoes e rotulos:
SELECT id, email
FROM users
WHERE payload @> '{"plan":"pro"}';
Algumas regras que vale lembrar:
- A correspondencia e por subconjunto: o documento pode carregar qualquer coisa alem do fragmento.
- Voce pode declarar varias condicoes de uma vez:
payload @> '{"plan":"pro","active":true}' exige ambas as chaves.
- Os tipos devem coincidir estritamente:
'{"active":true}' nao casa com "active":"true" (string contra booleano).
- O operador
<@ e o espelho: a <@ b significa "a esta contido em b".
Objetos aninhados e arrays
A forca do @> e que o fragmento pode ser aninhado e a correspondencia e recursiva. Vamos colocar itens e um endereco de entrega dentro de um pedido:
SELECT id, amount
FROM orders
WHERE payload @> '{"shipping":{"country":"DE"}}';
Nao importa que shipping tambem guarde city e zip -- basta que country seja igual a "DE".
Para arrays, @> testa a pertinencia, nao a posicao nem a ordem:
SELECT id
FROM orders
WHERE payload @> '{"items":[{"sku":"A-100"}]}';
SELECT id
FROM users
WHERE payload @> '{"tags":["beta","eu"]}';
Pegadinha: os escalares de um array casam "como um conjunto", e os objetos dentro de um array tambem sao comparados por contencao. Assim '[1,2]' @> '[2,1]' e verdadeiro (a ordem nao importa), enquanto '[1,2]' @> '[1,2,3]' e falso -- a esquerda nao tem o 3. E lembre: um objeto vazio '{}' esta contido em todo objeto, entao payload @> '{}' e verdadeiro quase sempre.
Indice GIN: onde o @> realmente vence
Sem indice, @> forca uma varredura sequencial. Mas o JSONB pode morar em um indice GIN, e entao as consultas de contencao ficam rapidas:
CREATE INDEX idx_users_payload ON users USING gin (payload);
SELECT id FROM users WHERE payload @> '{"plan":"pro"}';
Um detalhe sobre as classes de operador:
- A padrao (
jsonb_ops) suporta @>, ?, ?|, ?& e indexa tanto chaves quanto valores.
- A classe
jsonb_path_ops (USING gin (payload jsonb_path_ops)) e menor e mais rapida para @>, mas nao suporta os operadores de existencia de chave como ?.
CREATE INDEX idx_users_payload_path
ON users USING gin (payload jsonb_path_ops);
Confira o plano com EXPLAIN ANALYZE: se voce ver um Bitmap Index Scan no seu indice GIN, o planejador aproveitou o @>.
@> contra a igualdade com ->>
O filtro classico extrai um escalar e o compara:
SELECT id FROM users
WHERE payload ->> 'plan' = 'pro';
Funciona, mas tem armadilhas:
->> retorna text, entao numeros e booleanos sao convertidos para string; e facil introduzir um erro de tipo sutil.
- Um B-tree sobre
(payload ->> 'plan') precisa ser criado por chave separadamente; @> e atendido por um unico indice GIN sobre a coluna inteira.
- Para campos aninhados
->> vira uma escada payload -> 'shipping' ->> 'country', enquanto @> continua uma unica declaracao.
Regra pratica: para testar "o documento contem esta forma", use @> mais GIN. Para faixas e desigualdades (amount > 100, created_at < ...) @> e inutil -- ali voce precisa de ->> com um cast ou uma coluna dedicada.
Outros motores nao tem o operador: no MySQL o analogo e a funcao JSON_CONTAINS(payload, '{"plan":"pro"}') que retorna 0/1, enquanto as verificacoes de caminho passam por JSON_EXTRACT/->>. No ClickHouse o JSON costuma ser analisado com funcoes como JSONExtractString, e nao ha um unico operador de contencao.
O operador
@>faz uma unica pergunta a um valor JSONB: "voce contem este fragmento?". Nao e igualdade nem extracao de campo, e sim um teste de contencao: o documento da esquerda deve incluir tudo o que esta descrito a direita, em qualquer profundidade. Isso e mais compacto que uma cadeia de condicoes->>e, o mais importante, pode se apoiar em um indice GIN.O que significa "contem"
payload @> '{"plan":"pro"}'e verdadeiro quandopayloadtem uma chaveplancom o valor"pro"-- mesmo que dezenas de outras chaves estejam ao lado. O operando direito e um fragmento modelo, nao um documento completo.Suponha que
userstenha uma colunapayload jsonbcom configuracoes e rotulos:SELECT id, email FROM users WHERE payload @> '{"plan":"pro"}';Algumas regras que vale lembrar:
payload @> '{"plan":"pro","active":true}'exige ambas as chaves.'{"active":true}'nao casa com"active":"true"(string contra booleano).<@e o espelho:a <@ bsignifica "aesta contido emb".Objetos aninhados e arrays
A forca do
@>e que o fragmento pode ser aninhado e a correspondencia e recursiva. Vamos colocar itens e um endereco de entrega dentro de um pedido:SELECT id, amount FROM orders WHERE payload @> '{"shipping":{"country":"DE"}}';Nao importa que
shippingtambem guardecityezip-- basta quecountryseja igual a"DE".Para arrays,
@>testa a pertinencia, nao a posicao nem a ordem:-- order whose items array includes a SKU object SELECT id FROM orders WHERE payload @> '{"items":[{"sku":"A-100"}]}'; -- tags array containing both values, in any order SELECT id FROM users WHERE payload @> '{"tags":["beta","eu"]}';Indice GIN: onde o
@>realmente venceSem indice,
@>forca uma varredura sequencial. Mas o JSONB pode morar em um indice GIN, e entao as consultas de contencao ficam rapidas:CREATE INDEX idx_users_payload ON users USING gin (payload); -- now this can use the index SELECT id FROM users WHERE payload @> '{"plan":"pro"}';Um detalhe sobre as classes de operador:
jsonb_ops) suporta@>,?,?|,?&e indexa tanto chaves quanto valores.jsonb_path_ops(USING gin (payload jsonb_path_ops)) e menor e mais rapida para@>, mas nao suporta os operadores de existencia de chave como?.CREATE INDEX idx_users_payload_path ON users USING gin (payload jsonb_path_ops);Confira o plano com
EXPLAIN ANALYZE: se voce ver umBitmap Index Scanno seu indice GIN, o planejador aproveitou o@>.@>contra a igualdade com->>O filtro classico extrai um escalar e o compara:
SELECT id FROM users WHERE payload ->> 'plan' = 'pro';Funciona, mas tem armadilhas:
->>retornatext, entao numeros e booleanos sao convertidos para string; e facil introduzir um erro de tipo sutil.(payload ->> 'plan')precisa ser criado por chave separadamente;@>e atendido por um unico indice GIN sobre a coluna inteira.->>vira uma escadapayload -> 'shipping' ->> 'country', enquanto@>continua uma unica declaracao.Regra pratica: para testar "o documento contem esta forma", use
@>mais GIN. Para faixas e desigualdades (amount > 100,created_at < ...)@>e inutil -- ali voce precisa de->>com um cast ou uma coluna dedicada.Outros motores nao tem o operador: no MySQL o analogo e a funcao
JSON_CONTAINS(payload, '{"plan":"pro"}')que retorna 0/1, enquanto as verificacoes de caminho passam porJSON_EXTRACT/->>. No ClickHouse o JSON costuma ser analisado com funcoes comoJSONExtractString, e nao ha um unico operador de contencao.