Kustomisasi urutan pengurutan kunci jsonb yang melibatkan array

9

Saya punya tabel di PostgreSQL dengan beberapa data:

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"');

Dan saya mencoba mengurutkan baris-baris ini seperti itu:

SELECT key FROM t2 order by key;

Hasilnya adalah:

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

Tapi yang saya butuhkan adalah:

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

Apakah ada cara untuk mencapainya?

Antonio
sumber
Jadi, apakah Anda punya jawaban di sini?
Erwin Brandstetter

Jawaban:

8

Pertama, pertanyaan Anda serta nama kolom Anda "key"menyesatkan. Kunci kolom tidak mengandung kunci JSON , hanya nilai . Lain kita bisa menggunakan fungsi jsonb_object_keys(jsonb)untuk mengekstrak kunci, tetapi tidak begitu.

Dengan asumsi semua array JSON Anda kosong atau tahan angka integer seperti yang ditunjukkan. Dan nilai skalar (non-array) juga bilangan bulat.

Urutan sortir dasar Anda akan berfungsi dengan array Postgres integer(atau numeric). Saya menggunakan fungsi pembantu kecil ini untuk mengonversi jsonbarray ke 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)';

Penjelasan:

Kemudian tambahkan jsonb_typeof(jsonb)untuk sampai di:

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;

Menghasilkan hasil yang diinginkan dengan tepat.

Mengapa?

Manual untuk jsonbmenjelaskan:

The btreepemesanan untuk jsonbdatums jarang sangat menarik, tapi untuk kelengkapan itu adalah:

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

Objek dengan jumlah pasangan yang sama dibandingkan dalam urutan:

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

Perhatikan bahwa kunci objek dibandingkan dalam urutan penyimpanannya; khususnya, karena kunci yang lebih pendek disimpan sebelum kunci yang lebih panjang, ini dapat menyebabkan hasil yang mungkin tidak intuitif, seperti:

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

Demikian pula, array dengan jumlah elemen yang sama dibandingkan dalam urutan:

element-1, element-2 ...

Penekanan berani saya.
Itu sebabnya jsonb '[2]' < jsonb '[1, 2]'.
Tapi array Postgres hanya mengurutkan elemen demi elemen: '{2}'::int[] > '{1, 2}'- persis apa yang Anda cari.

Erwin Brandstetter
sumber
0

Mengacu pada masalah untuk memesan hasil Anda dengan nilai integer json. Mencoba:

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

Dalam kasus Anda, sepertinya itu adalah array untuk kunci pemesanan. Jadi pertama-tama cobalah untuk menggabungkan nilai dalam bidang "kunci" Anda.

dlg_
sumber