Fix trends
This commit is contained in:
parent
b2a5ff3eaf
commit
5822253796
|
@ -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.
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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:');
|
||||||
|
|
|
@ -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();
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue