Use postFilter

Elevate, May 20-22-2025, Miami Beach, Florida

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 query filter on both the returned product set and the calculated facet counts in a typical faceted search scenario.
  • Construct a ProductSearchRequest that correctly uses postFilter to refine the displayed product results while ensuring that the accompanying facet counts reflect the broader, pre-postFilter query.
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

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.

Example: price range facet

Let's take a price range facet as another example.

Ranges facet example showing price buckets and counts.

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.

Product Search request with facets and no postFilterjson
{
    "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

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.
{
  "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

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.
{
  "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"
    }
  ]
}

Test your knowledge