I\'m working on a Rails application that utilizes the Postgres JSON
data type. I have a JSON column called data
in a table called reports
Given this table (which you should have provided in a form like this):
CREATE TABLE reports (rep_id int primary key, data json);
INSERT INTO reports (rep_id, data)
VALUES
(1, '{"objects":[{"album": 1, "src":"fooA.png", "pos": "top"}, {"album": 2, "src":"barB.png", "pos": "top"}], "background":"background.png"}')
, (2, '{"objects":[{"album": 1, "src":"fooA.png", "pos": "top"}, {"album": 2, "src":"barC.png", "pos": "top"}], "background":"bacakground.png"}')
, (3, '{"objects":[{"album": 1, "src":"fooA.png", "pos": "middle"},{"album": 2, "src":"barB.png", "pos": "middle"}],"background":"background.png"}')
, (4, '{"objects":[{"album": 1, "src":"fooA.png", "pos": "top"}, {"album": 3, "src":"barB.png", "pos": "top"}], "background":"backgroundA.png"}')
;
Use json_populate_recordset() for unnesting the recordset "objects"
.
The function requires a registered row type to define the names and data types of resulting columns. For the purpose of this demo or generally for ad-hoc queries, a temp table modeled after "objects"
provides the same:
CREATE TEMP TABLE obj(album int, src text, pos text);
To find the top 3 most common combinations
... of entries that have the same album, src, and background
:
SELECT array_agg(r.rep_id) AS ids, count(*) AS ct
FROM reports r
, json_populate_recordset(null::obj, r.data->'objects') o
GROUP BY r.data->>'background'
, o.album
, o.scr
ORDER BY count(*) DESC
LIMIT 3;
Each object counts, no matter whether from the same row or not. You did not define how to handle that exactly. Consequently, rep_id
can pop up multiple times in the array ids
. Add DISTINCT
to array_agg()
to fold possible duplicates. The count ct
can be greater then the length of the array ids
in this case.
Requires Postgres 9.3 for the JSON functions and operators and the implicit JOIN LATERAL.
json_array_elements() just unnests the json array without transforming the result into a SQL row. Access individual fields with JSON operators accordingly.
SELECT array_agg(r.rep_id) AS ids, count(*) AS ct
FROM reports r
, json_array_elements(r.data->'objects') o
GROUP BY r.data->>'background'
, o->>'album'
, o->>'scr'
ORDER BY count(*) DESC
LIMIT 3;