# How SearchOrchestrator Coordinates Multi-Strategy Search in Claude-Mem

> Discover how SearchOrchestrator coordinates multi-strategy search in Claude-Mem, intelligently routing queries to SQLite Chroma or Hybrid search for optimal performance.

- Repository: [Alex Newman/claude-mem](https://github.com/thedotmack/claude-mem)
- Tags: deep-dive
- Published: 2026-02-16

---

**SearchOrchestrator acts as a decision-tree router that normalizes input parameters and automatically selects between SQLite, Chroma, or Hybrid search strategies based on query presence and vector store availability.**

Claude-Mem is an open-source memory system that enables persistent context across sessions. At the heart of its retrieval pipeline sits the `SearchOrchestrator`, which intelligently coordinates three distinct search strategies to handle everything from exact filter queries to semantic vector searches.

## Search Strategy Architecture

The system implements three independent strategies located in `src/services/worker/search/strategies/`:

- **`SQLiteSearchStrategy`** ([`SQLiteSearchStrategy.ts`](https://github.com/thedotmack/claude-mem/blob/main/SQLiteSearchStrategy.ts)): Executes filter-only queries directly against the SQLite database. Serves as the universal fallback when vector search is unavailable or fails.
- **`ChromaSearchStrategy`** ([`ChromaSearchStrategy.ts`](https://github.com/thedotmack/claude-mem/blob/main/ChromaSearchStrategy.ts)): Performs semantic vector search via ChromaDB, then hydrates result IDs from SQLite.
- **`HybridSearchStrategy`** ([`HybridSearchStrategy.ts`](https://github.com/thedotmack/claude-mem/blob/main/HybridSearchStrategy.ts)): Combines semantic relevance from Chroma with SQLite filters for concept-based retrieval.

The `SearchOrchestrator` ([`src/services/worker/search/SearchOrchestrator.ts`](https://github.com/thedotmack/claude-mem/blob/main/src/services/worker/search/SearchOrchestrator.ts)) exposes a unified interface while internally routing requests to the appropriate implementation.

## How SearchOrchestrator Routes Queries

The orchestrator implements a decision-tree routing logic that evaluates request parameters and system state to select the optimal strategy.

### Parameter Normalization

Before routing, the `normalizeParams` method (lines 38-82 in [`SearchOrchestrator.ts`](https://github.com/thedotmack/claude-mem/blob/main/SearchOrchestrator.ts)) converts URL-friendly inputs into a uniform `NormalizedParams` object. This handles comma-separated lists, date ranges (`dateStart`/`dateEnd`), and type conversions, ensuring downstream strategies receive consistent inputs regardless of the API surface.

### Strategy Selection Logic

The `search` method implements the following routing tree:

1. **No query string provided** → Route to `SQLiteSearchStrategy` for filter-only retrieval (lines 84-88).
2. **Query string present and Chroma available** → Route to `ChromaSearchStrategy` for semantic search (lines 90-101).
3. **Chroma fails or returns empty** → Fallback to `SQLiteSearchStrategy` (lines 100-107).
4. **No Chroma instance** (e.g., fresh install) → Return empty result with `usedChroma: false` (lines 113-120).

This architecture ensures graceful degradation: semantic search enhances results when available, but the system remains functional with SQLite alone.

## Higher-Level Search Helpers

Beyond the generic `search` method, the orchestrator provides specialized helpers that leverage `HybridSearchStrategy` when available:

- **`findByConcept`** (lines 124-137): Searches for observations linked to specific concepts, preferring hybrid semantic+filter approach.
- **`findByType`** (lines 139-152): Retrieves items by type classification with optional hybrid ranking.
- **`findByFile`** (lines 154-165): Locates observations associated with specific source files.

Each helper first checks for `this.hybridStrategy` existence, falling back to `SQLiteSearchStrategy` if the hybrid implementation is not instantiated.

## Result Processing and Formatting

The orchestrator owns post-processing utilities that transform raw database rows into user-facing output:

- **`ResultFormatter`** ([`src/services/worker/search/ResultFormatter.ts`](https://github.com/thedotmack/claude-mem/blob/main/src/services/worker/search/ResultFormatter.ts)): Converts `SearchResults` into readable strings via `formatSearchResults`.
- **`TimelineBuilder`** ([`src/services/worker/search/TimelineBuilder.ts`](https://github.com/thedotmack/claude-mem/blob/main/src/services/worker/search/TimelineBuilder.ts)): Constructs chronological views around anchor items using `getTimeline` and `formatTimeline`.

These components ensure consistent output formatting regardless of which search strategy generated the underlying data.

## Code Examples

### Basic Semantic Search with Automatic Fallback

```typescript
import { SearchOrchestrator } from './src/services/worker/search/SearchOrchestrator.js';
import { SessionSearch } from './src/sqlite/SessionSearch.js';
import { SessionStore } from './src/sqlite/SessionStore.js';
import { ChromaSync } from './src/sync/ChromaSync.js';

const sessionSearch = new SessionSearch(/* … */);
const sessionStore = new SessionStore(/* … */);
const chromaSync = new ChromaSync(/* … */); // null if Chroma unavailable

const orchestrator = new SearchOrchestrator(sessionSearch, sessionStore, chromaSync);

const result = await orchestrator.search({
  query: 'how did I debug the login flow',
  searchType: 'all',
  limit: 20,
});

console.log(orchestrator.formatSearchResults(result.results, 'how did I debug the login flow'));

```

If `chromaSync` is `null`, the orchestrator automatically routes to `SQLiteSearchStrategy`.

### Filter-Only Search (SQLite Only)

```typescript
const filterResult = await orchestrator.search({
  dateStart: '2024-01-01',
  dateEnd: '2024-01-31',
  type: 'observations',
  concepts: 'authentication',
  limit: 50,
});

console.log(orchestrator.formatSearchResults(filterResult.results, ''));

```

### Concept-Based Search Using Hybrid Strategy

```typescript
const conceptResult = await orchestrator.findByConcept('react-hooks', {
  limit: 10,
  project: 'frontend-app',
});

console.log(orchestrator.formatSearchResults(conceptResult.results, 'react hooks'));

```

### Building a Timeline Around Results

```typescript
const timeline = orchestrator.getTimeline(timelineData, anchorId, anchorEpoch, 5, 5);
console.log(orchestrator.formatTimeline(timeline, anchorId, { query: 'error handling' }));

```

## Summary

- **SearchOrchestrator** serves as the central router in [`src/services/worker/search/SearchOrchestrator.ts`](https://github.com/thedotmack/claude-mem/blob/main/src/services/worker/search/SearchOrchestrator.ts), coordinating three distinct search implementations.
- The **normalizeParams** method standardizes inputs before routing, ensuring consistency across strategies.
- **Strategy selection** follows a decision tree: no query → SQLite; query + Chroma → Chroma; Chroma failure → SQLite fallback.
- **Higher-level helpers** (`findByConcept`, `findByType`, `findByFile`) prefer Hybrid search when available, falling back to SQLite.
- **Post-processing** via `ResultFormatter` and `TimelineBuilder` provides consistent output formatting regardless of the underlying strategy.

## Frequently Asked Questions

### What happens if ChromaDB is not installed or unavailable?

If the `ChromaSync` instance passed to the orchestrator is `null` or undefined, the `search` method detects this condition (lines 113-120) and returns an empty result object with `usedChroma: false`. For queries without a free-text `query` parameter, the system continues to function normally using `SQLiteSearchStrategy` for filter-based retrieval.

### How does SearchOrchestrator handle date range filtering?

The orchestrator's `normalizeParams` method (lines 38-82) processes `dateStart` and `dateEnd` parameters, converting them from URL-friendly string formats into standardized Date objects or timestamps. These normalized parameters are then passed to the selected search strategy, which applies them as SQL `WHERE` clauses in `SQLiteSearchStrategy` or as metadata filters in `ChromaSearchStrategy`.

### Can I use SearchOrchestrator without the HybridSearchStrategy?

Yes. The `HybridSearchStrategy` is optional. The orchestrator checks for its existence in the higher-level helper methods (`findByConcept`, `findByType`, `findByFile`). If `this.hybridStrategy` is undefined, these methods automatically fall back to `SQLiteSearchStrategy`. The core `search` method functions entirely with just `SQLiteSearchStrategy` and optionally `ChromaSearchStrategy`.

### What is the difference between the search method and findByConcept?

The `search` method is the low-level entry point that routes requests to `SQLiteSearchStrategy` or `ChromaSearchStrategy` based on the presence of a `query` parameter. The `findByConcept` method is a higher-level helper specifically optimized for concept-based retrieval. It attempts to use `HybridSearchStrategy` (which combines semantic search with concept filtering) first, falling back to `SQLiteSearchStrategy` if hybrid search is unavailable.