Faceted search that adapts to
every filter.
Keep filters useful after every click. One facet query can preserve alternatives, expose impossible choices, and control exactly which filters each facet inherits.
{
"facet_filter_mode": "max",
"exclude_filters": ["color"],
"zeroes": true
}
Filter products without dead ends
Use the catalog like a shopper. Switch modes to see how Manticore changes facet behavior behind the same familiar interface.
Each example applies the stated filters and settings to the catalog. Watch the product count and facet choices update below.
Selected is active · Available can be added · Muted is impossible under the current filters.
Live catalog
Products
No products match every filter
Max mode keeps useful alternatives visible so you can recover without resetting the whole search.
One request → products + 4 facets
Every change sends one bounded request to Manticore Search. It returns the product result and native Brand, Category, Color, and Size buckets—no client-side count building.
Facet logic, directly in the frontend.
Use Manticore’s official JavaScript client to send one JSON request and render the buckets it returns. These are annotated production patterns: they show the essential Manticore calls, while endpoint policy, credentials, and filter-building remain specific to your application. No custom application backend is required to translate facets.
- One requestproducts and aggregations arrive together
- Native statesrender
selected,available, andunavailable - Read-only accessgive browser code only the table and permissions it needs
For a public site, expose a read-only HTTP endpoint over HTTPS. This demo uses a small same-origin gateway for that boundary; it does not contain application-side facet logic.
import Manticoresearch from 'manticoresearch';
const client = new Manticoresearch.ApiClient(
'https://search.example.com'
);
client.authentications.basicAuth.username = 'catalog_readonly';
client.authentications.basicAuth.password = 'public-read-only-password';
const search = new Manticoresearch.SearchApi(client);
Replace the example endpoint and read-only credentials with your own deployment values. The live demo source uses location.origin, so its static page and read-only search endpoint share one origin.
const fields = ['brand', 'category', 'color', 'size'];
const aggs = Object.fromEntries(fields.map(field => [field, {
terms: { field, size: 12, order: { _count: 'desc' } },
exclude_filters: [field],
zeroes: true
}]));
const request = {
table: 'products',
facet_filter_mode: 'max',
query: { bool: { must: activeFilters } },
aggs
};
This is one useful max-mode shape. It assumes your application has already built activeFilters. exclude_filters: [field] preserves choices in the facet being rendered; use filters for a precise inherited field set. Compare the full live request builder.
const response = await search.search(request);
renderProducts(response.hits.hits);
for (const field of fields) {
const buckets = response.aggregations[field].buckets;
for (const bucket of buckets) {
renderFacetValue({
value: bucket.key,
count: bucket.doc_count,
state: bucket.status
});
}
}
Manticore returns a generic JSON bucket shape: key, doc_count, and status. The UI only decides how to present those values—no browser-side recounting or availability inference. Compare the production renderer.