Appearance
Search
Flow ID: CF-04 | Module(s): search, doofinder | Complexity: High
Business Overview
Product search supports three backends: SQL full-text (default fallback), Apache Solr (v1/v2, primary), and Doofinder (external SaaS). The system routes to the configured backend transparently. Search serves two UIs: live autocomplete dropdown (AJAX, 8 results) and full results page with pagination.
What customers experience:
- Type in search box → live autocomplete with 8 products + vendors + categories
- Submit → full results page with product grid, vendor sidebar, category links
- Results include: products, vendors, categories, product lines, blog articles (v2)
- Autocomplete suggestions and spell-check corrections (Solr v2)
Key business behaviors:
- Three backend options: SQL FULLTEXT, Solr v1/v2, Doofinder (configured via
solrSearch.enabled+solrSearch.version) - Solr field boosts:
vendor_name^10,product_name^8,category^3,description^0.001 - 80% minimum match (
mmparameter) — at least 80% of search terms must match - SOUNDEX phonetic matching for vendor names (SQL backend)
- Search terms optionally tracked in
search_tracktable - Facebook Search event + Matomo
trackSiteSearch()fired - No filtering on search results page (unlike category pages)
API Reference
Legacy Endpoints
| Method | Path | Controller | Description |
|---|---|---|---|
| GET | /search?q={term} | Adv_search.php::searchPage() | Full results page |
| GET | /search?q={term} (XHR) | Adv_search.php::liveSearch() | Autocomplete (AJAX, 8 results) |
Explicit Routes
/search → search/index
/{lang}/search → search/indexCode Flow
Search Dispatch
File: ecommercen/search/controllers/Adv_search.php
Constructor selects model version (v1/v2), optionally loads Solr client. index() routes:
- AJAX →
liveSearch()(8 products max) - Full page →
searchPage()(paginated)
Core: searchInDb() (line 345-433)
- Term preparation: trim, escape
- Search tracking: insert into
search_trackifENABLE_SEARCH_TRACK - Execute search (cached): products + vendors + categories + blog (v2) + lines
- Analytics: Facebook Conversion API (Search event), Matomo
trackSiteSearch()
SQL Backend (Solr disabled)
4 query paths UNIONed: custom (SOUNDEX vendor), codes (SKU/barcode), name (FULLTEXT), content (FULLTEXT description). Scores combined and sorted.
Solr Backend (v2)
eDisMax query with field boosts, phrase slop, 80% minimum match, spellcheck + suggest enabled. Results: extract IDs → fetch products from MySQL. Suggestions: n-gram based autocomplete from text_el_suggest field.
Domain & REST Architecture
No REST API for search — search is legacy HMVC only. Solr v2 schema uses:
- ICU tokenizer with Greek language filters
- N-gram suggestion field (min 3, max 15 grams)
- Copy fields for catch-all search
Indexing: Job AdvSolrIndex runs daily (SY-06), full re-index.
Client Extension Points
| Type | Details |
|---|---|
| Search controller | Override in application/modules/search/controllers/ |
| Search model | Override Adv_search_model*.php for custom query logic |
| Solr schema | Extend Adv_solr_model_v2 for custom fields/analyzers |
| Config | solrSearch.enabled/version/host/port in app.php |
| Boost weights | searchPartsEnabled config per search path |
| Feature flags | ENABLE_SEARCH_TRACK, FACEBOOK_CONVERSION.ENABLED |
Data Model
| Table | Purpose |
|---|---|
shop_product / shop_product_mui | Product data (FULLTEXT indexed on name/description) |
shop_vendor / shop_vendor_mui | Vendor matching |
product_codes / shop_product_barcodes | SKU/barcode matching |
search_track | Search term analytics (term, date, lang) |
Business Rules
- Backend selection: controlled by
solrSearch.enabled(registry key) andsolrSearch.version('v1'or'v2'). Whenenabledis false the SQL fallback is used unconditionally. (ecommercen/search/controllers/Adv_search.php:32-51) - 80% minimum match (Solr): eDisMax queries include
mm=80%, requiring at least 80% of search terms to match. (ecommercen/search/models/Adv_search_model.php:662,ecommercen/search/models/Adv_search_model_v2.php:712) - SOUNDEX vendor matching (SQL path): vendor name queries append a
SOUNDEX()clause so phonetically similar spellings still match. Applied viastringHasSound()check before appending. (ecommercen/search/models/Adv_search_model.php:108,ecommercen/search/models/Adv_search_model_v2.php:116) - Degraded results are still cached: when a Solr query errors or the SQL fallback fails, the empty/degraded result is written into the pscache at the normal search TTL (
cache_l2_search_expires, default 43200 s). This absorbs repeat traffic during an outage instead of each request burning the Solr connect timeout. (ecommercen/search/controllers/Adv_search.php:360-380,application/config/cache.php:8) search_degradedrender variable: the controller setssearch_degradedin the render array for the search page, live search, and vendor live search, allowing themes to show a "search temporarily unavailable" message instead of a misleading "no results". (ecommercen/search/controllers/Adv_search.php:104,161,388,193)
Live Testing Findings
Solr Availability: The health check endpoint /_healthz/ready confirms Solr is unavailable in development environments, returning:
json
{"error": true, "message": "Search unavailable"}SQL Fallback Behavior: With Solr unavailable, the search page falls back to SQL full-text queries. The page returns HTTP 200 but yields "no results" for test queries, indicating the SQL fallback may require FULLTEXT indexes to be present and populated on the development database.
Live Search (Autocomplete): The autocomplete dropdown triggers via AJAX on each keypress in the search input. When Solr is down, live search requests complete (no server errors) but return empty result sets, providing a degraded but non-breaking experience.
Degraded-Mode Observability (#43, fixed): degraded search is no longer silent. Every failure point logs with a greppable Search degraded: prefix (AdvSolrClient::search() additionally logs Solr search request failed: with the transport error), and both search models return degraded => true inside the product-search result array when the Solr query errors or the SQL fallback query fails (e.g. missing FULLTEXT index — distinguished from a query that simply matches zero rows). The controller exposes this as search_degraded in the render array for the search page, live search, and vendor live search, so themes can render a "search temporarily unavailable" state instead of a misleading "no results". The flag travels inside the pscache payload; degraded empty results are still cached for the search TTL (deliberate — the cache absorbs repeat traffic during an outage instead of each request burning the Solr connect timeout). Also fixed: with APP_SOLR_DEBUG enabled and Solr down, the debug blocks fataled (->getBody() on a null response threw Error past a catch (Exception)) — now catch (Throwable).
Known Issues & Security Gaps
- Degraded-search silent empty (RESOLVED): prior to commit
5e87ac7769(#43), a Solr failure or missing FULLTEXT index caused the search page to return "no results" with no log output and no theme-visible signal. Fixed: all failure paths now log aSearch degraded:prefix and exposesearch_degradedto themes. No open issues at the time of writing.
Tests
Unit tests cover the degraded-flag behaviour added in #43 (commit 5e87ac7769):
tests/Legacy/Search/AdvSearchModelTest.php — targets Adv_search_model (v1):
| Test | Path | Assertion |
|---|---|---|
test_search_products_sql_failure_sets_degraded_true | SQL fallback, db->query() returns false | degraded === true, results/numRows empty |
test_search_products_sql_success_sets_degraded_false | SQL fallback, query returns 2 rows | degraded === false, numRows = 2 |
test_search_products_sql_success_returns_result_objects | SQL fallback success | result objects have correct product_id values |
test_solr_search_error_result_sets_degraded_true | Solr path, solr_client->search() returns error => true | degraded === true, results/numRows empty |
test_solr_search_success_with_empty_docs_sets_degraded_false | Solr path, success with 0 docs | degraded === false, numRows = 0 |
tests/Legacy/Search/AdvSearchModelV2Test.php — targets Adv_search_model_v2:
| Test | Path | Assertion |
|---|---|---|
test_search_products_sql_failure_sets_degraded_true_in_products_key | SQL fallback, db->query() returns false | result['products']['degraded'] === true, outer shape intact |
test_search_products_sql_failure_outer_shape_is_intact | SQL fallback failure | top-level keys products, suggestions, autocomplete, debug all present |
test_solr_process_results_error_sets_products_degraded_true | Solr processSearchResults, error => true | $products['degraded'] === true, results/numRows empty |
test_solr_process_results_error_returns_empty_suggestions_and_autocomplete | Solr error path | suggestions and autocomplete both [] |
test_solr_process_results_success_with_empty_docs_sets_degraded_false | Solr success, 0 docs | $products['degraded'] === false |
All tests use newInstanceWithoutConstructor() + direct property injection to bypass the CI3 model constructor. APP_SOLR_DEBUG is forced to false in setUp/tearDown to prevent the debug block from altering return shapes.
Related Flows
- CF-01 Product Browsing — shares product grid component
- CF-02 Product Detail — search result links to PDP
- CF-03 URL Routing — explicit
/searchroute definition - SY-06 Solr Indexing — how products get into Solr
- IN-10 Doofinder — alternative search backend
- IN-12 Analytics — Matomo
trackSiteSearch()integration - AD-45 Search Debug — admin search debugging tools
Wiki Guide: Solr setup and schema configuration — see Solr Guide.