Heratio Help Center article. Category: Technical / AI & Automation.
Fuzzy Search - Technical Documentation
Plugin: ahgDisplayPlugin Version: 3.2.23+ Last Updated: February 2026
1. Architecture Overview
2. Files
New Files
| File | Purpose |
|---|---|
ahgDisplayPlugin/lib/Services/FuzzySearchService.php |
Core spell-correction service |
ahgDisplayPlugin/database/fulltext_indexes.sql |
FULLTEXT index definitions |
ahgDisplayPlugin/lib/task/ahgAddFulltextIndexesTask.class.php |
CLI task to create indexes |
Modified Files
| File | Changes |
|---|---|
ahgDisplayPlugin/modules/display/actions/actions.class.php |
Fuzzy correction in executeBrowse(), FULLTEXT in applyTextSearchFilter(), ES fallback, new helper methods |
ahgDisplayPlugin/modules/display/templates/browseSuccess.php |
"Did you mean?" and auto-correct alert banners |
3. FuzzySearchService
Class Structure
namespace AhgDisplay\Services;
class FuzzySearchService
{
private array $vocabulary = []; // normalized => original
private array $soundexIndex = []; // soundex_code => [terms]
private array $metaphoneIndex = []; // metaphone_code => [terms]
public function loadVocabulary(): void;
public function correctQuery(string $query): array;
private function findLevenshteinMatch(string $word): ?array;
private function findPhoneticMatch(string $word): ?array;
private function buildPhoneticIndexes(): void;
}
correctQuery() Return Value
[
'original' => string, // Original query
'corrected' => string|null, // Corrected query (null if no correction)
'suggestion' => string|null, // Same as corrected
'confidence' => float, // 0.0 - 1.0
'corrections' => array, // Per-word correction details
'method' => string|null // 'levenshtein', 'soundex', 'metaphone', or null
]
Vocabulary Sources
Levenshtein Thresholds
| Word Length | Max Edit Distance | Example |
|---|---|---|
| 1-5 chars | 2 | "musem" → "museum" (distance 1) |
| 6+ chars | 3 | "archieves" → "archives" (distance 2) |
Confidence Thresholds
| Confidence | Action |
|---|---|
| >= 0.9 | Auto-correct (replace query, show "Showing results for X") |
| 0.5 - 0.89 | Suggest ("Did you mean: X?") |
| < 0.5 | No suggestion shown |
4. FULLTEXT Indexes
Index Definitions
CREATE FULLTEXT INDEX ft_ioi_title ON information_object_i18n(title);
CREATE FULLTEXT INDEX ft_ioi_scope ON information_object_i18n(scope_and_content);
CREATE FULLTEXT INDEX ft_ai_name ON actor_i18n(authorized_form_of_name);
CREATE FULLTEXT INDEX ft_ti_name ON term_i18n(name);
Installation
These four FULLTEXT indexes ship as part of the Heratio core schema
(database/core/00_core_schema.sql) and are created when the database is
installed, so no separate command is required. To add them to an existing
database, run the CREATE FULLTEXT INDEX statements above - MySQL skips any
index that already exists.
Behavior
- With indexes: Uses
MATCH(column) AGAINST(? IN NATURAL LANGUAGE MODE)for relevance-ranked results - Without indexes: Falls back to
LIKE %term%(exact substring matching) - Detection is cached per request via
isFulltextAvailable()static property
5. Elasticsearch Fuzzy Fallback
Trigger Condition
ES fuzzy is called only when:
- The primary SQL query returns 0 results
- A search query exists
SearchEngineFactoryclass is available
Query Structure
{
"query": {
"multi_match": {
"query": "archieves",
"fields": [
"i18n.en.title^3",
"i18n.en.scopeAndContent",
"display.creator^2",
"autocomplete^2"
],
"fuzziness": "AUTO",
"type": "best_fields"
}
},
"size": 200,
"_source": false
}
Fuzziness AUTO Behavior
| Term Length | Max Edits Allowed |
|---|---|
| 1-2 chars | Exact match only |
| 3-5 chars | 1 edit |
| 6+ chars | 2 edits |
6. Request Flow
7. Template Alerts
Three alert types in browseSuccess.php (no <script> or <style> tags, no CSP nonce needed):
| Variable | Alert Type | Content |
|---|---|---|
$didYouMean |
alert-info |
"Did you mean: [link]?" |
$correctedQuery |
alert-success |
"Showing results for X. Search instead for: [link]" |
$esAssistedSearch |
alert-warning |
"No exact matches. Showing fuzzy matches from search index." |
The "Search instead for" link appends &noCorrect=1 to bypass correction.
8. Graceful Degradation
Every layer is wrapped in try/catch. A failure in any layer never breaks the browse page.
9. FULLTEXT Index Setup
Create FULLTEXT Indexes
The four FULLTEXT indexes ship with the Heratio core schema
(database/core/00_core_schema.sql) and are created at database install time.
To (re)create them on an existing database, run the CREATE FULLTEXT INDEX
statements from section 4. MySQL skips any index that already exists, and index
creation is non-blocking on MySQL 8.0.12+.
10. Configuration
No configuration is needed. Fuzzy search activates automatically on the GLAM Browse page. The noCorrect URL parameter is the only user-facing control.
11. Dependencies
| Dependency | Required | Purpose |
|---|---|---|
| MySQL 8 InnoDB | Yes | FULLTEXT indexes, Levenshtein vocabulary queries |
| Elasticsearch/OpenSearch | No | Fuzzy fallback (graceful degradation) |
| ahgSemanticSearchPlugin | No | Adds thesaurus vocabulary (graceful degradation) |
PHP levenshtein() |
Yes | Built-in PHP function (C implementation) |
PHP soundex() |
Yes | Built-in PHP function |
PHP metaphone() |
Yes | Built-in PHP function |
Part of the Heratio AHG Framework - ahgDisplayPlugin