ditto/src/stats.ts

106 lines
3.2 KiB
TypeScript
Raw Normal View History

import { db, type EventStatsRow, type PubkeyStatsRow } from '@/db.ts';
import { Event, findReplyTag } from '@/deps.ts';
2023-12-08 00:43:24 +00:00
2023-12-10 17:10:11 +00:00
type PubkeyStat = keyof Omit<PubkeyStatsRow, 'pubkey'>;
type EventStat = keyof Omit<EventStatsRow, 'event_id'>;
2023-12-08 00:43:24 +00:00
type PubkeyStatDiff = ['pubkey_stats', pubkey: string, stat: PubkeyStat, diff: number];
type EventStatDiff = ['event_stats', eventId: string, stat: EventStat, diff: number];
type StatDiff = PubkeyStatDiff | EventStatDiff;
2023-12-08 00:43:24 +00:00
/** Store stats for the event in LMDB. */
2023-12-10 17:10:11 +00:00
function updateStats(event: Event) {
const statDiffs = getStatsDiff(event);
if (!statDiffs.length) return;
const pubkeyDiffs = statDiffs.filter(([table]) => table === 'pubkey_stats') as PubkeyStatDiff[];
const eventDiffs = statDiffs.filter(([table]) => table === 'event_stats') as EventStatDiff[];
return db.transaction().execute(() => {
return Promise.all([
pubkeyStatsQuery(pubkeyDiffs).execute(),
eventStatsQuery(eventDiffs).execute(),
]);
});
2023-12-10 17:10:11 +00:00
}
/** Calculate stats changes ahead of time so we can build an efficient query. */
function getStatsDiff(event: Event): StatDiff[] {
const statDiffs: StatDiff[] = [];
const firstE = event.tags.find(([name]) => name === 'e')?.[1];
2023-12-10 19:58:35 +00:00
const inReplyToId = findReplyTag(event as Event<1>)?.[1];
2023-12-08 00:43:24 +00:00
switch (event.kind) {
2023-12-10 17:10:11 +00:00
case 1:
statDiffs.push(['pubkey_stats', event.pubkey, 'notes_count', 1]);
2023-12-10 19:58:35 +00:00
if (inReplyToId) {
statDiffs.push(['event_stats', inReplyToId, 'replies_count', 1]);
}
break;
2023-12-08 00:43:24 +00:00
case 6:
if (firstE) {
statDiffs.push(['event_stats', firstE, 'reposts_count', 1]);
}
break;
2023-12-08 00:43:24 +00:00
case 7:
if (firstE) {
statDiffs.push(['event_stats', firstE, 'reactions_count', 1]);
}
2023-12-08 00:43:24 +00:00
}
return statDiffs;
2023-12-08 00:43:24 +00:00
}
function pubkeyStatsQuery(diffs: PubkeyStatDiff[]) {
const values: PubkeyStatsRow[] = diffs.map(([_, pubkey, stat, diff]) => {
2023-12-10 19:12:35 +00:00
const row: PubkeyStatsRow = {
pubkey,
followers_count: 0,
following_count: 0,
notes_count: 0,
};
row[stat] = diff;
return row;
});
2023-12-10 17:10:11 +00:00
return db.insertInto('pubkey_stats')
2023-12-10 19:12:35 +00:00
.values(values)
2023-12-10 17:10:11 +00:00
.onConflict((oc) =>
oc
.column('pubkey')
.doUpdateSet((eb) => ({
followers_count: eb('followers_count', '+', eb.ref('excluded.followers_count')),
following_count: eb('following_count', '+', eb.ref('excluded.following_count')),
notes_count: eb('notes_count', '+', eb.ref('excluded.notes_count')),
2023-12-10 17:10:11 +00:00
}))
);
2023-12-08 00:43:24 +00:00
}
function eventStatsQuery(diffs: EventStatDiff[]) {
const values: EventStatsRow[] = diffs.map(([_, event_id, stat, diff]) => {
2023-12-10 19:12:35 +00:00
const row: EventStatsRow = {
event_id,
replies_count: 0,
reposts_count: 0,
reactions_count: 0,
};
row[stat] = diff;
return row;
});
return db.insertInto('event_stats')
2023-12-10 19:12:35 +00:00
.values(values)
.onConflict((oc) =>
oc
.column('event_id')
.doUpdateSet((eb) => ({
replies_count: eb('replies_count', '+', eb.ref('excluded.replies_count')),
reposts_count: eb('reposts_count', '+', eb.ref('excluded.reposts_count')),
reactions_count: eb('reactions_count', '+', eb.ref('excluded.reactions_count')),
}))
);
}
2023-12-10 17:10:11 +00:00
export { updateStats };