Personalizar a ordem de classificação de chave jsonb envolvendo matrizes

9

Eu tenho uma tabela no PostgreSQL com alguns dados:

create table t2 (
    key jsonb,
    value jsonb
);

INSERT INTO t2(key, value)
 VALUES
 ('1', '"test 1"')
,('2', '"test 2"')
,('3', '"test 3"')
,('[]', '"test 4"')
,('[1]', '"test 5"')
,('[2]', '"test 6"')
,('[3]', '"test 7"')
,('[1, 2]', '"test 8"')
,('[1, 2, 3]', '"test 9"')
,('[1, 3]', '"test 10"')
,('[1,2,4]', '"test 11"')
,('[1, 2,4]', '"test 12"')
,('[1,3,13]', '"test 13"')
,('[1, 2, 15]', '"test 15"');

E eu tento classificar essas linhas assim:

SELECT key FROM t2 order by key;

O resultado é:

[]
1
2
3
[1]
[2] <==
[3] <==
[1, 2]
[1, 3] <==
[1, 2, 3]
[1, 2, 4]
[1, 2, 4]
[1, 2, 15]
[1, 3, 13]

Mas o que eu preciso é:

[]
1
2
3
[1]
[1, 2]
[1, 2, 3]
[1, 2, 4]
[1, 2, 4]
[1, 2, 15]
[1, 3] <==
[1, 3, 13]
[2] <==
[3] <==

Existe uma maneira de conseguir isso?

Antonio
fonte
Então você tem sua resposta aqui?
Erwin Brandstetter 28/03

Respostas:

8

Primeiro, sua pergunta e o nome da coluna "key"são enganosos. A chave da coluna não contém nenhuma chave JSON , apenas valores . Caso jsonb_object_keys(jsonb)contrário, poderíamos usar a função para extrair chaves, mas não é assim.

Supondo que todas as suas matrizes JSON estejam vazias ou contenham números inteiros, como demonstrado. E os valores escalares (não matrizes) também são inteiros.

Sua ordem de classificação básica funcionaria com matrizes do Postgres integer(ou numeric). Eu uso essa pequena função auxiliar para converter jsonbmatrizes para o Postgres int[]:

CREATE OR REPLACE FUNCTION jsonb_arr2int_arr(_js jsonb)
   RETURNS int[] LANGUAGE sql IMMUTABLE AS
'SELECT ARRAY(SELECT j::int FROM jsonb_array_elements_text(_js) j)';

Explicação:

Em seguida, adicione jsonb_typeof(jsonb)para chegar a:

SELECT key
FROM   t2
ORDER  BY key <> '[]'             -- special case for empty array
        , jsonb_typeof(key) DESC  -- 'number' before 'array'
        , CASE jsonb_typeof(key)  -- sort arrays as converted int[]
            WHEN 'array'  THEN jsonb_arr2int_arr(key)
            WHEN 'number' THEN ARRAY[key::text::int]
          END;

Produz exatamente o resultado desejado.

Por quê?

O manual para jsonbexplica:

A btreeordem dos dados jsonbraramente é de grande interesse, mas, para ser completo, é:

Object > Array > Boolean > Number > String > Null
Object with n pairs > object with n - 1 pairs
Array with n elements > array with n - 1 elements

Objetos com números iguais de pares são comparados na ordem:

key-1, value-1, key-2 ...

Observe que as chaves de objetos são comparadas em sua ordem de armazenamento; em particular, como as chaves mais curtas são armazenadas antes das chaves mais longas, isso pode levar a resultados que podem não ser intuitivos, como:

{ "aa": 1, "c": 1} > {"b": 1, "d": 1}

Da mesma forma, matrizes com números iguais de elementos são comparadas na ordem:

element-1, element-2 ...

Negrito ênfase minha.
Por isso jsonb '[2]' < jsonb '[1, 2]'.
Porém, as matrizes do Postgres apenas classificam elemento por elemento: '{2}'::int[] > '{1, 2}'- exatamente o que você estava procurando.

Erwin Brandstetter
fonte
0

Referindo-se ao problema para ordenar seus resultados por valores inteiros json. Tentar:

select myjson from mytable order by (myjson->>'some_int')::int;

No seu caso, parece ser uma matriz para a chave de pedidos. Então, primeiro tente concatinar os valores no seu campo "chave".

dlg_
fonte