source: Klonkt/scripts/backfill-reactions.mjs@ 0e27e54

main
Last change on this file since 0e27e54 was 0e27e54, checked in by Claude (agent) <aiclaude@…>, 5 weeks ago

Backfill-script voor reacties, en de volgorde-eis die ik gemist had

VOLGORDE-CORRECTIE op het plan in shaer-9e9: de backfill hoort TUSSEN fase 1 en
fase 2, niet ergens bij fase 0. Zodra de lezers naar de tussentabel wijzen
verdwijnen reacties die alleen in de kolommen staan uit beeld -- stil, want een
ontbrekende rij is geen fout. Mijn plan noemde de backfill-statements wel maar
zei niet wanneer ze moeten draaien.

Het script vult de tussentabel aan vanuit ap_timeline.liked/boosted. Idempotent
(INSERT OR IGNORE + de UNIQUE), met --dry-run, en het faalt met exit 1 als er
achteraf nog een vlag zonder tegenhanger is of als het aantal toegevoegde rijen
afwijkt van de meting vooraf. Dat laatste is het punt: je wilt kunnen zien dat je
precies het gemeten aantal hebt aangevuld en niet meer.

De controle kijkt per SOORT, niet per rij. Een post kan een like-rij in de
tussentabel hebben en toch een boost-vlag missen; de samenvattende meting uit
fase 0 telt die als "in orde" en dit script niet.

Gedraaid op dev: 13 rijen aangevuld (10 like, 3 boost), tussentabel 3 -> 16,
daarna nul vlaggen zonder tegenhanger en nul tussentabel-rijen zonder vlag. Er
staat een backup naast de database (database.sqlite.voor-backfill).

De oorspronkelijke reactiedatum is niet te herstellen -- wanneer je reageerde is
nergens vastgelegd, dus de backfill-rijen dragen de datum van nu. Dat staat ook
in de kop van het script.

Co-Authored-By: Claude Opus 5 <noreply@…>

  • Property mode set to 100644
File size: 3.5 KB
Line 
1#!/usr/bin/env node
2//
3// Backfill: reacties uit de afgeleide kolommen naar de tussentabel (shaer-9e9).
4//
5// "Heb ik hierop gereageerd" stond in Klonkt op twee plekken die door
6// verschillende routes werden gevuld: ap_timeline.liked/boosted (web-tijdlijn)
7// en ap_my_reactions (interact-pagina). Sinds fase 1 schrijft setReaction ze
8// allebei, maar alles van VOOR die wijziging staat nog maar in een van de twee.
9//
10// Dit script vult de tussentabel aan met wat alleen in de kolommen staat. Het
11// moet draaien VOORDAT de lezers naar de tussentabel wijzen (fase 2), anders
12// verdwijnen die reacties uit beeld -- stil, want een ontbrekende rij is geen
13// fout.
14//
15// node scripts/backfill-reactions.mjs --dry-run
16// node scripts/backfill-reactions.mjs
17//
18// Veilig om opnieuw te draaien: INSERT OR IGNORE plus de UNIQUE op
19// (site_slug, target_uri, kind) maakt het idempotent.
20//
21// Wat het NIET kan: de oorspronkelijke reactiedatum herstellen. Wanneer je
22// reageerde is nergens vastgelegd, dus de backfill-rijen krijgen de datum van
23// nu. Dat is geen verlies dat te repareren valt, wel iets om te weten als je
24// ooit op created_at gaat sorteren.
25
26import db from '../src/config/database.js';
27
28const dryRun = process.argv.includes('--dry-run');
29
30const scheef = (kind, kolom) => db.prepare(`
31 SELECT t.slug, t.id FROM ap_timeline t
32 WHERE t.${kolom} = 1
33 AND NOT EXISTS (SELECT 1 FROM ap_my_reactions r
34 WHERE r.site_slug = t.slug AND r.target_uri = t.id AND r.kind = ?)
35`).all(kind);
36
37const voor = {
38 tussentabel: db.prepare('SELECT COUNT(*) AS n FROM ap_my_reactions').get().n,
39 liked: db.prepare('SELECT COUNT(*) AS n FROM ap_timeline WHERE liked = 1').get().n,
40 boosted: db.prepare('SELECT COUNT(*) AS n FROM ap_timeline WHERE boosted = 1').get().n,
41};
42const teDoen = { like: scheef('like', 'liked'), boost: scheef('boost', 'boosted') };
43
44console.log('vooraf :', JSON.stringify(voor));
45console.log('aan te vullen: like =', teDoen.like.length, ', boost =', teDoen.boost.length);
46
47if (dryRun) {
48 for (const [kind, rijen] of Object.entries(teDoen)) {
49 for (const r of rijen.slice(0, 10)) console.log(` [dry-run] ${kind} ${r.slug} ${r.id}`);
50 if (rijen.length > 10) console.log(` ... en nog ${rijen.length - 10}`);
51 }
52 console.log('\n--dry-run: niets geschreven.');
53 process.exit(0);
54}
55
56const ins = db.prepare('INSERT OR IGNORE INTO ap_my_reactions (site_slug, target_uri, kind) VALUES (?,?,?)');
57let toegevoegd = 0;
58db.transaction(() => {
59 for (const [kind, rijen] of Object.entries(teDoen)) {
60 for (const r of rijen) toegevoegd += ins.run(r.slug, r.id, kind).changes;
61 }
62})();
63
64const na = {
65 tussentabel: db.prepare('SELECT COUNT(*) AS n FROM ap_my_reactions').get().n,
66 scheef: db.prepare(`
67 SELECT COUNT(*) AS n FROM ap_timeline t
68 WHERE (t.liked = 1 OR t.boosted = 1)
69 AND NOT EXISTS (SELECT 1 FROM ap_my_reactions r
70 WHERE r.site_slug = t.slug AND r.target_uri = t.id)`).get().n,
71};
72
73console.log('toegevoegd:', toegevoegd);
74console.log('achteraf :', JSON.stringify(na));
75
76// De controle die telt: is er nog een vlag zonder tegenhanger? Zo ja, dan is de
77// tussentabel nog niet veilig als bron en mag fase 2 niet.
78if (na.scheef !== 0) {
79 console.error(`\nLET OP: nog ${na.scheef} rij(en) met een vlag zonder tegenhanger. Fase 2 mag NIET.`);
80 process.exit(1);
81}
82if (toegevoegd !== teDoen.like.length + teDoen.boost.length) {
83 console.error('\nLET OP: het aantal toegevoegde rijen wijkt af van de meting vooraf.');
84 process.exit(1);
85}
86console.log('\nOK: elke vlag heeft nu een tegenhanger in de tussentabel.');
Note: See TracBrowser for help on using the repository browser.