Building faceted search with elasticsearch for e-commerce: part 3
Before you continue, make sure you read the previous posts:
- Building faceted search with elasticsearch for e-commerce: part 1
- Building faceted search with elasticsearch for e-commerce: part 2
The next step we’ll start filtering our results, without further delays, let’s see it in practice.
Querying
Remembering our options, we have:
Brand
(4) hokey
(4) ubest
(4) beert
Color
(4) white
(2) black
(2) green
(2) yellow
Department
(5) adventure
(3) soccer
(2) casualLet’s imagine we’d like to filter products with brand=hokey, we’ll start using the query parameter to do so:
POST /product/_search
{
"query": {
"bool": {
"filter": [
{
"nested": {
"path": "string_facet",
"query": {
"bool": {
"filter": [
{
"term": {
"string_facet.facet_name": "brand"
}
},
{
"term": {
"string_facet.facet_value": "hokey"
}
}
]
}
}
}
}
]
}
},
"aggs": { no changes here... }
}The query is telling elasticsearch to filter products which contains the facet_name=brand AND facet_value=hokey. As we are using nested type, we must declare nested param as we did in aggregations. The bool.filter is an AND operator.
It then returned, as expected, just 4 hits:
{
"took" : 4,
"timed_out" : false,
"_shards" : { ... },
"hits" : {
"total" : {
"value" : 4,
"relation" : "eq"
},
"max_score" : 0.0,
"hits" : [
{
"_index" : "product",
"_type" : "_doc",
"_id" : "5",
"_score" : 0.0,
"_source" : {
"name" : "Elvin Thompson",
"string_facet" : [
{
"facet_name" : "brand",
"facet_value" : "hokey"
},
{
"facet_name" : "color",
"facet_value" : "yellow"
},
{
"facet_name" : "department",
"facet_value" : "adventure"
}
]
}
},
{
"_index" : "product",
"_type" : "_doc",
"_id" : "7",
"_score" : 0.0,
"_source" : {
"name" : "Erwin Nader",
"string_facet" : [
{
"facet_name" : "brand",
"facet_value" : "hokey"
},
{
"facet_name" : "color",
"facet_value" : "white"
},
{
"facet_name" : "department",
"facet_value" : "adventure"
}
]
}
},
{
"_index" : "product",
"_type" : "_doc",
"_id" : "9",
"_score" : 0.0,
"_source" : {
"name" : "Everett Metz",
"string_facet" : [
{
"facet_name" : "brand",
"facet_value" : "hokey"
},
{
"facet_name" : "color",
"facet_value" : "white"
},
{
"facet_name" : "department",
"facet_value" : "soccer"
}
]
}
},
{
"_index" : "product",
"_type" : "_doc",
"_id" : "10",
"_score" : 0.0,
"_source" : {
"name" : "Leonida Oberbrunner",
"string_facet" : [
{
"facet_name" : "brand",
"facet_value" : "hokey"
},
{
"facet_name" : "color",
"facet_value" : "white"
},
{
"facet_name" : "department",
"facet_value" : "adventure"
}
]
}
}
]
},
"aggregations" : { ... }
}And then, if we want to filter by brand=hokey AND color=white:
POST /product/_search
{
"query": {
"bool": {
"filter": [
{
"nested": {
"path": "string_facet",
"query": {
"bool": {
"filter": [
{
"term": {
"string_facet.facet_name": "brand"
}
},
{
"term": {
"string_facet.facet_value": "hokey"
}
}
]
}
}
}
},
{
"nested": {
"path": "string_facet",
"query": {
"bool": {
"filter": [
{
"term": {
"string_facet.facet_name": "color"
}
},
{
"term": {
"string_facet.facet_value": "white"
}
}
]
}
}
}
}
]
}
},
"aggs": { no changes here... }
}
response:
{
"took" : 10,
"timed_out" : false,
"_shards" : { ... },
"hits" : {
"total" : {
"value" : 3,
"relation" : "eq"
},
"max_score" : 0.0,
"hits" : [
{
"_index" : "product",
"_type" : "_doc",
"_id" : "7",
"_score" : 0.0,
"_source" : {
"name" : "Erwin Nader",
"string_facet" : [
{
"facet_name" : "brand",
"facet_value" : "hokey"
},
{
"facet_name" : "color",
"facet_value" : "white"
},
{
"facet_name" : "department",
"facet_value" : "adventure"
}
]
}
},
{
"_index" : "product",
"_type" : "_doc",
"_id" : "9",
"_score" : 0.0,
"_source" : {
"name" : "Everett Metz",
"string_facet" : [
{
"facet_name" : "brand",
"facet_value" : "hokey"
},
{
"facet_name" : "color",
"facet_value" : "white"
},
{
"facet_name" : "department",
"facet_value" : "soccer"
}
]
}
},
{
"_index" : "product",
"_type" : "_doc",
"_id" : "10",
"_score" : 0.0,
"_source" : {
"name" : "Leonida Oberbrunner",
"string_facet" : [
{
"facet_name" : "brand",
"facet_value" : "hokey"
},
{
"facet_name" : "color",
"facet_value" : "white"
},
{
"facet_name" : "department",
"facet_value" : "adventure"
}
]
}
}
]
},
"aggregations" : { ... }
}Cool! The hits are filtered as expected, but… What about our aggregations? The query does affect it! Lets see what happened:
{
"took" : 2,
"timed_out" : false,
"_shards" : { ... },
"hits" : { ... },
"aggregations" : {
"facets" : {
"doc_count" : 9,
"names" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : "brand",
"doc_count" : 3,
"values" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : "hokey",
"doc_count" : 3
}
]
}
},
{
"key" : "color",
"doc_count" : 3,
"values" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : "white",
"doc_count" : 3
}
]
}
},
{
"key" : "department",
"doc_count" : 3,
"values" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : "adventure",
"doc_count" : 2
},
{
"key" : "soccer",
"doc_count" : 1
}
]
}
}
]
}
}
}
}With that query, ours option are:
Brand
(3) hokey
Color
(3) white
Department
(2) adventure
(1) soccerConclusion
It corresponds exactly with the products returned, but thinking in an e-commerce behavior, it does not look right, let’s see why.
When we filtered by brand=hokey it excluded all other options for brand, the same happened with color=white. But usually, it should be possible to filter by more than one brand (eg.: hokey and beert. It is very common, by the way). In the next chapter (and last!) we’ll see how to fix it.
Leave a comment