Source Code Reading: SearchService — Unified Abstraction for 6 Search Modes
DefaultSearchService is the full-text search service in Data Layer, built on Quarkus 3.x, leveraging Doris OLAP's full-text indexing and Redis ZSET structures. It supports 6 search modes (BESTMATCH/EXACT/PREFIX/FUZZY/WILDCARD/REGEX), generates parameterized SQL via SearchQueryBuilder, performs parallel multi-type search with result merging. Core features include: parallel multi-type search (thread pool + CompletableFuture), result highlighting (HighlightProcessor), three Facet aggregation types (Terms/Range/DateRange), Redis-driven recent and popular search tracking, sparse-result fuzzy spell suggestions, and saved search CRUD. This article analyzes the seven-step search flow, thread pool design for parallel search, Redis ZSET recent/popular implementation, three Facet bucketing computations, and automatic spell suggestion fallback strategy.
Source Code Reading: SearchService — Unified Abstraction for 6 Search Modes
“Series: S9 Source Code Reading · Article 8 | Level: Advanced | Reading Time: 25 min
#TL;DR
DefaultSearchService is the full-text search service in Data Layer, built on Quarkus 3.x, leveraging Doris OLAP's full-text indexing and Redis ZSET structures. It supports 6 search modes (BEST_MATCH/EXACT/PREFIX/FUZZY/WILDCARD/REGEX), generates parameterized SQL via SearchQueryBuilder, performs parallel multi-type search with result merging. Core features include: parallel multi-type search (thread pool + CompletableFuture), result highlighting (HighlightProcessor), three Facet aggregation types (Terms/Range/DateRange), Redis-driven recent and popular search tracking, sparse-result fuzzy spell suggestions, and saved search CRUD. This article analyzes the seven-step search flow, thread pool design for parallel search, Redis ZSET recent/popular implementation, three Facet bucketing computations, and automatic spell suggestion fallback strategy.
#Table of Contents
- Architecture and Five Collaborators
- Main Search Flow: Seven Steps
- Parallel Multi-Type Search
- 6 Search Modes
- Result Highlighting
- Facet Aggregation: Terms / Range / DateRange
- Redis Recent and Popular Search
- Autocomplete Suggestions
- Spell Suggestion Fuzzy Fallback
- Saved Search CRUD
- Key Takeaways
#1. Architecture and Five Collaborators
@ApplicationScoped
public class DefaultSearchService implements SearchService {
private final DorisClient dorisClient;
private final RedisDataSource redisDataSource;
private final SavedSearchRepository savedSearchRepository;
private final HighlightProcessor highlightProcessor;
private final SearchQueryBuilder queryBuilder;
private final ExecutorService searchExecutor =
Executors.newFixedThreadPool(Math.min(10, Runtime.getRuntime().availableProcessors() * 2));
}
| Collaborator | Responsibility |
|---|---|
DorisClient | Execute parameterized search SQL |
RedisDataSource | Recent/popular search ZSET storage |
SavedSearchRepository | Saved search persistence |
HighlightProcessor | Search result name field highlighting |
SearchQueryBuilder | Mode-specific parameterized SQL generation |
#2. Main Search Flow: Seven Steps
- Resolve entity types to search (from filter or all distinct types)
- Parallel per-type search via CompletableFuture + thread pool
- Sort by score DESC, truncate to page size
- Highlight name fields if requested
- Compute total hits and next page token
- Spell suggestions when results < 5 (fuzzy fallback)
- Facet aggregation if facet requests present
#3. Parallel Multi-Type Search
private final ExecutorService searchExecutor =
Executors.newFixedThreadPool(Math.min(10, Runtime.getRuntime().availableProcessors() * 2));
Thread pool sizing: min(10, CPU * 2) caps max parallelism at 10 on high-core machines, preventing excessive Doris connection pressure. @PreDestroy ensures graceful shutdown with 5-second timeout.
#4. 6 Search Modes
| Mode | SQL Strategy | Use Case |
|---|---|---|
| BEST_MATCH | Full-text index + relevance scoring | Default search |
| EXACT | = ? exact match | Precise lookup |
| PREFIX | LIKE 'query%' | Prefix search |
| FUZZY | Edit distance fuzzy matching | Typo-tolerant search |
| WILDCARD | LIKE '%query%' | Wildcard search |
| REGEX | Regular expression matching | Advanced search |
Default is BEST_MATCH when SearchMode is unspecified.
#5. Result Highlighting
if (request.getIncludeHighlights() && query != null && !query.isBlank()) {
allHits = applyHighlights(allHits, query, preTag, postTag);
}
HighlightProcessor marks matching query terms in the name field using customizable pre/post tags (e.g., <em> / </em>).
#6. Facet Aggregation: Terms / Range / DateRange
| Type | Description | Use Case |
|---|---|---|
| FACET_TERMS | Group by field value counts | Filter by type, status |
| FACET_RANGE | Bucket by numeric ranges | Price ranges |
| FACET_DATE_RANGE | Bucket by date granularity | By year/month/week |
#7. Redis Recent and Popular Search
private static final String REDIS_RECENT_PREFIX = "search:recent:"; // per-user ZSET
private static final String REDIS_POPULAR_PREFIX = "search:popular:"; // per-world ZSET
private static final int MAX_RECENT_SIZE = 100;
- Recent:
search:recent:{userId}ZSET, score = timestamp, keeps latest 100 entries - Popular:
search:popular:{worldId}ZSET, score = search count
Double-checked locking pattern for lazy Redis SortedSet command handle initialization.
#8. Autocomplete Suggestions
Three suggestion types ranked by score: Instance suggestions (1.0) > Recent keywords (0.9) > Popular keywords (0.7). Instance suggestions from Doris prefix search, keyword suggestions from Redis ZSETs.
#9. Spell Suggestion Fuzzy Fallback
if (allHits.size() < 5 && mode != SearchMode.FUZZY) {
spellSuggestions = collectSpellSuggestions(worldId, entityTypes, query, filter);
}
When search results are fewer than 5 and current mode is not FUZZY, automatically performs a FUZZY search fallback, extracting top 3 names as spell suggestions -- similar to Google's "Did you mean..." feature.
#10. Saved Search CRUD
Saved search functionality allows users to name and save search queries for later reuse, persisted via SavedSearchRepository. Supports list (with pagination), delete, and get-recent operations.
#11. Key Takeaways
- Parallel multi-type search: Thread pool + CompletableFuture queries multiple entity types in parallel, merged and sorted by score
- 6 search modes: From exact match to regex, covering all search scenarios
- Redis ZSET tracking: Recent (per-user) and popular (per-world) searches via efficient ZSET operations
- Three Facet types: Terms/Range/DateRange cover categorical, numeric, and temporal aggregation
- Automatic spell suggestions: Sparse results trigger FUZZY fallback for "Did you mean..." experience
- Result highlighting: Customizable pre/post tag keyword highlighting
#Next Article
S9-09: MetricRegistryService -- Metric Computation Strategy Routing. We will dive into the metric registry service, examining metric registration, validation, strategy recommendation, and automatic materialized view integration.
Tags: #coomia-dip #source-code-reading #data-Layer #search #full-text #doris #redis #facets #autocomplete #spell-suggest