Hi all,
I'm working on a complex e-commerce application that uses Elasticsearch as its backend. We have something that works, but we're trying to optimise some of our queries.
Essentially, we have "groups" of products, where we sell the same product in multiple sizes and colours. We call these variants.
I have a bunch of queries that let me perform faceted navigation of the product catalogue, so I can filter by colour, price, size etc. I have a new requirement: we would like to show one product in each variant group, but allow the user to see the other available variants.
For example, if we sell the "Fancy Rug" in red, blue, and green; when I view the rugs page, I should see one Fancy Rug, with colour swatches so that I can view the other variants. If I filter the page, to view only green products, I should see the green Fancy Rug, but I should still be able to view all the other variants.
I suspect that I need to make two queries: one to find the variant groups that match my filters, and a second to find the other products in each group, but I have this nagging feeling that I did make this work with a single query in the past.
I'm including a sample schema and dataset, and a list of requirements. How would people approach this problem?
Given the following schema
{
"mappings": {
"product": {
"dynamic": false,
"properties": {
"range": { "type": "keyword" },
"colour": { "type": "keyword" },
"name": { "type": "keyword" },
"type": { "type": "keyword" },
"price": { "type": "integer" }
}
}
}
}
and the following products
{ "index": {"_index": "test-products", "_type": "product", "_id": "1" }}
{ "range": "fancy", "type": "rug", "colour": "red", "name": "Fancy red rug", "price": 500 }
{ "index": {"_index": "test-products", "_type": "product", "_id": "2" }}
{ "range": "fancy", "type": "rug", "colour": "blue", "name": "Fancy blue rug", "price": 600 }
{ "index": {"_index": "test-products", "_type": "product", "_id": "3" }}
{ "range": "fancy", "type": "rug", "colour": "green", "name": "Fancy green rug", "price": 700 }
{ "index": {"_index": "test-products", "_type": "product", "_id": "4" }}
{ "range": "boring", "type": "rug", "colour": "green", "name": "Boring green rug", "price": 300 }
{ "index": {"_index": "test-products", "_type": "product", "_id": "5" }}
{ "range": "boring", "type": "rug", "colour": "blue", "name": "Boring blue rug", "price": 300 }
{ "index": {"_index": "test-products", "_type": "product", "_id": "6" }}
{ "range": "boring", "type": "rug", "colour": "red", "name": "Boring red rug", "price": 200 }
On the rugs page, I can see all rugs
With
-
Terms aggregation for colour
-
Range aggregation for price
-
The top hit for each range, plus a list of the other variants.
-
I need to apply source filtering separately to the top hit per range and the other variants because the real documents are large.
{ facets: { colour: { "red": 2, "green": 2, "blue: 2 }, price: { "min": 200, "max": 700 } } products: [ { name: "fancy red rug", price: 700, variants: { price_range: { "min": 500, "max": 700 } products: [ { "colour": "blue", "name": "fancy blue rug" }, { "colour": "green", "name": "fancy green rug" }, ] } }, { name: "boring green rug", price: 200, variants: { price_range: { "min": 200, "max": 300 } products: [ { "colour": "red", "name": "fancy red rug" }, { "colour": "blue", "name": "fancy blue rug" }, ] } } ] }
I can apply a filter to the rugs page, eg. I select the "blue" filter.
-
I should now see the two matching products.
-
I should see the full list of variations for each product
-
The price range aggregation needs to update to reflect the new range.
-
The colour terms aggregation remains the same.
-
The price range of the product group should remain the same.
{ facets: { colour: { "red": 2, "green": 2, "blue: 2 }, price: { "min": 300, "max": 600 } } products: [ { name: "fancy blue rug", price: 600, variants: { price_range: { "min": 500, "max": 700 } products: [ { "colour": "red", "name": "fancy red rug" }, { "colour": "green", "name": "fancy green rug" }, ] } }, { name: "boring blue rug", price: 300, variants: { price_range: { "min": 200, "max": 300 } products: [ { "colour": "red", "name": "fancy red rug" }, { "colour": "green", "name": "fancy green rug" }, ] } } ] }
I can apply a second filter to the rugs page, eg. I select price < 400
-
I should now see one matching product (boring blue rug has price < 400 AND colour = blue)
-
I should see the full list of variations for the product
{ facets: { colour: { "red": 2, "green": 2, "blue: 2 }, price: { "min": 300, "max": 600 } } products: [ { name: "boring blue rug", price: 300, variants: { price_range: { "min": 200, "max": 300 } products: [ { "colour": "red", "name": "fancy red rug" }, { "colour": "green", "name": "fancy green rug" }, ] } } ] }