How to Keep Selected Filter Counts Persistent While Updating Other Aggregations in Elasticsearch?

Title: How to Keep Selected Filter Counts Persistent While Updating Other Aggregations in Elasticsearch?

Body:

I'm working on an Elasticsearch-based search and filtering system where we use aggregations to display facet counts for different filter categories. Currently, Elasticsearch dynamically updates all filter counts based on the query results, but we need to modify this behavior.

Expected Behavior:

When a filter is applied to a category (e.g., Segment), the counts for all filters within Segment should remain unchanged, while counts for other filter categories (e.g., Products and Methods) should update dynamically.

Example:

You can see a similar behavior on this website: Titan Watches Filter

  • Click on "SHOW FILTERS"
  • Apply any filter from "Strap Material"
  • The count for Strap Material options remains the same, while counts for other categories (like Brand or Color) change accordingly.

Current Approach:

Right now, we're using Elasticsearch aggregations, but they automatically recalculate counts for all filters based on the query results. We need a way to persist the counts of the selected category while dynamically updating others.

Questions:

  1. Is there a built-in way in Elasticsearch to achieve this behavior?
  2. Would post-filtering or using global aggregations be a potential approach?
  3. Does anyone have an example of how to modify Elasticsearch queries to achieve this behavior?

Any insights, example queries, or workarounds would be really helpful!

@random_user_qna Welcome to the Elastic community!!

I think aggs with global aggregation is good enough. Here is the quick poc

POST _bulk
{ "index": { "_index": "watches", "_id": 1 } }
{ "name": "Titan Leather Watch", "gender": "Men", "brand": "Titan", "strap_material": "Leather", "price": 2500 }
{ "index": { "_index": "watches", "_id": 2 } }
{ "name": "Sonata Plastic Watch", "gender": "Women", "brand": "Sonata", "strap_material": "Plastic", "price": 1800 }
{ "index": { "_index": "watches", "_id": 3 } }
{ "name": "Fastrack Silicone Watch", "gender": "Unisex", "brand": "Fastrack", "strap_material": "Silicone", "price": 2200 }
{ "index": { "_index": "watches", "_id": 4 } }
{ "name": "Kenneth Cole Bimetal Watch", "gender": "Couple", "brand": "Kenneth Cole", "strap_material": "Bimetal", "price": 7500 }
{ "index": { "_index": "watches", "_id": 5 } }
{ "name": "Tommy Hilfiger Leather Watch", "gender": "Girls", "brand": "Tommy Hilfiger", "strap_material": "Leather", "price": 6500 }
{ "index": { "_index": "watches", "_id": 6 } }
{ "name": "SF 18 Karat Gold Watch", "gender": "Guys", "brand": "SF", "strap_material": "18 Karat Gold", "price": 10500 }
{ "index": { "_index": "watches", "_id": 7 } }
{ "name": "Zoop Acetate Watch", "gender": "Kids", "brand": "Zoop", "strap_material": "Acetate", "price": 1200 }
{ "index": { "_index": "watches", "_id": 8 } }
{ "name": "Titan Acetate & Metal Watch", "gender": "Men", "brand": "Titan", "strap_material": "Acetate & Metal", "price": 8000 }
{ "index": { "_index": "watches", "_id": 9 } }
{ "name": "Sonata Plastic Strap Watch", "gender": "Women", "brand": "Sonata", "strap_material": "Plastic", "price": 2100 }
{ "index": { "_index": "watches", "_id": 10 } }
{ "name": "Fastrack Leather Watch", "gender": "Unisex", "brand": "Fastrack", "strap_material": "Leather", "price": 3500 }



GET watches/_search
{
  "size": 0,
  "query": {
    "match": {
      "name": "Titan"
    }
  },
  "aggs": {
    "genders": {
      "terms": {
        "field": "gender.keyword"
      }
    },
    "brands": {
      "terms": {
        "field": "brand.keyword"
      }
    },
    "strap_materials": {
      "global": {},
      "aggs": {
        "materials": {
          "terms": {
            "field": "strap_material.keyword"
          }
        }
      }
    }
  }
}

Response

{
  "took": 0,
  "timed_out": false,
  "_shards": {
    "total": 1,
    "successful": 1,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": {
      "value": 2,
      "relation": "eq"
    },
    "max_score": null,
    "hits": []
  },
  "aggregations": {
    "genders": {
      "doc_count_error_upper_bound": 0,
      "sum_other_doc_count": 0,
      "buckets": [
        {
          "key": "Men",
          "doc_count": 2
        }
      ]
    },
    "brands": {
      "doc_count_error_upper_bound": 0,
      "sum_other_doc_count": 0,
      "buckets": [
        {
          "key": "Titan",
          "doc_count": 2
        }
      ]
    },
    "strap_materials": {
      "doc_count": 10,
      "materials": {
        "doc_count_error_upper_bound": 0,
        "sum_other_doc_count": 0,
        "buckets": [
          {
            "key": "Leather",
            "doc_count": 3
          },
          {
            "key": "Plastic",
            "doc_count": 2
          },
          {
            "key": "18 Karat Gold",
            "doc_count": 1
          },
          {
            "key": "Acetate",
            "doc_count": 1
          },
          {
            "key": "Acetate & Metal",
            "doc_count": 1
          },
          {
            "key": "Bimetal",
            "doc_count": 1
          },
          {
            "key": "Silicone",
            "doc_count": 1
          }
        ]
      }
    }
  }
}

Here the strap_material facets remains unchanged.

This topic was automatically closed 28 days after the last reply. New replies are no longer allowed.