# Use postFilter The `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. Within the `ProductSearchRequest`, `postFilter` serves the following purpose: - **Purpose:** It applies a filter to the query results **after** the main `query` has been executed and **after** the `facets` have been calculated based on that main `query`. 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 The `postFilter` is applied in a specific sequence during a Product Search request: 1. The main `query` is executed. 2. Based on the results of this main `query`, `facets` are calculated. These facet counts represent the distribution of products before any `postFilter` is applied. 3. The `postFilter` (if provided) is then applied to the product set obtained from step 1. 4. The API response includes: - The **products** matching both the main `query` and the `postFilter`. - The **facet counts** calculated in step 2 (that is, reflecting only the main `query`). ## Visualizing the postFilter flow This diagram illustrates how filters affect results and facets, and shows where `postFilter` fits into the lifecycle: - Search Query - Search Query with facets - Search Query with facets and postFiltering ```mermaid graph TD A[All Products] --> B{Main Query Filter}; B --> C[Product Query results]; C --> D{Facet Calculation}; D --> E[Query Response: Products & Facet Counts]; D --> F{postFilter: Reduce the matching products in the Query Response }; F --> G[Query Response: Products & Facet Counts with postFiler]; B --> H[Query Response: Product results only. No facets]; ``` ## 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. 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 the `postFilter`. - 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"). **Summary:** Use `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. ![Ranges facet example showing price buckets and counts.](https://docs.commercetools.com/learning-implement-product-discovery-and-presentation/images/build-product-listing-pages/ranges-facet-example.png) 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 One option is to modify the main `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 A better approach is to add a `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 In summary, you should use `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 Below are examples illustrating how to construct `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. ```json title="Product Search request with facets and no postFilter" { "query": { "filter": [ { "exact": { "field": "variants.attributes.color.key", "fieldType": "enum", "value": "white" } } ] }, "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 This request builds upon the previous one. It still searches for "white" items and defines the same price facets. However, it adds a `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. ```json { "query": { "filter": [ { "exact": { "field": "variants.attributes.color.key", "fieldType": "enum", "value": "white" } } ] }, "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 This is an example of what the response might look like for the request using `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`. ```json { "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" } ] } ``` ## Related pages - [Area overview page with navigation](/learning-implement-product-discovery-and-presentation.md) - [Previous page: Control facet calculations](/learning-implement-product-discovery-and-presentation/build-product-listing-pages/control-facet-calculations.md) - [Next page: Implement search results pages with facets](/learning-implement-product-discovery-and-presentation/build-product-listing-pages/implement-search-results-pages-with-facets.md)