Learn to use postFilter to filter results while preserving facet counts.
After completing this page, you should be able to:
- Explain the purpose of
postFilter
and the specific stage at which it is applied within the product search request lifecycle, distinguishing its application from main query filters. - Analyze the distinct effects of applying a filter using
postFilter
versus a main queryfilter
on both the returned product set and the calculated facet counts in a typical faceted search scenario. - Construct a
ProductSearchRequest
that correctly usespostFilter
to refine the displayed product results while ensuring that the accompanying facet counts reflect the broader, pre-postFilter
query.
postFilter
parameter lets you specify an additional filter on the result of the query after the API has calculated facets. This feature is crucial for implementing intuitive faceted navigation and is critical to understand and use effectively with filtering.ProductSearchRequest
, postFilter
serves the following purpose:- Purpose: It applies a filter to the query results after the main
query
has been executed and after thefacets
have been calculated based on that mainquery
.
This functionality is essential for building user-friendly faceted search experiences. It enables users to narrow down the displayed products (for example, by selecting a brand or color) while still seeing facet counts that reflect the broader, pre-selection result set. This allows for further exploration and refinement by the user.
How postFilter works
postFilter
is applied in a specific sequence during a Product Search request:- The main
query
is executed. - Based on the results of this main
query
,facets
are calculated. These facet counts represent the distribution of products before anypostFilter
is applied. - The
postFilter
(if provided) is then applied to the product set obtained from step 1. - The API response includes:
- The products matching both the main
query
and thepostFilter
. - The facet counts calculated in step 2 (that is, reflecting only the main
query
).
- The products matching both the main
Visualizing the postFilter flow
postFilter
fits into the lifecycle:- Search Query
- Search Query with facets
- Search Query with facets and postFiltering
postFilter vs. main query filter
Consider a common scenario in digital commerce: a user searches for "shoes", and your interface displays facets for "Brand" and "Color".
- Initial search:
- Main
query
:"shoes"
- The API returns all shoes and facet counts for all available brands (for example, Nike: 50, Adidas: 40, Puma: 30) and colors.
- Main
Now, the user clicks the "Nike" brand facet. You have two primary options for constructing the subsequent API request:
Option A: Add the filter to the main query (not ideal for dynamic facets)
- Action: Add the Nike filter directly to the main
query
. - Main
query
:"shoes" AND variants.attributes.brand.key="Nike"
- Results: Only Nike shoes are displayed.
- Facet counts: Facet counts are recalculated based only on Nike shoes. So, the "Brand" facet would likely show only "Nike: 50" (or similar), and other brand counts (Adidas, Puma) would become zero or disappear. This makes it difficult for the user to select a different brand or see how many Adidas shoes were in the original "shoes" search without clearing the filter.
Option B: Use postFilter (recommended for dynamic facets)
- Action: Keep the main
query
as "shoes" (or reflecting other persistent filters) and add the Nike filter to thepostFilter
. - Main
query
:"shoes"
postFilter
:variants.attributes.brand.key="Nike"
- Results: Only Nike shoes are displayed (as desired).
- Facet counts: The facet counts are still based on the original "shoes" query. The "Brand" facet will continue to show counts for Nike, Adidas, Puma, and so on, allowing the user to easily switch brands or combine this brand selection with other facet choices (for example, "Color").
postFilter
when you want the displayed products to be filtered, but you want the facet counts to reflect the state before that specific, user-driven filter was applied.Example: price range facet
Let's take a price range facet as another example.

Imagine a UI displaying these price range facets based on an initial query:
- €0 - €49 (Count: 15)
- €50 - €99 (Count: 25)
- €100 - €200 (Count: 10)
If a customer selects the "€50 - €99" price facet, how should the API request change?
Option 1: Change the main query object
query
to include an AND
condition for the price range (for example, price:range(50 to 99)
).- Products: Products priced €50-€99 are shown, which is the desired product set.
- Facets: The facet counts will update to reflect only products in the €50-€99 range. For instance, the price facet might show "€50-€99 (Count: 25)," while other ranges show 0. This is generally unhelpful as the user loses the context of other available price ranges from their original search.
Option 2: Change the query to use postFilter
postFilter
for the selected "€50 - €99" facet, keeping the main query broad.- Products: Products priced €50-€99 are shown (same as Option 1).
- Facets: The facet counts will remain as they were, reflecting all products from the main query:
- €0 - €49 (Count: 15)
- €50 - €99 (Count: 25)
- €100 - €200 (Count: 10) This allows the user to see other available price brackets and easily switch or clear their selection, providing a better user experience.
When to use postFilter
postFilter
when:- Implementing dynamic faceted navigation where users click facet values to refine results.
- You need to show users the broader context of available options (via facet counts) even after they have narrowed down the visible product list.
ProductSearchRequest examples with postFilter
ProductSearchRequest
calls with and without postFilter
.Example 1: Product Search request with facets and no postFilter
This request searches for "white" items and defines price range facets. The facet counts returned will be based on all "white" items.
{
"query": {
"filter": [
{
"fullText": {
"field": "name",
"language": "en",
"value": "white",
"caseInsensitive": true
}
}
]
},
"facets": [
{
"ranges": {
"name": "price",
"field": "variants.prices.centAmount",
"fieldType": "long",
"ranges": [
{
"key": "* to 50",
"to": 5000
},
{
"key": "50 to 100",
"from": 5000,
"to": 10000
},
{
"key": "100 to *",
"from": 10000
}
]
}
}
],
"limit": 1
}
Example 2: Product Search request with postFilter
postFilter
to refine the displayed products to only those in the 50-100 price range (specifically, where variants.prices.centAmount
is >= 5000 and < 10000). The facet counts, however, will still reflect all "white" items across all price ranges defined in the facets
section.{
"query": {
"filter": [
{
"fullText": {
"field": "name",
"language": "en",
"value": "white",
"caseInsensitive": true
}
}
]
},
"facets": [
{
"ranges": {
"name": "price",
"field": "variants.prices.centAmount",
"fieldType": "long",
"ranges": [
{
"key": "* to 50",
"to": 5000
},
{
"key": "50 to 100",
"from": 5000,
"to": 10000
},
{
"key": "100 to *",
"from": 10000
}
]
}
}
],
"postFilter": {
"range": {
"field": "variants.prices.centAmount",
"gte": 5000,
"lt": 10000
}
},
"limit": 1
}
Example 3: corresponding response
postFilter
. The results
array would contain products matching "white" AND priced between 50 and 100 (if limit
allowed and such products exist). The facets
section shows the buckets
(counts) for each price range based on the broader query for "white" items, not just those filtered by the postFilter
.{
"total": 244,
"offset": 0,
"limit": 1,
"facets": [
{
"name": "price",
"buckets": [
{
"key": "* to 50",
"count": 22
},
{
"key": "50 to 100",
"count": 101
},
{
"key": "100 to *",
"count": 208
}
]
}
],
"results": [
{
"id": "26beb46b-7d09-4700-b3ce-e678ae1abc6c"
}
]
}