You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
85 lines
2.8 KiB
85 lines
2.8 KiB
import { eventMatchesNip50LocalFullTextQuery } from '@/lib/nip50-local-text-match' |
|
import indexedDb from '@/services/indexed-db.service' |
|
import { eventService } from '@/services/client.service' |
|
import type { Event } from 'nostr-tools' |
|
|
|
export type CollectLocalTextSearchParams = { |
|
query: string |
|
/** Kind filter (same semantics as NIP-50 `kinds` on relays). */ |
|
allowedKinds: readonly number[] |
|
/** |
|
* Session LRU scan cap for {@link EventService.getSessionEventsMatchingSearch}. |
|
* Use `0` when the caller already merged the session layer synchronously. |
|
*/ |
|
sessionCap: number |
|
/** `limit` passed to {@link IndexedDbService.getCachedAndArchivedEventsMatchingLocalSearch}. */ |
|
idbMergedLimit: number |
|
archiveScanMaxMs?: number |
|
/** |
|
* When true, also scan non–event-archive stores via {@link IndexedDbService.searchAllCachedEventsFullText} |
|
* (same extra coverage as the mention / citation picker path). |
|
*/ |
|
includeOtherStoresFullText?: boolean |
|
/** Max rows from {@link IndexedDbService.searchAllCachedEventsFullText} when enabled. */ |
|
fullTextStoreHitCap?: number |
|
} |
|
|
|
/** |
|
* Merges local session + publication + event-archive (and optionally other IndexedDB stores) for the same |
|
* text query and kind filter, deduped by id, sorted newest-first. Every row must satisfy |
|
* {@link eventMatchesNip50LocalFullTextQuery} (defense in depth on top of store-specific scans). |
|
*/ |
|
export async function collectLocalEventsForTextSearch( |
|
params: CollectLocalTextSearchParams |
|
): Promise<Event[]> { |
|
const q = params.query.trim() |
|
if (!q) return [] |
|
|
|
const kindsArr = [...params.allowedKinds] |
|
if (kindsArr.length === 0) return [] |
|
|
|
const kindSet = new Set(kindsArr) |
|
const seen = new Set<string>() |
|
const out: Event[] = [] |
|
|
|
const push = (ev: Event) => { |
|
if (!kindSet.has(ev.kind)) return |
|
if (!eventMatchesNip50LocalFullTextQuery(ev, q)) return |
|
if (seen.has(ev.id)) return |
|
seen.add(ev.id) |
|
out.push(ev) |
|
} |
|
|
|
if (params.sessionCap > 0) { |
|
for (const ev of eventService.getSessionEventsMatchingSearch(q, params.sessionCap, kindsArr)) { |
|
push(ev) |
|
} |
|
} |
|
|
|
if (params.includeOtherStoresFullText) { |
|
const cap = params.fullTextStoreHitCap ?? 260 |
|
try { |
|
const hits = await indexedDb.searchAllCachedEventsFullText(q, { limit: cap }) |
|
for (const hit of hits) { |
|
if (hit.value) push(hit.value as Event) |
|
} |
|
} catch { |
|
/* optional cross-store scan */ |
|
} |
|
} |
|
|
|
const idbOpts = |
|
params.archiveScanMaxMs !== undefined ? { archiveScanMaxMs: params.archiveScanMaxMs } : undefined |
|
const fromPubArchive = await indexedDb.getCachedAndArchivedEventsMatchingLocalSearch( |
|
q, |
|
params.idbMergedLimit, |
|
kindsArr, |
|
idbOpts |
|
) |
|
for (const ev of fromPubArchive) { |
|
push(ev) |
|
} |
|
|
|
out.sort((a, b) => b.created_at - a.created_at || b.id.localeCompare(a.id)) |
|
return out |
|
}
|
|
|