Back to Blog

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.

CoomiaPublished on December 8, 20255 min read
Share this articleTwitter / X

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

  1. Architecture and Five Collaborators
  2. Main Search Flow: Seven Steps
  3. Parallel Multi-Type Search
  4. 6 Search Modes
  5. Result Highlighting
  6. Facet Aggregation: Terms / Range / DateRange
  7. Redis Recent and Popular Search
  8. Autocomplete Suggestions
  9. Spell Suggestion Fuzzy Fallback
  10. Saved Search CRUD
  11. Key Takeaways

#1. Architecture and Five Collaborators

Java
@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));
}
CollaboratorResponsibility
DorisClientExecute parameterized search SQL
RedisDataSourceRecent/popular search ZSET storage
SavedSearchRepositorySaved search persistence
HighlightProcessorSearch result name field highlighting
SearchQueryBuilderMode-specific parameterized SQL generation

#2. Main Search Flow: Seven Steps

  1. Resolve entity types to search (from filter or all distinct types)
  2. Parallel per-type search via CompletableFuture + thread pool
  3. Sort by score DESC, truncate to page size
  4. Highlight name fields if requested
  5. Compute total hits and next page token
  6. Spell suggestions when results < 5 (fuzzy fallback)
  7. Facet aggregation if facet requests present
Java
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

ModeSQL StrategyUse Case
BEST_MATCHFull-text index + relevance scoringDefault search
EXACT= ? exact matchPrecise lookup
PREFIXLIKE 'query%'Prefix search
FUZZYEdit distance fuzzy matchingTypo-tolerant search
WILDCARDLIKE '%query%'Wildcard search
REGEXRegular expression matchingAdvanced search

Default is BEST_MATCH when SearchMode is unspecified.

#5. Result Highlighting

Java
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

TypeDescriptionUse Case
FACET_TERMSGroup by field value countsFilter by type, status
FACET_RANGEBucket by numeric rangesPrice ranges
FACET_DATE_RANGEBucket by date granularityBy year/month/week
Java
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

Java
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

  1. Parallel multi-type search: Thread pool + CompletableFuture queries multiple entity types in parallel, merged and sorted by score
  2. 6 search modes: From exact match to regex, covering all search scenarios
  3. Redis ZSET tracking: Recent (per-user) and popular (per-world) searches via efficient ZSET operations
  4. Three Facet types: Terms/Range/DateRange cover categorical, numeric, and temporal aggregation
  5. Automatic spell suggestions: Sparse results trigger FUZZY fallback for "Did you mean..." experience
  6. 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