# Semantic Search Semantic search lets users find research by meaning, not just title keywords. It indexes completed research reports and their sources into FAISS vector stores, then offers three search modes -- Hybrid (default), Text-Only, and AI-Only -- with a three-tier ranked merge algorithm for hybrid results. ## Table of Contents - [Indexing Pipeline](#indexing-pipeline) - [Search Pipeline](#search-pipeline) - [Three-Tier Merge Algorithm](#three-tier-merge-algorithm) - [File Structure](#file-structure) - [API Routes](#api-routes) - [Reusing on Other Pages](#reusing-on-other-pages) - [See Also](#see-also) --- ## Indexing Pipeline ```mermaid flowchart LR RH[ResearchHistory
report + sources] --> RHI[ResearchHistoryIndexer
converts to Documents] RHI --> DOC[Document rows
+ DocumentCollection links] DOC --> FACTORY[RAGServiceFactory
resolves settings] FACTORY --> RAG[LibraryRAGService
chunk + embed] RAG --> FAISS[(FAISS Index
on disk)] ``` **Steps:** 1. `ResearchHistoryIndexer` reads completed `ResearchHistory` rows and their `ResearchResource` sources. 2. Each report becomes a `Document` (source\_type `research_report`). Each source with sufficient content becomes a `Document` (source\_type `research_source`). 3. Documents are linked to the History `Collection` via `DocumentCollection`. 4. `RAGServiceFactory` creates a `LibraryRAGService` with the collection's embedding settings. 5. `LibraryRAGService` chunks each document, generates embeddings, and writes the FAISS index to disk. Bulk indexing uses **SSE streaming** -- the `/index` endpoint yields progress events (`start`, `progress`, `complete`, `error`) so the frontend can show a real-time progress bar. --- ## Search Pipeline ```mermaid flowchart LR Q[User query] --> MODE{Search Mode} MODE -->|Text-Only| TF[Title filter
instant, client-side] MODE -->|AI-Only| API MODE -->|Hybrid| TF & API API[POST /library/api/collections/:id/search] --> CSE[CollectionSearchEngine] CSE --> FAISS[(FAISS Index)] FAISS --> ENRICH[Enrich metadata
report/source type] ENRICH --> JSON[JSON response] TF --> RENDER JSON --> MERGE[buildTieredResults
three-tier merge] MERGE --> RENDER[Render cards] ``` **Text filter** runs instantly on the client against cached history items. **Semantic search** is an async `POST` to the collection search endpoint, which delegates to `CollectionSearchEngine` for FAISS similarity search. In **Hybrid mode**, text results render immediately while the semantic call runs in the background; once it resolves, `buildTieredResults` merges both result sets into three tiers and re-renders. --- ## Three-Tier Merge Algorithm Implemented in `semantic_search.js :: buildTieredResults()`. | Tier | Contents | Sort Order | |------|----------|------------| | **Tier 1** | Matched both text filter AND semantic search | Similarity score DESC | | **Tier 2** | Text-only matches (no semantic hit) | Original order (recency) | | **Tier 3** | Semantic-only matches (below a visual divider) | Similarity score DESC | The merge groups semantic results by `research_id` (keeping the best similarity per research), then classifies each text result as Tier 1 or Tier 2 based on whether a corresponding semantic match exists. Remaining semantic-only results become Tier 3. --- ## File Structure ### Backend ``` research_library/ ├── search/ # Semantic search subpackage │ ├── __init__.py # Exports search_bp, ResearchHistoryIndexer │ ├── routes/ │ │ └── search_routes.py # 4 endpoints + _enrich helper │ └── services/ │ └── research_history_indexer.py # Converts ResearchHistory -> Documents -> RAG ├── services/ │ ├── rag_service_factory.py # Creates LibraryRAGService with collection settings │ └── library_rag_service.py # FAISS indexing, chunking, embedding management ``` ### Frontend ``` js/components/ ├── semantic_search.js # Shared module (window.SemanticSearch): │ # renderSnippet, buildTieredResults, │ # createSemanticResultCard, isSafeExternalUrl ├── history.js # Page controller: mode switching, hybrid merge, rendering └── history_search.js # Indexing UI, semantic API calls, collection ID caching js/config/ └── constants.js # LDR_CONSTANTS.SEARCH_MODE: HYBRID | TEXT | SEMANTIC css/components/ └── semantic-search.css # All semantic search styles (badges, cards, dividers) ``` --- ## API Routes All routes require `@login_required`. Blueprint prefix: `/library`. | Method | Path | Purpose | |--------|------|---------| | `GET` | `/api/research-history/collection` | Get collection ID and indexing status (auto-converts unconverted entries) | | `POST` | `/api/research-history/convert-all` | Convert all completed research to Documents | | `POST` | `/api/research//add-to-collection` | Add research to a custom collection | | `POST` | `/api/collections//search` | Semantic search any collection (generic) | The search endpoint is **collection-agnostic** -- it works for any collection type. For `research_history` collections, results are enriched with report/source type metadata. --- ## Reusing on Other Pages The `semantic_search.js` shared module is designed for reuse on library or collection pages. 1. **Load the shared assets** in your template (order matters): ```html ``` 2. **Call the search endpoint** with `POST /library/api/collections//search`: ```javascript const response = await fetch(`/library/api/collections/${collectionId}/search`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ query: 'your search', limit: 10 }), }); const { results } = await response.json(); ``` 3. **Render results** using the shared utilities: ```javascript for (const result of results) { container.appendChild(SemanticSearch.createSemanticResultCard(result)); } // Or for hybrid mode with text + semantic merge: const tiered = SemanticSearch.buildTieredResults(textResults, semanticResults); // tiered.tier1, tiered.tier2, tiered.tier3 ``` No backend changes needed -- the search route and `CollectionSearchEngine` already support any collection type. --- ## See Also - [Architecture Overview](./OVERVIEW.md) -- System architecture - [Database Schema](./DATABASE_SCHEMA.md) -- Document, Collection, and DocumentCollection models - [Library & RAG Guide](../library-and-rag.md) -- User-facing guide to library and search features