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?
-
1I'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.András Váczi– András Váczi2014年11月05日 16:12:20 +00:00Commented 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.kritik– kritik2014年11月12日 19:18:54 +00:00Commented Nov 12, 2014 at 19:18
2 Answers 2
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:
-
Great, we have one more solutionkritik– kritik2017年01月25日 06:46:33 +00:00Commented Jan 25, 2017 at 6:46
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')