Nested datatype

edit

The nested type is a specialised version of the object datatype that allows arrays of objects to be indexed in a way that they can be queried independently of each other.

How arrays of objects are flattened

edit

Arrays of inner object fields do not work the way you may expect. Lucene has no concept of inner objects, so Elasticsearch flattens object hierarchies into a simple list of field names and values. For instance, the following document:

PUT my_index/_doc/1
{
  "group" : "fans",
  "user" : [ 
    {
      "first" : "John",
      "last" :  "Smith"
    },
    {
      "first" : "Alice",
      "last" :  "White"
    }
  ]
}

The user field is dynamically added as a field of type object.

would be transformed internally into a document that looks more like this:

{
  "group" :        "fans",
  "user.first" : [ "alice", "john" ],
  "user.last" :  [ "smith", "white" ]
}

The user.first and user.last fields are flattened into multi-value fields, and the association between alice and white is lost. This document would incorrectly match a query for alice AND smith:

GET my_index/_search
{
  "query": {
    "bool": {
      "must": [
        { "match": { "user.first": "Alice" }},
        { "match": { "user.last":  "Smith" }}
      ]
    }
  }
}

Using nested fields for arrays of objects

edit

If you need to index arrays of objects and to maintain the independence of each object in the array, you should use the nested datatype instead of the object datatype. Internally, nested objects index each object in the array as a separate hidden document, meaning that each nested object can be queried independently of the others, with the nested query:

PUT my_index
{
  "mappings": {
    "properties": {
      "user": {
        "type": "nested" 
      }
    }
  }
}

PUT my_index/_doc/1
{
  "group" : "fans",
  "user" : [
    {
      "first" : "John",
      "last" :  "Smith"
    },
    {
      "first" : "Alice",
      "last" :  "White"
    }
  ]
}

GET my_index/_search
{
  "query": {
    "nested": {
      "path": "user",
      "query": {
        "bool": {
          "must": [
            { "match": { "user.first": "Alice" }},
            { "match": { "user.last":  "Smith" }} 
          ]
        }
      }
    }
  }
}

GET my_index/_search
{
  "query": {
    "nested": {
      "path": "user",
      "query": {
        "bool": {
          "must": [
            { "match": { "user.first": "Alice" }},
            { "match": { "user.last":  "White" }} 
          ]
        }
      },
      "inner_hits": { 
        "highlight": {
          "fields": {
            "user.first": {}
          }
        }
      }
    }
  }
}

The user field is mapped as type nested instead of type object.

This query doesn’t match because Alice and Smith are not in the same nested object.

This query matches because Alice and White are in the same nested object.

inner_hits allow us to highlight the matching nested documents.

Nested documents can be:

Because nested documents are indexed as separate documents, they can only be accessed within the scope of the nested query, the nested/reverse_nested aggregations, or nested inner hits.

For instance, if a string field within a nested document has index_options set to offsets to allow use of the postings during the highlighting, these offsets will not be available during the main highlighting phase. Instead, highlighting needs to be performed via nested inner hits. The same consideration applies when loading fields during a search through docvalue_fields or stored_fields.

Parameters for nested fields

edit

The following parameters are accepted by nested fields:

dynamic

Whether or not new properties should be added dynamically to an existing nested object. Accepts true (default), false and strict.

properties

The fields within the nested object, which can be of any datatype, including nested. New properties may be added to an existing nested object.

Limits on nested mappings and objects

edit

As described earlier, each nested object is indexed as a separate document under the hood. Continuing with the example above, if we indexed a single document containing 100 user objects, then 101 Lucene documents would be created — one for the parent document, and one for each nested object. Because of the expense associated with nested mappings, Elasticsearch puts settings in place to guard against performance problems:

index.mapping.nested_fields.limit
The nested type should only be used in special cases, when arrays of objects need to be queried independently of each other. To safeguard against poorly designed mappings, this setting limits the number of unique nested types per index. In our example, the user mapping would count as only 1 towards this limit. Defaults to 50.
index.mapping.nested_objects.limit
This setting limits the number of nested objects that a single document may contain across all nested types, in order to prevent out of memory errors when a document contains too many nested objects. To illustrate how the setting works, say we added another nested type called comments to our example mapping above. Then for each document, the combined number of user and comment objects it contains must be below the limit. Defaults to 10000.

Additional background on these settings, including information on their default values, can be found in Settings to prevent mappings explosion.