3

I have column document of data type json with logging data like:

CREATE TABLE foo(document)
AS
 VALUES (
 '{
 "blocks": {
 "participant-information_primary__Primary__39011__2288960": {
 "first_name": "First name"
 }
 }
 }'::jsonb
 ), (
 '{
 "blocks": {
 "participant-information_primary__Primary__39011__2288969": {
 "first_name": "Joe ZZZ"
 }
 }
 }'
 ), (
 '{
 "blocks": {
 "participant-information_primary__Primary__39011__2288169": {
 "first_name": "AAA ZZZ"
 }
 }
 }'
 ), (
 '{
 "blocks": {
 "participant-information_primary__Primary__39011__1288169": {
 "first_name": "kkk"
 }
 }
 }'
 );

Key name 'participant-information_primary__Primary__39011__2288960' is dynamic and can vary by the trailing integer number. I want to do something like:

ORDER BY document->'blocks'->'participant-information_primary*'->>'first_name'

How can I do it?

Evan Carroll
65.7k50 gold badges259 silver badges511 bronze badges
asked Nov 5, 2014 at 15:05
2
  • 1
    I'd store the integer part separately. Then you may be able to have a slightly different JSON structure that can be searched to your liking. Commented Nov 5, 2014 at 16:12
  • Yes, but I need to save here id. But I found an answer on my question. Will post it. Commented Nov 12, 2014 at 19:18

2 Answers 2

1

Using json_object_keys() like you found yourself already (or jsonb_object_keys() for jsonb), you can do this with simple inline SQL. I suggest a LATERAL subquery.

If, like your example suggests, there is exactly one key inside the key 'blocks', the solutions is simple:

SELECT *
FROM tbl, json_object_keys(document->'blocks') AS part
ORDER BY document->'blocks'->part->>'first_name';

The comma is short notation for CROSS JOIN LATERAL.

If there can be any number of keys, you need to be more verbose:

SELECT *
FROM tbl
LEFT JOIN LATERAL (
 SELECT part
 FROM json_object_keys(document->'blocks') part
 WHERE part ~ '^participant-information_primary__Primary__' -- your pattern here
 LIMIT 1 -- just to be safe
 ) s ON true
ORDER BY document->'blocks'->part->>'first_name';

The LEFT JOIN preserves rows where no matching key is found at all.

Related:

answered Jan 23, 2017 at 16:10
1
  • Great, we have one more solution Commented Jan 25, 2017 at 6:46
0

Solution to the problem

 -- field should be escaped already
 CREATE OR REPLACE FUNCTION json_sort_by(_j json, field text) RETURNS text AS $$
 declare key text;
 declare search text[];
 BEGIN
 search := string_to_array(field, '.');
 IF field ~ '^blocks' THEN
 FOREACH key IN ARRAY ARRAY(select json_object_keys(_j->'blocks')) LOOP
 IF key ~ ('^'::text || search[2]) THEN
 return _j->'blocks'->key->>search[3];
 END IF;
 END LOOP;
 return '';
 ELSE
 return json_extract_path_text(_j, VARIADIC search);
 END IF;
 END;
 $$
 LANGUAGE plpgsql IMMUTABLE;

And then in SQL query run order by json_sort_by(json, 'fddf.fdfd.fdfd')

answered Nov 12, 2014 at 19:21

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.