refactor(trends.ts): move logic one level up, rename 'languagesIds' to 'values', remove WITH SQL statement

This commit is contained in:
P. Reis 2024-10-03 19:40:29 -03:00
parent a5def9fa6c
commit 67b0684a81

View file

@ -1,5 +1,4 @@
import { LanguageCode } from 'iso-639-1'; import { NostrFilter } from '@nostrify/nostrify';
import { NostrEvent, NostrFilter } from '@nostrify/nostrify';
import { Stickynotes } from '@soapbox/stickynotes'; import { Stickynotes } from '@soapbox/stickynotes';
import { Kysely, sql } from 'kysely'; import { Kysely, sql } from 'kysely';
@ -20,48 +19,39 @@ export async function getTrendingTagValues(
tagNames: string[], tagNames: string[],
/** Filter of eligible events. */ /** Filter of eligible events. */
filter: NostrFilter, filter: NostrFilter,
/** Results must be inside 'languagesIds' */ /** If present, only tag values in this list are permitted to trend. */
languagesIds?: string[], values?: string[],
): Promise<{ value: string; authors: number; uses: number }[]> { ): Promise<{ value: string; authors: number; uses: number }[]> {
let query = kysely.with('trends', (db) => { let query = kysely
let query = db .selectFrom([
.selectFrom([ 'nostr_events',
'nostr_events', sql<{ key: string; value: string }>`jsonb_each_text(nostr_events.tags_index)`.as('kv'),
sql<{ key: string; value: string }>`jsonb_each_text(nostr_events.tags_index)`.as('kv'), sql<{ key: string; value: string }>`jsonb_array_elements_text(kv.value::jsonb)`.as('element'),
sql<{ key: string; value: string }>`jsonb_array_elements_text(kv.value::jsonb)`.as('element'), ])
]) .select(({ fn }) => [
.select(({ fn }) => [ fn<string>('lower', ['element.value']).as('value'),
fn<string>('lower', ['element.value']).as('value'), fn.agg<number>('count', ['nostr_events.pubkey']).distinct().as('authors'),
fn.agg<number>('count', ['nostr_events.pubkey']).distinct().as('authors'), fn.countAll<number>().as('uses'),
fn.countAll<number>().as('uses'), ])
]) .where('kv.key', '=', (eb) => eb.fn.any(eb.val(tagNames)))
.where('kv.key', '=', (eb) => eb.fn.any(eb.val(tagNames))) .groupBy((eb) => eb.fn<string>('lower', ['element.value']))
.groupBy((eb) => eb.fn<string>('lower', ['element.value'])) .orderBy('authors desc').orderBy('uses desc');
.orderBy((eb) => eb.fn.agg('count', ['nostr_events.pubkey']).distinct(), 'desc');
if (filter.kinds) { if (filter.kinds) {
query = query.where('nostr_events.kind', '=', ({ fn, val }) => fn.any(val(filter.kinds))); query = query.where('nostr_events.kind', '=', ({ fn, val }) => fn.any(val(filter.kinds)));
} }
if (filter.authors) { if (filter.authors) {
query = query.where('nostr_events.pubkey', '=', ({ fn, val }) => fn.any(val(filter.authors))); query = query.where('nostr_events.pubkey', '=', ({ fn, val }) => fn.any(val(filter.authors)));
} }
if (typeof filter.since === 'number') { if (typeof filter.since === 'number') {
query = query.where('nostr_events.created_at', '>=', filter.since); query = query.where('nostr_events.created_at', '>=', filter.since);
} }
if (typeof filter.until === 'number') { if (typeof filter.until === 'number') {
query = query.where('nostr_events.created_at', '<=', filter.until); query = query.where('nostr_events.created_at', '<=', filter.until);
} }
return query; if (values) {
}) query = query.where('element.value', 'in', values);
.selectFrom(['trends'])
.select(['value', 'authors', 'uses']);
if (languagesIds) {
query = query.where('trends.value', 'in', languagesIds);
} }
query = query.orderBy('authors desc').orderBy('uses desc');
if (typeof filter.limit === 'number') { if (typeof filter.limit === 'number') {
query = query.limit(filter.limit); query = query.limit(filter.limit);
} }
@ -83,7 +73,7 @@ export async function updateTrendingTags(
limit: number, limit: number,
extra = '', extra = '',
aliases?: string[], aliases?: string[],
language?: LanguageCode, values?: string[],
) { ) {
console.info(`Updating trending ${l}...`); console.info(`Updating trending ${l}...`);
const kysely = await Storages.kysely(); const kysely = await Storages.kysely();
@ -94,25 +84,15 @@ export async function updateTrendingTags(
const tagNames = aliases ? [tagName, ...aliases] : [tagName]; const tagNames = aliases ? [tagName, ...aliases] : [tagName];
let languagesIds: NostrEvent['id'][] = [];
if (language) {
const result = (await kysely.selectFrom('nostr_events')
.select('id')
.where('language', '=', language)
.where('nostr_events.created_at', '>=', yesterday)
.where('nostr_events.created_at', '<=', now)
.execute()).map((event) => event.id);
languagesIds = result;
}
try { try {
const trends = await getTrendingTagValues(kysely, tagNames, { const trends = await getTrendingTagValues(kysely, tagNames, {
kinds, kinds,
since: yesterday, since: yesterday,
until: now, until: now,
limit, limit,
}, languagesIds); }, values);
console.log(trends);
if (!trends.length) { if (!trends.length) {
console.info(`No trending ${l} found. Skipping.`); console.info(`No trending ${l} found. Skipping.`);
return; return;
@ -125,7 +105,7 @@ export async function updateTrendingTags(
content: '', content: '',
tags: [ tags: [
['L', 'pub.ditto.trends'], ['L', 'pub.ditto.trends'],
['l', languagesIds.length ? `${l}.${language}` : l, 'pub.ditto.trends'], ['l', l, 'pub.ditto.trends'],
...trends.map(({ value, authors, uses }) => [tagName, value, extra, authors.toString(), uses.toString()]), ...trends.map(({ value, authors, uses }) => [tagName, value, extra, authors.toString(), uses.toString()]),
], ],
created_at: Math.floor(Date.now() / 1000), created_at: Math.floor(Date.now() / 1000),
@ -150,16 +130,30 @@ export function updateTrendingZappedEvents(): Promise<void> {
/** Update trending events. */ /** Update trending events. */
export async function updateTrendingEvents(): Promise<void> { export async function updateTrendingEvents(): Promise<void> {
const languages = Conf.preferredLanguages; const results: Promise<void>[] = [
if (!languages) return updateTrendingTags('#e', 'e', [1, 6, 7, 9735], 40, Conf.relay, ['q']); updateTrendingTags('#e', 'e', [1, 6, 7, 9735], 40, Conf.relay, ['q']),
];
const promise: Promise<void>[] = []; const kysely = await Storages.kysely();
for (const language of languages) { for (const language of Conf.preferredLanguages ?? []) {
promise.push(updateTrendingTags('#e', 'e', [1, 6, 7, 9735], 40, Conf.relay, ['q'], language)); const yesterday = Math.floor((Date.now() - Time.days(1)) / 1000);
const now = Math.floor(Date.now() / 1000);
const rows = await kysely
.selectFrom('nostr_events')
.select('nostr_events.id')
.where('nostr_events.language', '=', language)
.where('nostr_events.created_at', '>=', yesterday)
.where('nostr_events.created_at', '<=', now)
.execute();
const ids = rows.map((row) => row.id);
results.push(updateTrendingTags(`#e.${language}`, 'e', [1, 6, 7, 9735], 40, Conf.relay, ['q'], ids));
} }
await Promise.allSettled(promise); await Promise.allSettled(results);
} }
/** Update trending hashtags. */ /** Update trending hashtags. */