Fix trends

This commit is contained in:
Alex Gleason 2024-05-21 17:57:59 -05:00
parent b2a5ff3eaf
commit 5822253796
No known key found for this signature in database
GPG Key ID: 7211D1F99744FBB7
4 changed files with 27 additions and 19 deletions

View File

@ -5,13 +5,12 @@ import { Conf } from '@/config.ts';
import { Time } from '@/utils.ts'; import { Time } from '@/utils.ts';
import { stripTime } from '@/utils/time.ts'; import { stripTime } from '@/utils/time.ts';
import { TrendsWorker } from '@/workers/trends.ts'; import { TrendsWorker } from '@/workers/trends.ts';
import { Context } from 'hono';
await TrendsWorker.open('data/trends.sqlite3'); await TrendsWorker.open('data/trends.sqlite3');
const limitSchema = z.coerce.number().catch(10).transform((value) => Math.min(Math.max(value, 0), 20)); const limitSchema = z.coerce.number().catch(10).transform((value) => Math.min(Math.max(value, 0), 20));
const trendingTagsController: AppController = async (c: Context) => { const trendingTagsController: AppController = async (c) => {
const limit = limitSchema.parse(c.req.query('limit')); const limit = limitSchema.parse(c.req.query('limit'));
if (limit < 1) return c.json([]); if (limit < 1) return c.json([]);
@ -28,7 +27,7 @@ const trendingTagsController: AppController = async (c: Context) => {
return c.json( return c.json(
await Promise.all(tags.map(async ({ tag, uses, accounts }) => ({ await Promise.all(tags.map(async ({ tag, uses, accounts }) => ({
tag, name: tag,
url: Conf.local(`/tags/${tag}`), url: Conf.local(`/tags/${tag}`),
history: [ history: [
// Use the full 24h query for the current day. Then use `offset: 1` to adjust for this below. // Use the full 24h query for the current day. Then use `offset: 1` to adjust for this below.

View File

@ -177,7 +177,7 @@ async function trackHashtags(event: NostrEvent): Promise<void> {
try { try {
debug('tracking tags:', JSON.stringify(tags)); debug('tracking tags:', JSON.stringify(tags));
await TrendsWorker.addTagUsages(event.pubkey, tags, date.valueOf()); await TrendsWorker.addTagUsages(event.pubkey, tags, date);
} catch (_e) { } catch (_e) {
// do nothing // do nothing
} }

View File

@ -1,4 +1,5 @@
import { assertEquals } from '@std/assert'; import { assertEquals } from '@std/assert';
import { TrendsWorker } from './trends.ts'; import { TrendsWorker } from './trends.ts';
await TrendsWorker.open(':memory:'); await TrendsWorker.open(':memory:');

View File

@ -27,7 +27,7 @@ interface TagsDB {
tag_usages: { tag_usages: {
tag: string; tag: string;
pubkey8: string; pubkey8: string;
inserted_at: number; inserted_at: Date;
}; };
} }
@ -41,6 +41,13 @@ export const TrendsWorker = {
}), }),
}); });
await sql`PRAGMA synchronous = normal`.execute(kysely);
await sql`PRAGMA temp_store = memory`.execute(kysely);
await sql`PRAGMA foreign_keys = ON`.execute(kysely);
await sql`PRAGMA auto_vacuum = FULL`.execute(kysely);
await sql`PRAGMA journal_mode = WAL`.execute(kysely);
await sql`PRAGMA mmap_size = 50000000`.execute(kysely);
await kysely.schema await kysely.schema
.createTable('tag_usages') .createTable('tag_usages')
.ifNotExists() .ifNotExists()
@ -64,19 +71,19 @@ export const TrendsWorker = {
}, },
/** Gets the most used hashtags between the date range. */ /** Gets the most used hashtags between the date range. */
async getTrendingTags({ since, until, limit = 10, threshold = 3 }: GetTrendingTagsOpts): Promise<{ getTrendingTags({ since, until, limit = 10, threshold = 3 }: GetTrendingTagsOpts): Promise<{
tag: string; tag: string;
accounts: number; accounts: number;
uses: number; uses: number;
}[]> { }[]> {
return await kysely.selectFrom('tag_usages') return kysely.selectFrom('tag_usages')
.select(({ fn }) => [ .select(({ fn }) => [
'tag', 'tag',
fn.agg<number>('count', ['pubkey8']).distinct().as('accounts'), fn.agg<number>('count', ['pubkey8']).distinct().as('accounts'),
fn.countAll<number>().as('uses'), fn.countAll<number>().as('uses'),
]) ])
.where('inserted_at', '>=', since.valueOf()) .where('inserted_at', '>=', since)
.where('inserted_at', '<', until.valueOf()) .where('inserted_at', '<', until)
.groupBy('tag') .groupBy('tag')
.having((c) => c(c.fn.agg('count', ['pubkey8']).distinct(), '>=', threshold)) .having((c) => c(c.fn.agg('count', ['pubkey8']).distinct(), '>=', threshold))
.orderBy((c) => c.fn.agg('count', ['pubkey8']).distinct(), 'desc') .orderBy((c) => c.fn.agg('count', ['pubkey8']).distinct(), 'desc')
@ -92,15 +99,15 @@ export const TrendsWorker = {
const result = await kysely const result = await kysely
.selectFrom('tag_usages') .selectFrom('tag_usages')
.select(({ fn }) => [ .select(({ fn }) => [
'inserted_at', sql<number>`date(inserted_at)`.as('day'),
fn.agg<number>('count', ['pubkey8']).distinct().as('accounts'), fn.agg<number>('count', ['pubkey8']).distinct().as('accounts'),
fn.countAll<number>().as('uses'), fn.countAll<number>().as('uses'),
]) ])
.where('tag', '=', tag) .where('tag', '=', tag)
.where('inserted_at', '>=', since.valueOf()) .where('inserted_at', '>=', since)
.where('inserted_at', '<', until.valueOf()) .where('inserted_at', '<', until)
.groupBy(sql`inserted_at`) .groupBy(sql`date(inserted_at)`)
.orderBy(sql`inserted_at`, 'desc') .orderBy(sql`date(inserted_at)`, 'desc')
.limit(limit) .limit(limit)
.offset(offset) .offset(offset)
.execute(); .execute();
@ -113,15 +120,16 @@ export const TrendsWorker = {
// Fill in missing dates with 0 usages. // Fill in missing dates with 0 usages.
return dateRange.map((day) => { return dateRange.map((day) => {
const data = result.find((item) => new Date(item.inserted_at).getTime() === day.getTime()); const data = result.find((item) => new Date(item.day).getTime() === day.getTime());
if (data) { if (data) {
return { ...data, day: new Date(data.inserted_at) }; return { ...data, day: new Date(data.day) };
} else {
return { day, accounts: 0, uses: 0 };
} }
return { day, accounts: 0, uses: 0 };
}); });
}, },
async addTagUsages(pubkey: string, hashtags: string[], inserted_at = new Date().valueOf()): Promise<void> { async addTagUsages(pubkey: string, hashtags: string[], inserted_at = new Date()): Promise<void> {
const pubkey8 = NSchema.id().parse(pubkey).substring(0, 8); const pubkey8 = NSchema.id().parse(pubkey).substring(0, 8);
const tags = hashtagSchema.array().min(1).parse(hashtags); const tags = hashtagSchema.array().min(1).parse(hashtags);
@ -134,7 +142,7 @@ export const TrendsWorker = {
async cleanupTagUsages(until: Date): Promise<void> { async cleanupTagUsages(until: Date): Promise<void> {
await kysely await kysely
.deleteFrom('tag_usages') .deleteFrom('tag_usages')
.where('inserted_at', '<', until.valueOf()) .where('inserted_at', '<', until)
.execute(); .execute();
}, },
}; };