From 9a4458dd351ff3dcb05a68e59676cb8414a65fd8 Mon Sep 17 00:00:00 2001 From: Lutz Biedinger Date: Tue, 4 Jun 2024 10:18:20 +0200 Subject: [PATCH] feat: stories view counts (#2336) * blog views on stories * update specs * rename variables * wildcards for blog cleanup deletion * prevent action duplication, to adhere to unique key constraint * clean up old duplicate actions before old object deletion * cascade delete for actions-object fk * cascade delete on all fk constraints * add renamed migration --- ...-add-cascade-delete-to-fk-constraints.psql | 29 ++++++++++++ .../model/08-clean-up-blog-objects.psql | 44 +++++++++++++++++++ .../src/server-middleware/api/events/views.js | 8 +++- .../api/events/views.spec.js | 2 +- 4 files changed, 80 insertions(+), 3 deletions(-) create mode 100644 docker/compose/postgres/model/07-add-cascade-delete-to-fk-constraints.psql create mode 100644 docker/compose/postgres/model/08-clean-up-blog-objects.psql diff --git a/docker/compose/postgres/model/07-add-cascade-delete-to-fk-constraints.psql b/docker/compose/postgres/model/07-add-cascade-delete-to-fk-constraints.psql new file mode 100644 index 0000000000..32493203dc --- /dev/null +++ b/docker/compose/postgres/model/07-add-cascade-delete-to-fk-constraints.psql @@ -0,0 +1,29 @@ +SET search_path TO EVENTS; + +-- remove existing fk constraints and re-create them with cascade deletion + +-- Actions table +ALTER TABLE actions +DROP CONSTRAINT fk_object; +ALTER TABLE actions +DROP CONSTRAINT fk_action_type; +ALTER TABLE actions +DROP CONSTRAINT fk_session; + +ALTER TABLE actions +ADD CONSTRAINT fk_object FOREIGN KEY (object_id) REFERENCES objects (id) ON DELETE CASCADE; +ALTER TABLE actions +ADD CONSTRAINT fk_action_type FOREIGN KEY (action_type_id) REFERENCES action_types (id) ON DELETE CASCADE; +ALTER TABLE actions +ADD CONSTRAINT fk_session FOREIGN KEY (session_id) REFERENCES sessions (id) ON DELETE CASCADE; + +-- History table +ALTER TABLE history +DROP CONSTRAINT fk_object; +ALTER TABLE history +DROP CONSTRAINT fk_action_type; + +ALTER TABLE history +ADD CONSTRAINT fk_object FOREIGN KEY (object_id) REFERENCES objects (id) ON DELETE CASCADE; +ALTER TABLE history +ADD CONSTRAINT fk_action_type FOREIGN KEY (action_type_id) REFERENCES action_types (id) ON DELETE CASCADE; diff --git a/docker/compose/postgres/model/08-clean-up-blog-objects.psql b/docker/compose/postgres/model/08-clean-up-blog-objects.psql new file mode 100644 index 0000000000..a7b9f8cc0a --- /dev/null +++ b/docker/compose/postgres/model/08-clean-up-blog-objects.psql @@ -0,0 +1,44 @@ +SET search_path TO EVENTS; + +-- ensure an object for the '/stories' uri exists for all /blog uris + +INSERT INTO objects (uri) + (SELECT DISTINCT REPLACE(uri, '/blog/', '/stories/') + FROM objects) ON CONFLICT DO NOTHING; + +-- on actions/history linked to /blog/ objects, update the link to be to the /stories/ object +-- do not update old actions where a user viewed old & new objects in the same session already. + +UPDATE actions +SET object_id=new_object_id +FROM + (SELECT old_actions.id old_action_id, + new_objects.id new_object_id + FROM objects old_objects + INNER JOIN objects new_objects ON new_objects.uri=REPLACE(old_objects.uri, '/blog/', '/stories/') + INNER JOIN actions old_actions ON old_actions.object_id=old_objects.id + LEFT JOIN actions duplicate_actions ON ( + duplicate_actions.object_id=new_objects.id + AND duplicate_actions.session_id=old_actions.session_id + AND duplicate_actions.action_type_id=old_actions.action_type_id) + WHERE old_objects.uri like '%/blog/%' + AND new_objects.uri like '%/stories/%' + AND duplicate_actions.id IS NULL) old_actions_new_objects +WHERE id=old_action_id; + +UPDATE history +SET object_id=new_id +FROM + (SELECT old.id old_id, + new.id new_id + FROM objects old + INNER JOIN objects new ON new.uri=REPLACE(old.uri, '/blog/', '/stories/') + WHERE old.uri like '%/blog/%' + AND new.uri like '%/stories/%') old_new +WHERE object_id=old_id; + +-- delete the /blog/ objects + +DELETE +FROM objects +WHERE uri like '%/blog/%'; diff --git a/packages/portal/src/server-middleware/api/events/views.js b/packages/portal/src/server-middleware/api/events/views.js index eb5f4a39c9..20c1022cee 100644 --- a/packages/portal/src/server-middleware/api/events/views.js +++ b/packages/portal/src/server-middleware/api/events/views.js @@ -14,6 +14,10 @@ export default (config = {}) => { // Ignore any search query or hash const uri = `${url.origin}${url.pathname}`; + // TODO: update old objects, then stop using legacy uris + // @/docker/compose/postgres/model/07-clean-up-blog-objects.psql + const legacyUri = url.pathname.startsWith('/stories') ? `${url.origin}${url.pathname.replace('/stories', '/blog')}` : uri; + const result = await pg.query(` SELECT SUM(views) AS views FROM @@ -33,9 +37,9 @@ export default (config = {}) => { LEFT JOIN events.action_types AT ON a.action_type_id=at.id GROUP BY at.name, o.uri) actions_and_history - WHERE action_type_name='view' AND (uri=$1 OR uri LIKE $2) + WHERE action_type_name='view' AND (uri=$1 OR uri=$2) `, - [uri, `${uri}?%`] + [uri, legacyUri] ); const viewCount = Number(result.rows[0]?.views); diff --git a/packages/portal/tests/unit/server-middleware/api/events/views.spec.js b/packages/portal/tests/unit/server-middleware/api/events/views.spec.js index 363b027219..d4b0021a5d 100644 --- a/packages/portal/tests/unit/server-middleware/api/events/views.spec.js +++ b/packages/portal/tests/unit/server-middleware/api/events/views.spec.js @@ -48,7 +48,7 @@ describe('@/server-middleware/api/events/views', () => { expect(pgPoolQuery.calledWith( sinon.match((sql) => sql.trim().startsWith('SELECT ')), - ['https://example.com/example', 'https://example.com/example?%'] + ['https://example.com/example', 'https://example.com/example'] )).toBe(true); });