Personnaliser l'ordre de tri des clés jsonb impliquant des tableaux

9

J'ai une table dans PostgreSQL avec quelques données:

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

Et j'essaie de trier ces lignes comme ça:

SELECT key FROM t2 order by key;

Le résultat est:

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

Mais ce dont j'ai besoin c'est:

[]
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-t-il un moyen d'y parvenir?

Antonio
la source
Alors, avez-vous votre réponse ici?
Erwin Brandstetter

Réponses:

8

Tout d'abord, votre question ainsi que le nom de votre colonne "key"sont trompeurs. La clé de colonne ne contient aucune clé JSON , uniquement des valeurs . Sinon, nous pourrions utiliser la fonction jsonb_object_keys(jsonb)pour extraire des clés, mais ce n'est pas le cas.

En supposant que tous vos tableaux JSON sont vides ou contiennent des nombres entiers comme illustré. Et les valeurs scalaires (non-tableaux) sont également entières.

Votre ordre de tri de base fonctionnerait avec les tableaux Postgres integer(ou numeric). J'utilise cette petite fonction d'aide pour convertir des jsonbtableaux en 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)';

Explication:

Ajoutez ensuite jsonb_typeof(jsonb)pour arriver à:

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;

Produit exactement le résultat souhaité.

Pourquoi?

Le manuel de jsonbexplique:

La btreecommande de jsonbdatums présente rarement un grand intérêt, mais pour être complète, elle est:

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

Les objets avec un nombre égal de paires sont comparés dans l'ordre:

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

Notez que les clés d'objet sont comparées dans leur ordre de stockage; en particulier, étant donné que des clés plus courtes sont stockées avant des clés plus longues, cela peut conduire à des résultats qui peuvent ne pas être intuitifs, tels que:

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

De même, les tableaux avec un nombre égal d'éléments sont comparés dans l'ordre:

element-1, element-2 ...

Accentuation sur moi.
Voilà pourquoi jsonb '[2]' < jsonb '[1, 2]'.
Mais les tableaux Postgres trient simplement élément par élément: '{2}'::int[] > '{1, 2}'- exactement ce que vous cherchiez.

Erwin Brandstetter
la source
0

Se référant au problème pour classer vos résultats par valeurs entières json. Essayer:

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

Dans votre cas, il semble s'agir d'un tableau pour la clé de commande. Essayez donc d'abord de concaténer les valeurs dans votre champ "clé".

dlg_
la source