I have a Postgres DB setup, and I’m trying to return a certain data shape back from a query.
My data looks like this:
CREATE TABLE resource_actions(
resource TEXT NOT NULL,
actions TEXT NOT NULL,
entity TEXT NOT NULL,
UNIQUE(resource, actions, entity)
);
INSERT INTO resource_actions (resource, actions, entity) VALUES
('reading-list', 'read', 'Citizen'),
('reading-list', 'read', 'Employee'),
('books', 'read', 'Employee'),
('titles', 'update', 'Citizen'),
('titles', 'read', 'Citizen'),
('titles', 'update', 'Employee'),
('titles', 'read', 'Employee'),
('authors', 'update', 'Employee'),
('authors', 'read', 'Citizen'),
('reviews', 'read', 'Citizen'),
('reviews', 'read', 'Employee'),
('reviews', 'create', 'Citizen'),
('reviews', 'create', 'Employee'),
('employees', 'read', 'Employee'),
('employees', 'read', 'Boss'),
('employees', 'delete', 'Boss'),
('employees', 'create', 'Boss'),
('employees', 'update', 'Boss'),
('employee-schedule', 'read', 'Boss'),
('employee-schedule', 'delete', 'Boss'),
('employee-schedule', 'create', 'Boss'),
('employee-schedule', 'update', 'Boss');
I want to return data like so:
{
read:['Citizen':['reading-list', 'titles', 'authors','reviews'], 'Employee':['books','reading-list', 'titles', 'authors'], 'Boss':[]]
create:['Citizen':['reviews'], 'Employee':['reviews'], 'Boss':['employees','employee-schedule']],
update: ['Citizen':['titles'], 'Employee':['authors','titles'], 'Boss':['employees','employee-schedule']],
delete:['Citizen':[], 'Employee':[], 'Boss':['employees','employee-schedule']]
}
I have a SQL query which is close, I just can’t for the life of me figure out what I need to change to get the right shape of the data. The query I have is here:
select actions,
JSON_AGG(JSON_BUILD_OBJECT(resource, entity)) AS groupedActions
FROM resource_actions
GROUP BY actions
I have a SQL fiddle showing what I have so far: http://sqlfiddle.com/#!17/93bf5/1/0
It gets me close, but I’m not understanding how to aggregate the data by entity
and show an array of the resources they have access to. Any help is appreciated!
3
Answers
DBfiddle demo
All as single JSON:
DBFiddle demo
To aggregate actions with concatenation you can use a custom aggregation
db<>fiddle
You can construct objects in subqueries/CTEs, then collect them with
jsonb_object_agg()
: demoUse
jsonb
if you can: it’s indexable, lighter, faster and more flexible, offering more functions and operators, includingjsonpath
interaction, at a small processing cost on input.If you can’t, all
jsonb
functions above have directjson
equivalents they can be swapped out for.Except for
jsonb_pretty()
below, where you’ll have to castjson
tojsonb
. If you want to prettify some text-based json, you can feed it as a string literal – it’ll be detected asunknown
and auto-cast to a supported type.Output through
jsonb_pretty()
: