Search code examples
arraysjsonpostgresqlsql-order-byaggregate-functions

Postgresql : Sorting JSON array by numeric key value


I've been reading similar questions which look to have simple enough solutions, but applying them here doesn't seem to be working for me. I have a json column in a postgres database table which contains x and y co-ordinates. Performing a simple select on this column gives back the following for example :

[
{
 "x": "-1.6827080672804147",
 "y": "-0.011726425465745486"
},
{
 "x": "2.4016256261667235",
 "y": "0.016304356672382222"
},
{
 "x": "0.2278035109735127",
 "y": "0.0013854154958112177"
},
{
 "x": "1.2104642489702613",
 "y": "0.008129416140682903"
},
{
 "x": "-0.3281865438803838",
 "y": "-0.0024303442506510738"
},
{
 "x": "-0.2401461868455415",
 "y": "-0.0018261232803209514"
}, 
               .
               .
               .
               .
]

I would like to return this object ordered in terms of the x coordinate ascending. The above resultset was actually the result of running the following query:

  SELECT coordinates 
    FROM schema_name.table_name
   WHERE ....
     AND ....
ORDER BY coordinates ->> 'x' asc;

I also tried using

ORDER BY cast(coordinates->>'x' as numeric)  ASC

which yields the following:

ERROR:  cannot cast type json to numeric

I'm sure it's something silly I'm missing. Any pointers in the right direction would be greatly appreciated.


Solution

  • You should use order by in json_agg(). You may want to define a function that can be useful in various contexts:

    create or replace function sort_my_array(json)
    returns json language sql immutable as $$
        select json_agg(value order by (value->>'x')::numeric)
        from json_array_elements($1)
    $$;
    
    select sort_my_array(coordinates)
    from my_table
    

    If you do not like custom functions, use json_array_elements() in a lateral join:

    select json_agg(value order by (value->>'x')::numeric)
    from my_table
    cross join json_array_elements(coordinates)
    

    Test it in db<>fiddle.