From 40c187680e8580c2cec374e8dea87ca94bd17152 Mon Sep 17 00:00:00 2001 From: "P. Reis" Date: Fri, 13 Sep 2024 10:22:05 -0300 Subject: [PATCH 01/20] feat: create author_search table --- src/db/migrations/032_add_author_search.ts | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 src/db/migrations/032_add_author_search.ts diff --git a/src/db/migrations/032_add_author_search.ts b/src/db/migrations/032_add_author_search.ts new file mode 100644 index 00000000..d5a93c06 --- /dev/null +++ b/src/db/migrations/032_add_author_search.ts @@ -0,0 +1,18 @@ +import { Kysely, sql } from 'kysely'; + +export async function up(db: Kysely): Promise { + await db.schema + .createTable('author_search') + .addColumn('pubkey', 'char(64)', (col) => col.primaryKey()) + .addColumn('search', 'text', (col) => col.notNull()) + .ifNotExists() + .execute(); + + await sql`CREATE EXTENSION IF NOT EXISTS pg_trgm;`.execute(db); + await sql`CREATE INDEX author_search_search_idx ON author_search USING GIN (search gin_trgm_ops);`.execute(db); +} + +export async function down(db: Kysely): Promise { + await db.schema.dropIndex('author_search_search_idx').ifExists().execute(); + await db.schema.dropTable('author_search').execute(); +} From 8bc8712cf37d542258c9eecf4179d99e0ec8496d Mon Sep 17 00:00:00 2001 From: "P. Reis" Date: Fri, 13 Sep 2024 10:23:08 -0300 Subject: [PATCH 02/20] feat: create and add author_search interface to DittoTables --- src/db/DittoTables.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/db/DittoTables.ts b/src/db/DittoTables.ts index a62c485d..38f7249c 100644 --- a/src/db/DittoTables.ts +++ b/src/db/DittoTables.ts @@ -6,6 +6,7 @@ export interface DittoTables extends NPostgresSchema { event_stats: EventStatsRow; pubkey_domains: PubkeyDomainRow; event_zaps: EventZapRow; + author_search: AuthorSearch; } interface AuthorStatsRow { @@ -47,3 +48,8 @@ interface EventZapRow { amount_millisats: number; comment: string; } + +interface AuthorSearch { + pubkey: string; + search: string; +} From b5aefdd93e00d2921005f763013dccbbd7dde759 Mon Sep 17 00:00:00 2001 From: "P. Reis" Date: Fri, 13 Sep 2024 10:24:33 -0300 Subject: [PATCH 03/20] feat: add pg_trgm extension in PGlite constructor --- src/db/adapters/DittoPglite.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/db/adapters/DittoPglite.ts b/src/db/adapters/DittoPglite.ts index 4ec7d8a5..0e93075d 100644 --- a/src/db/adapters/DittoPglite.ts +++ b/src/db/adapters/DittoPglite.ts @@ -1,4 +1,5 @@ import { PGlite } from '@electric-sql/pglite'; +import { pg_trgm } from '@electric-sql/pglite/contrib/pg_trgm'; import { PgliteDialect } from '@soapbox/kysely-pglite'; import { Kysely } from 'kysely'; @@ -10,7 +11,7 @@ export class DittoPglite { static create(databaseUrl: string): DittoDatabase { const kysely = new Kysely({ dialect: new PgliteDialect({ - database: new PGlite(databaseUrl), + database: new PGlite(databaseUrl, { extensions: { pg_trgm } }), }), log: KyselyLogger, }); From c03ea07dcb253e6ff1b59c403133ee72cea2f7bf Mon Sep 17 00:00:00 2001 From: "P. Reis" Date: Fri, 13 Sep 2024 10:27:12 -0300 Subject: [PATCH 04/20] feat: create getPubkeysBySearch() function and use it inside searchEvents() function --- src/controllers/api/search.ts | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/src/controllers/api/search.ts b/src/controllers/api/search.ts index 9bddc336..19e72cac 100644 --- a/src/controllers/api/search.ts +++ b/src/controllers/api/search.ts @@ -1,8 +1,10 @@ import { NostrEvent, NostrFilter, NSchema as n } from '@nostrify/nostrify'; import { nip19 } from 'nostr-tools'; +import { Kysely, sql } from 'kysely'; import { z } from 'zod'; import { AppController } from '@/app.ts'; +import { DittoTables } from '@/db/DittoTables.ts'; import { booleanParamSchema } from '@/schema.ts'; import { Storages } from '@/storages.ts'; import { hydrateEvents } from '@/storages/hydrate.ts'; @@ -89,9 +91,21 @@ async function searchEvents({ q, type, limit, account_id }: SearchQuery, signal: filter.authors = [account_id]; } + const filter2: NostrFilter = { + kinds: [0], + limit, + }; + if (type === 'accounts') { + const kysely = await Storages.kysely(); + + const pubkeys = await getPubkeysBySearch(kysely, { q, limit }); + + filter2.authors = pubkeys; // if pubkeys is empty the filter 2 will be discarded + } + const store = await Storages.search(); - return store.query([filter], { signal }) + return store.query([filter, filter2], { signal }) .then((events) => hydrateEvents({ events, store, signal })); } @@ -170,4 +184,16 @@ async function getLookupFilters({ q, type, resolve }: SearchQuery, signal: Abort return []; } -export { searchController }; +/** Get pubkeys whose name and NIP-05 is similar to 'q' */ +async function getPubkeysBySearch(kysely: Kysely, { q, limit }: Pick) { + const pubkeys = (await sql` + SELECT *, word_similarity(${q}, search) AS sml + FROM author_search + WHERE ${q} % search + ORDER BY sml DESC, search LIMIT ${limit} + `.execute(kysely)).rows.map((row) => (row as { pubkey: string }).pubkey); + + return pubkeys; +} + +export { getPubkeysBySearch, searchController }; From 9d2667679fdac0adc956b4927ddd8417e4f08500 Mon Sep 17 00:00:00 2001 From: "P. Reis" Date: Fri, 13 Sep 2024 10:28:54 -0300 Subject: [PATCH 05/20] feat(pipeline.ts): create handleAuthorSearch() function --- src/pipeline.ts | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/pipeline.ts b/src/pipeline.ts index 88e5f29b..9d1a8038 100644 --- a/src/pipeline.ts +++ b/src/pipeline.ts @@ -57,6 +57,7 @@ async function handleEvent(event: DittoEvent, signal: AbortSignal): Promise, event: NostrEvent) { } } +async function handleAuthorSearch(kysely: Kysely, event: NostrEvent) { + if (event.kind !== 0) return; + const { name, nip05 } = n.json().pipe(n.metadata()).catch({}).parse(event.content); + const search = [name, nip05].filter(Boolean).join(' ').trim(); + + try { + await kysely.insertInto('author_search').values({ + pubkey: event.pubkey, + search, + }).onConflict( + (oc) => + oc.column('pubkey') + .doUpdateSet({ search }), + ) + .execute(); + } catch { + // do nothing + } +} + export { handleEvent, handleZaps }; From e1cd1777e3d76b8f3b67d8d415215dc7e70ba424 Mon Sep 17 00:00:00 2001 From: "P. Reis" Date: Fri, 13 Sep 2024 10:30:10 -0300 Subject: [PATCH 06/20] test: add author_search table in createTestDB to drop it after use --- src/test.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test.ts b/src/test.ts index 45946f00..00bf4354 100644 --- a/src/test.ts +++ b/src/test.ts @@ -63,6 +63,7 @@ export async function createTestDB() { 'pubkey_domains', 'nostr_events', 'event_zaps', + 'author_search', ] ) { await kysely.schema.dropTable(table).ifExists().cascade().execute(); From a6f1098bc6fc52dee99de61983399961af5c1aa8 Mon Sep 17 00:00:00 2001 From: "P. Reis" Date: Fri, 13 Sep 2024 10:32:05 -0300 Subject: [PATCH 07/20] test: getPubkeysBySearch() function --- src/controllers/api/search.test.ts | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 src/controllers/api/search.test.ts diff --git a/src/controllers/api/search.test.ts b/src/controllers/api/search.test.ts new file mode 100644 index 00000000..2c5e91bd --- /dev/null +++ b/src/controllers/api/search.test.ts @@ -0,0 +1,21 @@ +import { assertEquals } from '@std/assert'; + +import { createTestDB } from '@/test.ts'; +import { getPubkeysBySearch } from '@/controllers/api/search.ts'; + +Deno.test('fuzzy search works', async () => { + await using db = await createTestDB(); + + await db.kysely.insertInto('author_search').values({ + pubkey: '47259076c85f9240e852420d7213c95e95102f1de929fb60f33a2c32570c98c4', + search: 'patrickReiis patrickdosreis.com', + }).execute(); + + assertEquals(await getPubkeysBySearch(db.kysely, { q: 'pat rick', limit: 1 }), []); + assertEquals(await getPubkeysBySearch(db.kysely, { q: 'patrick dos reis', limit: 1 }), [ + '47259076c85f9240e852420d7213c95e95102f1de929fb60f33a2c32570c98c4', + ]); + assertEquals(await getPubkeysBySearch(db.kysely, { q: 'dosreis.com', limit: 1 }), [ + '47259076c85f9240e852420d7213c95e95102f1de929fb60f33a2c32570c98c4', + ]); +}); From 24d909fd28383fda2f10934e7246483fe1be52ff Mon Sep 17 00:00:00 2001 From: "P. Reis" Date: Fri, 13 Sep 2024 10:34:15 -0300 Subject: [PATCH 08/20] feat: create script to populate author_search table --- deno.json | 3 ++- scripts/db-populate-search.ts | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 scripts/db-populate-search.ts diff --git a/deno.json b/deno.json index 699ab620..0070b142 100644 --- a/deno.json +++ b/deno.json @@ -18,7 +18,8 @@ "stats:recompute": "deno run -A scripts/stats-recompute.ts", "soapbox": "curl -O https://dl.soapbox.pub/main/soapbox.zip && mkdir -p public && mv soapbox.zip public/ && cd public/ && unzip -o soapbox.zip && rm soapbox.zip", "trends": "deno run -A scripts/trends.ts", - "clean:deps": "deno cache --reload src/app.ts" + "clean:deps": "deno cache --reload src/app.ts", + "db:populate-search": "deno run -A scripts/db-populate-search.ts" }, "unstable": ["cron", "ffi", "kv", "worker-options"], "exclude": ["./public"], diff --git a/scripts/db-populate-search.ts b/scripts/db-populate-search.ts new file mode 100644 index 00000000..9c698c8f --- /dev/null +++ b/scripts/db-populate-search.ts @@ -0,0 +1,35 @@ +import { NSchema as n } from '@nostrify/nostrify'; +import { Storages } from '@/storages.ts'; +import { DittoTables } from '@/db/DittoTables.ts'; + +const kysely = await Storages.kysely(); +const stream = kysely + .selectFrom('nostr_events') + .select(['pubkey', 'content']) + .where('kind', '=', 0) + .stream(); + +const values: DittoTables['author_search'][] = []; + +for await (const author of stream) { + const { name, nip05 } = n.json().pipe(n.metadata()).catch({}).parse(author.content); + const search = [name, nip05].filter(Boolean).join(' ').trim(); + + values.push({ + pubkey: author.pubkey, + search, + }); +} + +try { + await kysely.insertInto('author_search').values(values).onConflict( + (oc) => + oc.column('pubkey') + .doUpdateSet((eb) => ({ search: eb.ref('excluded.search') })), + ) + .execute(); +} catch { + // do nothing +} + +Deno.exit(); From 935cc7c5a574ba9bf5cb148a14f4f0db3a932377 Mon Sep 17 00:00:00 2001 From: "P. Reis" Date: Fri, 13 Sep 2024 14:33:34 -0300 Subject: [PATCH 09/20] refactor: remove NIP-50 search if looking for accounts, use same filter --- src/controllers/api/search.ts | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/controllers/api/search.ts b/src/controllers/api/search.ts index 19e72cac..1a1bd867 100644 --- a/src/controllers/api/search.ts +++ b/src/controllers/api/search.ts @@ -91,21 +91,18 @@ async function searchEvents({ q, type, limit, account_id }: SearchQuery, signal: filter.authors = [account_id]; } - const filter2: NostrFilter = { - kinds: [0], - limit, - }; if (type === 'accounts') { const kysely = await Storages.kysely(); const pubkeys = await getPubkeysBySearch(kysely, { q, limit }); - filter2.authors = pubkeys; // if pubkeys is empty the filter 2 will be discarded + filter.authors = pubkeys; + filter.search = undefined; } const store = await Storages.search(); - return store.query([filter, filter2], { signal }) + return store.query([filter], { signal }) .then((events) => hydrateEvents({ events, store, signal })); } From be76197e3ac92ceae17a50db907fda4b4ee91a76 Mon Sep 17 00:00:00 2001 From: "P. Reis" Date: Fri, 13 Sep 2024 15:08:36 -0300 Subject: [PATCH 10/20] refactor: remove handleAuthorSearch() function and put its logic inside parseMetadata() function --- src/pipeline.ts | 44 +++++++++++++++++++++----------------------- 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/src/pipeline.ts b/src/pipeline.ts index b208e9e2..aeba0150 100644 --- a/src/pipeline.ts +++ b/src/pipeline.ts @@ -58,7 +58,6 @@ async function handleEvent(event: DittoEvent, signal: AbortSignal): Promise + oc.column('pubkey') + .doUpdateSet({ search }), + ) + .execute(); + } catch { + // do nothing + } + if (!nip05) return; // Fetch nip05. @@ -148,7 +167,6 @@ async function parseMetadata(event: NostrEvent, signal: AbortSignal): Promise, event: NostrEvent) { } } -async function handleAuthorSearch(kysely: Kysely, event: NostrEvent) { - if (event.kind !== 0) return; - const { name, nip05 } = n.json().pipe(n.metadata()).catch({}).parse(event.content); - const search = [name, nip05].filter(Boolean).join(' ').trim(); - - try { - await kysely.insertInto('author_search').values({ - pubkey: event.pubkey, - search, - }).onConflict( - (oc) => - oc.column('pubkey') - .doUpdateSet({ search }), - ) - .execute(); - } catch { - // do nothing - } -} - export { handleEvent, handleZaps }; From 69c21581310a720aed184fcb7f673282d7393d9f Mon Sep 17 00:00:00 2001 From: "P. Reis" Date: Fri, 13 Sep 2024 16:19:38 -0300 Subject: [PATCH 11/20] refactor: return ordered accounts by similarity relevance in searchEvents() function --- src/controllers/api/search.ts | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/src/controllers/api/search.ts b/src/controllers/api/search.ts index 1a1bd867..2555109b 100644 --- a/src/controllers/api/search.ts +++ b/src/controllers/api/search.ts @@ -91,19 +91,35 @@ async function searchEvents({ q, type, limit, account_id }: SearchQuery, signal: filter.authors = [account_id]; } + const pubkeys: string[] = []; if (type === 'accounts') { const kysely = await Storages.kysely(); - const pubkeys = await getPubkeysBySearch(kysely, { q, limit }); + pubkeys.push(...(await getPubkeysBySearch(kysely, { q, limit }))); + + if (!filter?.authors) filter.authors = pubkeys; + else filter.authors.push(...pubkeys); - filter.authors = pubkeys; filter.search = undefined; } const store = await Storages.search(); - return store.query([filter], { signal }) + const events = await store.query([filter], { signal }) .then((events) => hydrateEvents({ events, store, signal })); + + if (type !== 'accounts') return events; + + const orderedEvents: NostrEvent[] = events.map((event, index) => { + const pubkey = pubkeys[index]; + + const orderedEvent = events.find((e) => e.pubkey === pubkey); + if (orderedEvent) return orderedEvent; + + return event; + }); + + return orderedEvents; } /** Get event kinds to search from `type` query param. */ From 6387ee440cc9d7c773088f158c45cc17cab93a3c Mon Sep 17 00:00:00 2001 From: "P. Reis" Date: Fri, 13 Sep 2024 16:36:01 -0300 Subject: [PATCH 12/20] feat: return multiple accounts in searchController --- src/controllers/api/search.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/controllers/api/search.ts b/src/controllers/api/search.ts index 2555109b..c778bc4f 100644 --- a/src/controllers/api/search.ts +++ b/src/controllers/api/search.ts @@ -49,9 +49,8 @@ const searchController: AppController = async (c) => { if (event) { events = [event]; - } else { - events = await searchEvents(result.data, signal); } + events.push(...(await searchEvents(result.data, signal))); const viewerPubkey = await c.get('signer')?.getPublicKey(); From b3e56320a04ab866b9aeeb782e794d6252be1f49 Mon Sep 17 00:00:00 2001 From: "P. Reis" Date: Fri, 13 Sep 2024 18:41:20 -0300 Subject: [PATCH 13/20] feat(accountSearchController): return accounts in autocomplete form --- src/controllers/api/accounts.ts | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/controllers/api/accounts.ts b/src/controllers/api/accounts.ts index 9c635565..a8aa5d55 100644 --- a/src/controllers/api/accounts.ts +++ b/src/controllers/api/accounts.ts @@ -6,6 +6,7 @@ import { type AppController } from '@/app.ts'; import { Conf } from '@/config.ts'; import { getAuthor, getFollowedPubkeys } from '@/queries.ts'; import { booleanParamSchema, fileSchema } from '@/schema.ts'; +import { getPubkeysBySearch } from '@/controllers/api/search.ts'; import { Storages } from '@/storages.ts'; import { uploadFile } from '@/utils/upload.ts'; import { nostrNow } from '@/utils.ts'; @@ -115,6 +116,7 @@ const accountSearchQuerySchema = z.object({ const accountSearchController: AppController = async (c) => { const { signal } = c.req.raw; const { limit } = c.get('pagination'); + const kysely = await Storages.kysely(); const result = accountSearchQuerySchema.safeParse(c.req.query()); @@ -133,9 +135,22 @@ const accountSearchController: AppController = async (c) => { return c.json(pubkey ? [await accountFromPubkey(pubkey)] : []); } - const events = event ? [event] : await store.query([{ kinds: [0], search: query, limit }], { signal }); + const pubkeys = await getPubkeysBySearch(kysely, { q: query, limit }); - const accounts = await hydrateEvents({ events, store, signal }).then( + const events = event ? [event] : await store.query([{ kinds: [0], authors: pubkeys, limit }], { + signal, + }); + + const orderedEvents = events.map((event, index) => { + const pubkey = pubkeys[index]; + + const orderedEvent = events.find((e) => e.pubkey === pubkey); + if (orderedEvent) return orderedEvent; + + return event; + }); + + const accounts = await hydrateEvents({ events: orderedEvents, store, signal }).then( (events) => Promise.all( events.map((event) => renderAccount(event)), From 197b2c8c8b3b62e005134e405a430c55a0bbc0c5 Mon Sep 17 00:00:00 2001 From: "P. Reis" Date: Fri, 13 Sep 2024 18:49:20 -0300 Subject: [PATCH 14/20] refactor(populate search script): use store.req instead of streaming --- scripts/db-populate-search.ts | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/scripts/db-populate-search.ts b/scripts/db-populate-search.ts index 9c698c8f..beec2d52 100644 --- a/scripts/db-populate-search.ts +++ b/scripts/db-populate-search.ts @@ -2,23 +2,23 @@ import { NSchema as n } from '@nostrify/nostrify'; import { Storages } from '@/storages.ts'; import { DittoTables } from '@/db/DittoTables.ts'; +const store = await Storages.db(); const kysely = await Storages.kysely(); -const stream = kysely - .selectFrom('nostr_events') - .select(['pubkey', 'content']) - .where('kind', '=', 0) - .stream(); const values: DittoTables['author_search'][] = []; -for await (const author of stream) { - const { name, nip05 } = n.json().pipe(n.metadata()).catch({}).parse(author.content); - const search = [name, nip05].filter(Boolean).join(' ').trim(); +for await (const msg of store.req([{ kinds: [0] }])) { + if (msg[0] === 'EVENT') { + const { pubkey, content } = msg[2]; - values.push({ - pubkey: author.pubkey, - search, - }); + const { name, nip05 } = n.json().pipe(n.metadata()).catch({}).parse(content); + const search = [name, nip05].filter(Boolean).join(' ').trim(); + + values.push({ + pubkey: pubkey, + search, + }); + } } try { From f99ea7c33fa4cf385513a487df164b48ea1d3f71 Mon Sep 17 00:00:00 2001 From: "P. Reis" Date: Fri, 13 Sep 2024 18:57:47 -0300 Subject: [PATCH 15/20] refactor(getPubkeysBySearch): cast as string --- src/controllers/api/search.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/controllers/api/search.ts b/src/controllers/api/search.ts index c778bc4f..d62c08e8 100644 --- a/src/controllers/api/search.ts +++ b/src/controllers/api/search.ts @@ -198,12 +198,12 @@ async function getLookupFilters({ q, type, resolve }: SearchQuery, signal: Abort /** Get pubkeys whose name and NIP-05 is similar to 'q' */ async function getPubkeysBySearch(kysely: Kysely, { q, limit }: Pick) { - const pubkeys = (await sql` + const pubkeys = (await sql<{ pubkey: string }>` SELECT *, word_similarity(${q}, search) AS sml FROM author_search WHERE ${q} % search ORDER BY sml DESC, search LIMIT ${limit} - `.execute(kysely)).rows.map((row) => (row as { pubkey: string }).pubkey); + `.execute(kysely)).rows.map(({ pubkey }) => pubkey); return pubkeys; } From d7ae3722c8e3a4557e997ed787e52b99c449b19b Mon Sep 17 00:00:00 2001 From: "P. Reis" Date: Sun, 15 Sep 2024 16:52:49 -0300 Subject: [PATCH 16/20] refactor: insert each event per iteration in for loop - db:populate-search --- scripts/db-populate-search.ts | 33 +++++++++++++++------------------ 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/scripts/db-populate-search.ts b/scripts/db-populate-search.ts index beec2d52..e34aaa75 100644 --- a/scripts/db-populate-search.ts +++ b/scripts/db-populate-search.ts @@ -1,12 +1,9 @@ import { NSchema as n } from '@nostrify/nostrify'; import { Storages } from '@/storages.ts'; -import { DittoTables } from '@/db/DittoTables.ts'; const store = await Storages.db(); const kysely = await Storages.kysely(); -const values: DittoTables['author_search'][] = []; - for await (const msg of store.req([{ kinds: [0] }])) { if (msg[0] === 'EVENT') { const { pubkey, content } = msg[2]; @@ -14,22 +11,22 @@ for await (const msg of store.req([{ kinds: [0] }])) { const { name, nip05 } = n.json().pipe(n.metadata()).catch({}).parse(content); const search = [name, nip05].filter(Boolean).join(' ').trim(); - values.push({ - pubkey: pubkey, - search, - }); + try { + await kysely.insertInto('author_search').values({ + pubkey, + search, + }).onConflict( + (oc) => + oc.column('pubkey') + .doUpdateSet((eb) => ({ search: eb.ref('excluded.search') })), + ) + .execute(); + } catch { + // do nothing + } + } else { + break; } } -try { - await kysely.insertInto('author_search').values(values).onConflict( - (oc) => - oc.column('pubkey') - .doUpdateSet((eb) => ({ search: eb.ref('excluded.search') })), - ) - .execute(); -} catch { - // do nothing -} - Deno.exit(); From 3b8a800cd2bb4bbb2659776a96b228169044b474 Mon Sep 17 00:00:00 2001 From: "P. Reis" Date: Sun, 15 Sep 2024 17:27:56 -0300 Subject: [PATCH 17/20] refactor(search enchance): map over pubkeys instead of events --- src/controllers/api/accounts.ts | 15 +++++---------- src/controllers/api/search.ts | 15 +++++---------- 2 files changed, 10 insertions(+), 20 deletions(-) diff --git a/src/controllers/api/accounts.ts b/src/controllers/api/accounts.ts index a8aa5d55..4b684c33 100644 --- a/src/controllers/api/accounts.ts +++ b/src/controllers/api/accounts.ts @@ -137,20 +137,15 @@ const accountSearchController: AppController = async (c) => { const pubkeys = await getPubkeysBySearch(kysely, { q: query, limit }); - const events = event ? [event] : await store.query([{ kinds: [0], authors: pubkeys, limit }], { + let events = event ? [event] : await store.query([{ kinds: [0], authors: pubkeys, limit }], { signal, }); - const orderedEvents = events.map((event, index) => { - const pubkey = pubkeys[index]; + events = pubkeys.map((pubkey) => { + return events.find((event) => event.pubkey === pubkey); + }).filter((event) => event !== undefined); - const orderedEvent = events.find((e) => e.pubkey === pubkey); - if (orderedEvent) return orderedEvent; - - return event; - }); - - const accounts = await hydrateEvents({ events: orderedEvents, store, signal }).then( + const accounts = await hydrateEvents({ events, store, signal }).then( (events) => Promise.all( events.map((event) => renderAccount(event)), diff --git a/src/controllers/api/search.ts b/src/controllers/api/search.ts index d62c08e8..30bad8e9 100644 --- a/src/controllers/api/search.ts +++ b/src/controllers/api/search.ts @@ -104,21 +104,16 @@ async function searchEvents({ q, type, limit, account_id }: SearchQuery, signal: const store = await Storages.search(); - const events = await store.query([filter], { signal }) + let events = await store.query([filter], { signal }) .then((events) => hydrateEvents({ events, store, signal })); if (type !== 'accounts') return events; - const orderedEvents: NostrEvent[] = events.map((event, index) => { - const pubkey = pubkeys[index]; + events = pubkeys.map((pubkey) => { + return events.find((event) => event.pubkey === pubkey); + }).filter((event) => event !== undefined); - const orderedEvent = events.find((e) => e.pubkey === pubkey); - if (orderedEvent) return orderedEvent; - - return event; - }); - - return orderedEvents; + return events; } /** Get event kinds to search from `type` query param. */ From ed74b2464a56a51aa16a7a3f2db1353a4f610e8f Mon Sep 17 00:00:00 2001 From: "P. Reis" Date: Sun, 15 Sep 2024 17:42:26 -0300 Subject: [PATCH 18/20] refactor: write it like a normal if statement --- src/controllers/api/search.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/controllers/api/search.ts b/src/controllers/api/search.ts index 30bad8e9..e220413b 100644 --- a/src/controllers/api/search.ts +++ b/src/controllers/api/search.ts @@ -96,8 +96,11 @@ async function searchEvents({ q, type, limit, account_id }: SearchQuery, signal: pubkeys.push(...(await getPubkeysBySearch(kysely, { q, limit }))); - if (!filter?.authors) filter.authors = pubkeys; - else filter.authors.push(...pubkeys); + if (!filter?.authors) { + filter.authors = pubkeys; + } else { + filter.authors.push(...pubkeys); + } filter.search = undefined; } From c5711ea07173ffbbdab42319712df2497981b0fe Mon Sep 17 00:00:00 2001 From: "P. Reis" Date: Sun, 15 Sep 2024 17:42:58 -0300 Subject: [PATCH 19/20] refactor(accountSearchController): only reassign events if event is undefined --- src/controllers/api/accounts.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/controllers/api/accounts.ts b/src/controllers/api/accounts.ts index 4b684c33..6e3cac5b 100644 --- a/src/controllers/api/accounts.ts +++ b/src/controllers/api/accounts.ts @@ -141,10 +141,11 @@ const accountSearchController: AppController = async (c) => { signal, }); - events = pubkeys.map((pubkey) => { - return events.find((event) => event.pubkey === pubkey); - }).filter((event) => event !== undefined); - + if (!event) { + events = pubkeys.map((pubkey) => { + return events.find((event) => event.pubkey === pubkey); + }).filter((event) => event !== undefined); + } const accounts = await hydrateEvents({ events, store, signal }).then( (events) => Promise.all( From dc69f21e0bc93c2befabe7a43361c626bb46e653 Mon Sep 17 00:00:00 2001 From: "P. Reis" Date: Sun, 15 Sep 2024 17:46:10 -0300 Subject: [PATCH 20/20] refactor: write map function in a cleaner way --- src/controllers/api/accounts.ts | 6 +++--- src/controllers/api/search.ts | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/controllers/api/accounts.ts b/src/controllers/api/accounts.ts index 6e3cac5b..c946b697 100644 --- a/src/controllers/api/accounts.ts +++ b/src/controllers/api/accounts.ts @@ -142,9 +142,9 @@ const accountSearchController: AppController = async (c) => { }); if (!event) { - events = pubkeys.map((pubkey) => { - return events.find((event) => event.pubkey === pubkey); - }).filter((event) => event !== undefined); + events = pubkeys + .map((pubkey) => events.find((event) => event.pubkey === pubkey)) + .filter((event) => !!event); } const accounts = await hydrateEvents({ events, store, signal }).then( (events) => diff --git a/src/controllers/api/search.ts b/src/controllers/api/search.ts index e220413b..01fb6665 100644 --- a/src/controllers/api/search.ts +++ b/src/controllers/api/search.ts @@ -112,9 +112,9 @@ async function searchEvents({ q, type, limit, account_id }: SearchQuery, signal: if (type !== 'accounts') return events; - events = pubkeys.map((pubkey) => { - return events.find((event) => event.pubkey === pubkey); - }).filter((event) => event !== undefined); + events = pubkeys + .map((pubkey) => events.find((event) => event.pubkey === pubkey)) + .filter((event) => !!event); return events; }