Personalizza l'ordinamento delle chiavi jsonb che coinvolge gli array


9

Ho una tabella in PostgreSQL con alcuni dati:

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 provo a ordinare queste righe in questo modo:

SELECT key FROM t2 order by key;

Il risultato è:

[]
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]

Ma quello di cui ho bisogno è:

[]
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] <==

C'è un modo per raggiungerlo?


Quindi hai la tua risposta qui?
Erwin Brandstetter,

Risposte:


8

Prima di tutto, la tua domanda e il nome della tua colonna "key"sono fuorvianti. La chiave di colonna non contiene chiavi JSON , ma solo valori . Altrimenti potremmo usare la funzione jsonb_object_keys(jsonb)per estrarre i tasti, ma non è così.

Supponendo che tutti gli array JSON siano vuoti o contengano numeri interi, come dimostrato. E anche i valori scalari (non array) sono interi.

Il tuo ordinamento di base funzionerebbe con gli array Postgres integer(o numeric). Uso questa piccola funzione di aiuto per convertire jsonbarray in 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)';

Spiegazione:

Quindi aggiungi jsonb_typeof(jsonb)per arrivare 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;

Produce esattamente il risultato desiderato.

Perché?

Il manuale per jsonbspiega:

L' btreeordinamento per i jsonbdatum è raramente di grande interesse, ma per completezza è:

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

Gli oggetti con uguale numero di coppie vengono confrontati nell'ordine:

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

Si noti che le chiavi degli oggetti vengono confrontate nel loro ordine di archiviazione; in particolare, poiché le chiavi più brevi sono memorizzate prima delle chiavi più lunghe, ciò può portare a risultati che potrebbero non essere intuitivi, come:

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

Allo stesso modo, le matrici con uguale numero di elementi vengono confrontate nell'ordine:

element-1, element-2 ...

Enorme enfasi sulla mia.
Ecco perché jsonb '[2]' < jsonb '[1, 2]'.
Ma le matrici di Postgres ordinano solo elemento per elemento: '{2}'::int[] > '{1, 2}'- esattamente quello che stavi cercando.


0

Riferendosi al problema per ordinare i risultati in base a valori interi json. Provare:

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

Nel tuo caso sembra essere un array per la chiave di ordinazione. Quindi prima prova a concatenare i valori nel tuo campo "chiave".

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.