Embeddings — Векторный поиск по схожести¶
Уровень: L2 (details) | ← Назад
Обзор¶
Система векторного поиска позволяет находить похожие вещи и образы за <5ms. ML-модель на бэкенде генерирует embedding-вектор (1024 dim) для каждой вещи. Фронтенд хранит вектора в IndexedDB, вычисляет cosine similarity в Web Worker и отображает результаты в UI.
Pipeline: от ML до UI¶
graph LR
subgraph Backend
ML[ML Service<br/>ResNet50] --> API[Backend API<br/>EmbeddingDto]
end
subgraph "Frontend: Storage"
API --> Sync[Initial Sync<br/>+ Refresh Sync]
Sync --> IDB[(IndexedDB<br/>embeddings table)]
end
subgraph "Frontend: Compute"
IDB --> SvcLoad[similarityService<br/>load candidates]
SvcLoad --> Worker[Web Worker<br/>search.worker.ts]
Worker --> Results[TopK Results<br/>id + score]
end
subgraph "Frontend: UI"
Results --> Hook[useEmbeddingSearch<br/>useSimilarItems/Outfits]
Hook --> Rail[SimilarItemsRail]
Hook --> Grid[SimilarOutfitsGrid]
end

Sequence Diagram: Поиск похожих вещей¶
sequenceDiagram
participant Page as WardrobeItemPage
participant Hook as useSimilarItems
participant Svc as similarityService
participant Repo as embeddingRepository
participant IDB as IndexedDB
participant W as Web Worker
Page->>Hook: useSimilarItems(itemId, 8)
Hook->>Svc: findSimilarItems(itemId, 8)
Svc->>Repo: get('item', itemId)
Repo->>IDB: embeddings.get(['item', id])
IDB-->>Repo: LocalEmbedding (query vector)
Repo-->>Svc: query vector (Float32Array)
alt Cache stale (first search or after invalidation)
Svc->>Repo: getAllByType('item')
Repo->>IDB: embeddings.where('entity_type').equals('item')
IDB-->>Repo: All item embeddings
Repo-->>Svc: candidates[]
Svc->>W: postMessage({type: 'SEARCH', query, candidates, topK: 8})
else Cache fresh
Svc->>W: postMessage({type: 'SEARCH', query, candidates: [], topK: 8})
Note over W: Uses cached candidates
end
W->>W: cosineSimilarity for each candidate
W->>W: sort by score, take topK
W-->>Svc: {results: [{id, score}...], timeMs: 3.2}
Svc-->>Hook: results
Hook-->>Page: {results, isLoading: false}
Page->>Page: useLocalItemsByIds(results.map(r => r.id))
Page->>Page: <SimilarItemsRail items={...} />

Архитектура: 6 слоёв¶
| Слой | Модуль | Ответственность |
|---|---|---|
| 1. Storage | embeddingRepository |
CRUD эмбеддингов в IndexedDB |
| 2. Math | vectorMath |
cosine similarity, mean vector, topK |
| 3. Worker | search.worker.ts |
Вычисления в фоновом потоке |
| 4. Service | similarityService |
Оркестрация: кеш, worker lifecycle, timeout |
| 5. Hooks | useEmbeddingSearch |
React-обёртки: state, loading, error |
| 6. UI | SimilarItemsRail, SimilarOutfitsGrid |
Визуализация результатов |
Outfit Embeddings (производные)¶
Образы не имеют собственных ML-эмбеддингов. Вместо этого:
Вычисляется на фронтенде после sync:
1. outfitEmbeddingService.computeAndStore(outfitId) — для одного образа
2. outfitEmbeddingService.recomputeAll() — для всех образов (после initial sync)
Производительность¶
| Операция | Время | Где выполняется |
|---|---|---|
| Load query embedding | <1ms | Main thread (IndexedDB get) |
| Load all candidates | ~5ms | Main thread (IndexedDB toArray) |
| Cosine similarity × N | <3ms | Web Worker (не блокирует UI) |
| Total search | <5ms | — |
| Worker timeout | 5000ms | Fallback при проблемах |
Cache Invalidation¶
similarityService.invalidateCache('item') — после sync/update/delete
similarityService.invalidateCache('outfit') — после outfit changes
При инвалидации следующий поиск перезагрузит все кандидаты из IndexedDB и отправит их в Worker.
Навигация¶
- → Математика — cosine similarity, mean vector, topK
- → Web Worker — search.worker.ts, протокол сообщений
- → Хранение — embeddingRepository + outfitEmbeddingService
- → UI компоненты — SimilarItemsRail, SimilarOutfitsGrid, hooks
- ← Назад к обзору