SearchEngine¶
SearchEngine — это синглтон, который владеет реестром provider'ов, кэшем session'ов и логикой диспетчеризации запрос → session. В пределах одной app session существует ровно один экземпляр — он создаётся в App.vue и передаётся дочерним компонентам через provide/inject.
Источник: src/search/engine.ts
Создание¶
export function createSearchEngine(opts: SearchEngineOptions = {}): SearchEngine {
const cache = new SessionCache<SearchSession>(opts.cacheMax ?? 30);
const registry: Record<string, SearchProvider> = {
rutracker: rutrackerProvider,
soulseek: soulseekProvider,
};
// ...
}
SessionCache — LRU-кэш с ключом "${normalizedQuery}|${enabledProviders}". Это означает, что повторный поиск по тому же запросу с тем же набором включённых provider'ов вернёт существующую живую session, а не запустит новую — полезно при навигации назад.
query()¶
async function query(
rawQuery: string,
enabled: Record<string, boolean>, // { rutracker: true, soulseek: true }
queryOpts: { skipResolver?: boolean } = {},
): Promise<SearchSession>
Полный поток выполнения:
rawQuery (например, "нон стоп молли")
↓
normalizeQuery → lowercase trim → cacheKey
↓ промах кэша
resolveQuery(rawQuery) [Tauri IPC → Rust resolver]
↓ ResolveResult | null
формирование providerQ:
canonical == null → providerQ = rawQuery
canonical.title == "" → providerQ = stripBrackets(artist)
иначе → providerQ = "Artist Title" (скобки удалены)
↓
new SearchSession(providerQ, { providers, pipeline, resolved, rawQuery })
↓
cache.set(cacheKey, session)
↓
return session
Удаление скобок¶
Перед формированием providerQ из канонического результата удаляются скобки:
const stripBrackets = (s: unknown): string =>
String(s ?? "")
.replace(/\([^()]*\)/g, "")
.replace(/\[[^\]]*\]/g, "")
.replace(/\s{2,}/g, " ")
.trim();
Это предотвращает отправку запроса "1.Kla$ (Первый класс)" provider'ам — вставка в скобках является аннотацией Genius, а не частью реальной строки поиска.
skipResolver¶
При skipResolver: true поиск в кэше также пропускается, а необработанный запрос используется напрямую как providerQ. Используется для ручного режима «буквального поиска» в интерфейсе.
Реестр provider'ов¶
При запуске регистрируются два provider'а:
const registry: Record<string, SearchProvider> = {
rutracker: rutrackerProvider,
soulseek: soulseekProvider,
};
Параметр enabled (из search store, управляемого переключателями в интерфейсе) определяет, какие provider'ы реально выполняются для данного запроса:
Сортировка обеспечивает детерминированность ключей кэша независимо от порядка свойств объекта.
Cache¶
Session'ы кэшируются по ключу "${normalizedQuery}|${sortedEnabledProviders}". Попадание в кэш возвращает существующую session — её генераторы могут ещё выполняться, а вызывающий код получает живое реактивное представление.
Размер кэша по умолчанию — 30. LRU-вытеснение удаляет наименее недавно используемую session при заполнении кэша.
clearCache() вызывается, когда пользователь явно очищает результаты поиска или когда изменяются настройки.