I have a simple query as follows:
x
select json_agg(json_build_object(
'name','name',
'score',random(),
'snippet','random text'
)) as data
from table;
I have simplified the above for the purposes of this question.
This results in something like this being returned:
[{"name" : "name", "score" : 0.896277624648064, "snippet" : "random text"},
{"name" : "name", "score" : 0.845714892726392, "snippet" : "random text"},
{"name" : "name", "score" : 0.688958259299397, "snippet" : "random text"},
{"name" : "name", "score" : 0.870620720088482, "snippet" : "random text"},
{"name" : "name", "score" : 0.282366622239351, "snippet" : "random text"}]
Is there a way I can order the underlying json_agg by the score within the object?
I’ve tried:
select json_agg(json_build_object(
'name','name',
'score',random(),
'snippet','random text'
) order by score) as data
from table;
And also this:
select json_agg(json_build_object(
'name','name',
'score',random(),
'snippet','random text'
) a order by a->>'score') as data
from table;
But both of the above fail
Advertisement
Answer
I don’t see how random()
fits in. But you can generate the record in the from
clause and then access the field:
select json_agg(jobj order by jobj->>'score')
from table t cross join lateral
(values (json_build_object('name', 'name',
'score', random(),
'snippet', 'random text'
)
)
) v(jobj);
The problem is that Postgres over-optimizes the above and returns the same score
for all rows. One solution is to correlate the subquery, forcing evaluation for each row in t
. Here is one method that works:
select json_agg(jobj order by jobj->>'score')
from t cross join lateral
(values (json_build_object('name', 'name',
'score', (case when t.n is null then random() else random() end),
'snippet', 'random text'
)
)
) v(jobj);
Here is a db<>fiddle.