0
votes

I have a products catalogue where every product is indexed as follows (queried from http://localhost:9200/products/_doc/1) as sample:

{
  "_index": "products_20201202145032789",
  "_type": "_doc",
  "_id": "1",
  "_version": 1,
  "_seq_no": 0,
  "_primary_term": 1,
  "found": true,
  "_source": {
    "title": "Roncato Eglo",
    "description": "Amazing LED light made of wood and description continues.",
    "price": 3990,
    "manufacturer": "Eglo",
    "category": [
      "Lights",
      "Indoor lights"
    ],
    "options": [
      {
        "title": "Mount type",
        "value": "E27"
      },
      {
        "title": "Number of bulps",
        "value": "4"
      },
      {
        "title": "Batteries included",
        "value": "true"
      },
      {
        "title": "Ligt temperature",
        "value": "warm"
      },
      {
        "title": "Material",
        "value": "wood"
      },
      {
        "title": "Voltage",
        "value": "230"
      }
    ]
  }
}

Every option contains different value, so there are many Mount type values, Light temperature values, Material values, and so on.

How can I create an aggregation (filter) where I can let customers choose between various Mount Type options:

[ ] E27
[X] E14
[X] GU10
...

Or let them choose from different Material options displayed as checkboxes:

[X] Wood
[ ] Metal
[ ] Glass
...

I can handle it on frontend once the buckets are created. Creation of different buckets for these options is What I am struggling with.

I have succesfully created and displayed and using aggregations for Category, Manufacturer and other basic ones. Thes product options are stored in has_many_through relationships in database. I am using Rails + searchkick gem, but those allow me to create raw queries to elastic search.

1

1 Answers

1
votes

The prerequisite for such aggregation is to have options field as nested.

Sample index mapping:

PUT test
{
  "mappings": {
    "properties": {
      "title": {
        "type": "keyword"
      },
      "options": {
        "type": "nested",
        "properties": {
          "title": {
            "type": "keyword"
          },
          "value": {
            "type": "keyword"
          }
        }
      }
    }
  }
}

Sample docs:

PUT test/_doc/1
{
  "title": "Roncato Eglo",
  "options": [
    {
      "title": "Mount type",
      "value": "E27"
    },
    {
      "title": "Material",
      "value": "wood"
    }
  ]
}

PUT test/_doc/2
{
  "title": "Eglo",
  "options": [
    {
      "title": "Mount type",
      "value": "E27"
    },
    {
      "title": "Material",
      "value": "metal"
    }
  ]
}

Assumption: For a given document a title under option appears only once. For e.g. there can exists only one nested document under option having title as Material.

Query for aggregation:

GET test/_search
{
  "size": 0, 
  "aggs": {
    "OPTION": {
      "nested": {
        "path": "options"
      },
      "aggs": {
        "TITLE": {
          "terms": {
            "field": "options.title",
            "size": 10
          },
          "aggs": {
            "VALUES": {
              "terms": {
                "field": "options.value",
                "size": 10
              }
            }
          }
        }
      }
    }
  }
}

Response:

{
  "took" : 2,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 2,
      "relation" : "eq"
    },
    "max_score" : null,
    "hits" : [ ]
  },
  "aggregations" : {
    "OPTION" : {
      "doc_count" : 4,
      "TITLE" : {
        "doc_count_error_upper_bound" : 0,
        "sum_other_doc_count" : 0,
        "buckets" : [
          {
            "key" : "Material",
            "doc_count" : 2,
            "VALUES" : {
              "doc_count_error_upper_bound" : 0,
              "sum_other_doc_count" : 0,
              "buckets" : [
                {
                  "key" : "metal",
                  "doc_count" : 1
                },
                {
                  "key" : "wood",
                  "doc_count" : 1
                }
              ]
            }
          },
          {
            "key" : "Mount type",
            "doc_count" : 2,
            "VALUES" : {
              "doc_count_error_upper_bound" : 0,
              "sum_other_doc_count" : 0,
              "buckets" : [
                {
                  "key" : "E27",
                  "doc_count" : 2
                }
              ]
            }
          }
        ]
      }
    }
  }
}