Nested objects can come in handy in certain situations. Basically, with nested objects, ElasticSearch allows us to connect multiple documents together—one main document and multiple dependent ones. Now, imagine that we have a shop with clothes and we store the size and color of each T-shirt. Our standard, non-nested mappings could look like the following code (stored in cloth.json
):
{ "cloth" : { "properties" : { "name" : {"type" : "string", "store" : "yes", "index" : "analyzed"}, "size" : {"type" : "string", "store" : "yes", "index" : "not_analyzed"}, "color" : {"type" : "string", "store" : "yes", "index" : "not_analyzed"} } } }
Now imagine that we have a T-shirt in our shop that we only have in the XXL size in "red" and XL size in "black". So our example document could look like the following code:
{ "name" : "Test shirt", "size" : [ "XXL", "XL" ], "color" : [ "red", "black" ] }
But if one of our clients were to search our shop in order to find the XXL T-shirt in black, a query similar to the following one could be run:
curl -XGET 'localhost:9200/shop/cloth/_search?pretty=true' -d '{ "query" : { "bool" : { "must" : [ { "term" : { "size" : "XXL" } }, { "term" : { "color" : "black" } } ] } } }'
We should get no results right? But in fact ElasticSearch returned the following document:
{ (…) "hits" : { "total" : 1, "max_score" : 0.2712221, "hits" : [ { "_index" : "shop", "_type" : "cloth", "_id" : "1", "_score" : 0.2712221, "_source" : { "name" : "Test shirt", "size" : [ "XXL", "XL" ], "color" : [ "red", "black" ]} } ] } }
So, let's modify our mappings to use nested objects to separate color and size to different, nested documents (we store these mappings in the cloth_nested.json
file):
{ "cloth" : { "properties" : { "name" : {"type" : "string", "store" : "yes", "index" : "analyzed"}, "variation" : { "type" : "nested", "properties" : { "size" : {"type" : "string", "store" : "yes", "index" : "not_analyzed"}, "color" : {"type" : "string", "store" : "yes", "index" : "not_analyzed"} } } } } }
As you can see, we've introduced a new object inside our cloth
type, variation
. This is a nested object (type property
set to nested
). It basically says that we will want to index nested documents. Now, let's modify our document to something like the following code:
{ "name" : "Test shirt", "variation" : [ { "size" : "XXL", "color" : "red" }, { "size" : "XL", "color" : "black" } ] }
We've structured the document so that each size and its matching color is a separate document. However, if you were to run our previous query, it wouldn't return any documents. This is because in order to query for nested documents, we need to use a specialized query. So now our query looks like this:
curl -XGET 'localhost:9200/shop/cloth/_search?pretty=true' -d '{ "query" : { "nested" : { "path" : "variation", "query" : { "bool" : { "must" : [ { "term" : { "variation.size" : "XXL" } }, { "term" : { "variation.color" : "black" } } ] } } } } }'
And now, the preceding query wouldn't return the indexed document, because we don't have a nested document that has a size equal to XXL
and color black
.
Lastly, let's look at the query. As you can see, we've used the nested
query in order to search in the nested documents. The path
property specifies the name of the nested object (yes, we can have multiple objects). As you can see, we just included a standard query section under the nested
type. Please also note that we specified the full path for the field names in the nested objects, which is handy when you have multi-level nesting, which is also possible.
If you would like to use the nested type functionality as a filter, you can use it as there is a nested
filter that has the same functionality as the nested
query. Please refer to the Filtering your results section in Chapter 3, Extending Your Structure and Search, for information about filtering.