Maintainer overview of VIP Search integration, facets middleware, and URL handling.
System pieces
vip-config redirects ──► WordPress /search/{term}
│
ElasticPress_Middleware ──► ep_integrate + aggregations
│
ElasticPress_Facets_API ──► Interactivity contract
│
prc-ep/* blocks ──► ep_filter_* URL + query refresh
Facet request path
Provider model
ElasticPress_Middleware is always registered from includes/class-plugin.php. The context provider and REST settings always use ElasticPress (urlKey = ep_filter_).
ElasticPress_Facets_API normalizes aggregations into the Interactivity API contract used by facet blocks.
Query takeover
ElasticPress integrates every main query flagged isPubListingQuery (publications, taxonomy archives, search) plus a dedicated dataset archive opt-in in prc-datasets. Facetable marking uses ep_is_facetable when needed.
Aggregations include:
- Taxonomy terms (disjunctive for checkbox OR/AND-across)
- Years (
date_terms.year) - Time-since ranges (
past-month,past-6-months,past-12-months,past-2-years)
Registered facet slugs (from plugin README): category, formats, regions-countries, bylines, research-teams, years, time_since.
URL scheme
All surfaces use ep_filter_* query params. Legacy FacetWP underscore params (_categories, _authors, _formats, _years, _time_since, and related) are 301'd in vip-config/server-redirects.php before WordPress boots. _date_range is stripped without remap.
Frontend HTML /search* integrates via facets middleware. Additional hooks cover REST/feed gaps (ep_integrate, 100-character search term cap, feed posts_per_page cap at 20). Admin DataViews list search sets ep_integrate plus the prc_wp_admin_dataview query flag (Search_Query in prc-wp-admin-dataview). Admin_Dataview_Search indexes draft / pending / private / future (not trash) and expands search fields (title, slug, and chart design_slug). Numeric IDs use post__in, not the ES post_id field. See the plugin README search routing table.
After expanding indexable statuses, reindex VIP Search (wp vip-search index) so drafts and other unpublished posts are searchable.
Origin hardening (search / staff)
Deep unique /search/* renders can saturate PHP-FPM when bots invent junk terms. Guards:
| Layer | Rule |
|---|---|
vip-config/redirects.php | Nonsense /search/{term}/ (consecutive ++ / spaces, noise-only, no real word) → 301 / |
ElasticPress_Middleware::MAX_NAVIGABLE_PAGE | Facets-only threshold (100); result pagination still uses MAX_RESULT_WINDOW / per_page |
ElasticPress_Facets_API | Empty facets past page 100 or for nonsense s |
Plugin | Defensive WP short-circuit + 404 if nonsense s reaches WordPress |
MAX_RESULT_WINDOW (10000) remains the Elasticsearch result/pager ceiling. Keep vip-config heuristics in sync with is_nonsense_search_term() in includes/utils.php.
MySQL fallback
When ES fails, visibility SQL still applies; facet filters are not re-applied on MySQL. UI renders disabled facet shells (isDisabled).
Cache
Cache keys hash query + selected facets + a manual invalidation stamp (construct_cache_key). Cache groups derive from the request path with pagination stripped (construct_cache_group).
Caches invalidate on content updates, when the manual invalidation date changes, and when facet configuration changes.
Debug
- PHP: define
PRC_ELASTICPRESS_DEBUGto log provider decisions under[PRC Facets - ElasticPress] - Client:
window.prcFacetsDebug = true
Key files
| Path | Purpose |
|---|---|
includes/class-plugin.php | Search integration, sanitization, facet bootstrap |
includes/class-admin-dataview-search.php | Admin DataViews search fields and unpublished statuses |
includes/providers/ | Middleware + Facets API |
includes/class-rest-api.php | Facet settings REST |
vip-config/server-redirects.php | Pre-WordPress search + legacy facet redirects |
src/ | Facet blocks |