If I were you, I would lose the JSON and use the PostgreSQL ARRAY type - PostgreSQL has very powerful array processing capabilities which might be better suited to your needs.
JSON is usually for "variable" data where the field names and values can vary (clothing items would be a good example), whereas your data is fairly "static" - i.e. the "person" table is fully defined at the beginning.
What I did to solve your issue was the following (all of the code below is available on the fiddle here):
CREATE TABLE event
(
id INTEGER PRIMARY KEY,
event_name TEXT NOT NULL,
event_date DATE NOT NULL
);
CREATE TABLE person
(
id INTEGER PRIMARY KEY,
full_name TEXT NOT NULL UNIQUE, -- make UNIQUE (suggestion)
email_address TEXT
);
CREATE TABLE event_person
(
person_id INTEGER NOT NULL REFERENCES person (id),
event_id INTEGER NOT NULL REFERENCES event (id),
PRIMARY KEY (person_id, event_id) -- you can't attend the same event twice!
);
Then we populate our tables with a small amount of sample data:
INSERT INTO event VALUES
(1, 'concert', '2024-01-10'),
(2, 'dinner', '2024-01-24'),
(3, 'football match', '2024-02-05'),
(4, 'baseball game', '2024-02-15');
and
INSERT INTO person VALUES
(1, 'Billy Joel', 'billy@example.net'),
(2, 'Bono', 'bono@eanet.ie'),
(3, 'Prince', 'prince@heaven.com'),
(4, 'Lady Gaga', 'lg@disney.com');
and now, those who attended which events:
INSERT INTO event_person VALUES
(1,3), (2,4), (1,2), (3,4), (3,3), (2,2);
First, we run this query to obtain all of the data for all of the events - which events were attended by whom:
SELECT
e.id AS eid, e.event_name AS en, e.event_date AS ed,
p.id AS pid, p.full_name AS pfn, p.email_address AS pea
FROM event e
JOIN event_person ep
ON e.id = ep.event_id
JOIN person p
ON p.id = ep.person_id
ORDER BY e.id;
Result:
eid en ed pid pfn pea
2 dinner 2024-01-24 1 Billy Joel billy@example.net
2 dinner 2024-01-24 2 Bono bono@eanet.ie
3 football match 2024-02-05 1 Billy Joel billy@example.net
3 football match 2024-02-05 3 Prince prince@heaven.com
4 baseball game 2024-02-15 2 Bono bono@eanet.ie
4 baseball game 2024-02-15 3 Prince prince@heaven.com
But, that's not exactly the result you want - you want the events aggregated by attendee - so we use the ARRAY_AGG (examples) function as follows:
SELECT
eid, en, ed, ARRAY_AGG(pid::TEXT || ' ' || pfn || ' ' || pea)
FROM
(
SELECT
e.id AS eid, e.event_name AS en, e.event_date AS ed,
p.id AS pid, p.full_name AS pfn, p.email_address AS pea
FROM event e
JOIN event_person ep
ON e.id = ep.event_id
JOIN person p
ON p.id = ep.person_id
ORDER BY e.id
)
GROUP BY eid, en, ed
ORDER BY eid;
Result:
eid en ed array_agg
2 dinner 2024-01-24 {"1 Billy Joel billy@example.net","2 Bono bono@eanet.ie"}
3 football match 2024-02-05 {"1 Billy Joel billy@example.net","3 Prince prince@heaven.com"}
4 baseball game 2024-02-15 {"2 Bono bono@eanet.ie","3 Prince prince@heaven.com"}
This is an alternative to the JSON approach, but is, I believe, better suited to the data model as presented. HTH and welcome to dba.se!