mirror of
https://github.com/aljazceru/ditto.git
synced 2026-01-16 03:44:26 +01:00
Merge branch 'main' into zap-notification-streaming
This commit is contained in:
@@ -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,8 +135,17 @@ 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 });
|
||||
|
||||
let events = event ? [event] : await store.query([{ kinds: [0], authors: pubkeys, limit }], {
|
||||
signal,
|
||||
});
|
||||
|
||||
if (!event) {
|
||||
events = pubkeys
|
||||
.map((pubkey) => events.find((event) => event.pubkey === pubkey))
|
||||
.filter((event) => !!event);
|
||||
}
|
||||
const accounts = await hydrateEvents({ events, store, signal }).then(
|
||||
(events) =>
|
||||
Promise.all(
|
||||
|
||||
21
src/controllers/api/search.test.ts
Normal file
21
src/controllers/api/search.test.ts
Normal file
@@ -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',
|
||||
]);
|
||||
});
|
||||
@@ -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';
|
||||
@@ -47,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();
|
||||
|
||||
@@ -89,10 +90,33 @@ 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();
|
||||
|
||||
pubkeys.push(...(await getPubkeysBySearch(kysely, { q, limit })));
|
||||
|
||||
if (!filter?.authors) {
|
||||
filter.authors = pubkeys;
|
||||
} else {
|
||||
filter.authors.push(...pubkeys);
|
||||
}
|
||||
|
||||
filter.search = undefined;
|
||||
}
|
||||
|
||||
const store = await Storages.search();
|
||||
|
||||
return store.query([filter], { signal })
|
||||
let events = await store.query([filter], { signal })
|
||||
.then((events) => hydrateEvents({ events, store, signal }));
|
||||
|
||||
if (type !== 'accounts') return events;
|
||||
|
||||
events = pubkeys
|
||||
.map((pubkey) => events.find((event) => event.pubkey === pubkey))
|
||||
.filter((event) => !!event);
|
||||
|
||||
return events;
|
||||
}
|
||||
|
||||
/** Get event kinds to search from `type` query param. */
|
||||
@@ -170,4 +194,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<DittoTables>, { q, limit }: Pick<SearchQuery, 'q' | 'limit'>) {
|
||||
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(({ pubkey }) => pubkey);
|
||||
|
||||
return pubkeys;
|
||||
}
|
||||
|
||||
export { getPubkeysBySearch, searchController };
|
||||
|
||||
Reference in New Issue
Block a user