# Search Migration Guide ## Overview This guide helps you migrate from the old `SearchEvaluatorService` to the new `SearchOrchestratorService`. ## Why Migrate? The new orchestrator provides: - ✅ **Correct filtering**: All field operators actually work - ✅ **Better highlighting**: Precise ranges instead of text matching - ✅ **More features**: Context lines, max results, match ranges - ✅ **Better performance**: Pre-calculated ranges, no rescanning ## Quick Migration ### Before (Old) ```typescript import { SearchEvaluatorService } from './core/search/search-evaluator.service'; constructor(private evaluator: SearchEvaluatorService) {} search(query: string) { const results = this.evaluator.search(query, { caseSensitive: false }); // Results don't include ranges results.forEach(result => { result.matches.forEach(match => { // match.startOffset and match.endOffset are basic }); }); } ``` ### After (New) ```typescript import { SearchOrchestratorService } from './core/search/search-orchestrator.service'; constructor(private orchestrator: SearchOrchestratorService) {} search(query: string) { const results = this.orchestrator.execute(query, { caseSensitive: false, contextLines: 5, // NEW: Adjustable context maxResults: 100 // NEW: Limit results }); // Results include precise ranges results.forEach(result => { result.matches.forEach(match => { // match.ranges: MatchRange[] with start/end/line/context }); }); } ``` ## Step-by-Step Migration ### 1. Update Imports ```typescript // OLD import { SearchEvaluatorService, SearchResult } from './core/search/search-evaluator.service'; // NEW import { SearchOrchestratorService, SearchResult } from './core/search/search-orchestrator.service'; ``` ### 2. Update Injection ```typescript // OLD constructor(private evaluator: SearchEvaluatorService) {} // NEW constructor(private orchestrator: SearchOrchestratorService) {} ``` ### 3. Update Method Calls ```typescript // OLD const results = this.evaluator.search(query, options); // NEW const results = this.orchestrator.execute(query, options); ``` ### 4. Update Result Handling ```typescript // OLD results.forEach(result => { const { noteId, matches, score } = result; // matches[].startOffset, matches[].endOffset }); // NEW results.forEach(result => { const { noteId, matches, score, allRanges } = result; // matches[].ranges: MatchRange[] // allRanges: MatchRange[] (all ranges in note) }); ``` ## Highlighting Migration ### Before (Manual) ```typescript highlightMatch(context: string, matchText: string): string { const regex = new RegExp(`(${matchText})`, 'gi'); return context.replace(regex, '$1'); } ``` ### After (Service) ```typescript import { SearchHighlighterService } from './core/search/search-highlighter.service'; constructor(private highlighter: SearchHighlighterService) {} highlightMatch(match: SearchMatch): string { // Use ranges for precise highlighting if (match.ranges && match.ranges.length > 0) { return this.highlighter.highlightWithRanges(match.context, match.ranges); } // Fallback to text-based return this.highlighter.highlightMatches(match.context, [match.text], false); } ``` ## Preferences Migration ### Before (Manual State) ```typescript export class MyComponent { collapseResults = false; showMoreContext = false; // Manual localStorage ngOnInit() { const saved = localStorage.getItem('my-prefs'); if (saved) { const prefs = JSON.parse(saved); this.collapseResults = prefs.collapse; } } savePrefs() { localStorage.setItem('my-prefs', JSON.stringify({ collapse: this.collapseResults })); } } ``` ### After (Service) ```typescript import { SearchPreferencesService } from './core/search/search-preferences.service'; export class MyComponent { constructor(private preferences: SearchPreferencesService) {} collapseResults = false; showMoreContext = false; ngOnInit() { // Auto-load preferences const prefs = this.preferences.getPreferences('my-context'); this.collapseResults = prefs.collapseResults; this.showMoreContext = prefs.showMoreContext; } onToggleCollapse() { // Auto-save preferences this.preferences.updatePreferences('my-context', { collapseResults: this.collapseResults }); } } ``` ## Component Migration ### SearchResultsComponent #### Before ```typescript ``` #### After ```typescript ``` ### SearchPanelComponent No changes needed! The component now includes toggles automatically. ```typescript ``` ## Common Patterns ### Pattern 1: Search with Context ```typescript // OLD: Fixed context const results = this.evaluator.search(query); // NEW: Adjustable context const results = this.orchestrator.execute(query, { contextLines: this.showMoreContext ? 5 : 2 }); ``` ### Pattern 2: Limit Results ```typescript // OLD: Manual slicing const results = this.evaluator.search(query).slice(0, 100); // NEW: Built-in limit const results = this.orchestrator.execute(query, { maxResults: 100 }); ``` ### Pattern 3: Highlighting ```typescript // OLD: Manual regex const highlighted = text.replace( new RegExp(`(${term})`, 'gi'), '$1' ); // NEW: Service with XSS protection const highlighted = this.highlighter.highlightMatches( text, [term], caseSensitive ); ``` ## Testing Migration ### Before ```typescript it('should search', () => { const results = evaluator.search('test'); expect(results.length).toBeGreaterThan(0); }); ``` ### After ```typescript it('should search with orchestrator', () => { const results = orchestrator.execute('test'); expect(results.length).toBeGreaterThan(0); expect(results[0].allRanges).toBeDefined(); expect(results[0].matches[0].ranges).toBeDefined(); }); ``` ## Backward Compatibility The old `SearchEvaluatorService` still works as a **wrapper**: ```typescript // This still works (delegates to orchestrator) const results = this.evaluator.search(query, options); // But you won't get the new features: // - No contextLines option // - No maxResults option // - No allRanges in results // - matches[].ranges are converted from allRanges[0] ``` ## Breaking Changes **None!** All existing code continues to work. ## Deprecation Timeline - **Now**: `SearchEvaluatorService` marked as `@deprecated` - **v2.0**: `SearchEvaluatorService` will be removed - **Migration window**: ~6 months ## Checklist - [ ] Update imports to `SearchOrchestratorService` - [ ] Update injection in constructors - [ ] Replace `.search()` with `.execute()` - [ ] Add `SearchHighlighterService` for highlighting - [ ] Add `SearchPreferencesService` for preferences - [ ] Update component inputs (collapseAll, showMoreContext, contextLines) - [ ] Update tests to check for ranges - [ ] Remove manual localStorage code - [ ] Test all search scenarios ## Need Help? - **Documentation**: `src/core/search/README.md` - **Examples**: `src/components/search-panel/search-panel.component.ts` - **Tests**: `src/core/search/*.spec.ts` - **Issues**: Create a GitHub issue with `[search]` prefix ## FAQ ### Q: Do I have to migrate immediately? **A:** No, the old service still works. But you won't get the bug fixes and new features. ### Q: Will my existing code break? **A:** No, backward compatibility is maintained. ### Q: What if I only want highlighting? **A:** You can use `SearchHighlighterService` independently: ```typescript import { SearchHighlighterService } from './core/search/search-highlighter.service'; constructor(private highlighter: SearchHighlighterService) {} highlight(text: string, terms: string[]) { return this.highlighter.highlightMatches(text, terms, false); } ``` ### Q: What if I only want preferences? **A:** You can use `SearchPreferencesService` independently: ```typescript import { SearchPreferencesService } from './core/search/search-preferences.service'; constructor(private preferences: SearchPreferencesService) {} loadPrefs() { return this.preferences.getPreferences('my-context'); } ``` ### Q: Can I mix old and new? **A:** Yes, but not recommended. Stick to one approach per component. ## Examples See complete examples in: - `src/components/search-panel/search-panel.component.ts` - `src/components/search-results/search-results.component.ts` - `src/core/search/*.spec.ts`