[[complex-core-fields]]=== Complex core field types Besides the simple scalar datatypes that we mentioned above, JSON alsohas `null` values, arrays and objects, all of which are supported byElasticsearch: ==== Multi-value fields It is quite possible that we want our `tag` field to contain morethan one tag. Instead of a single string, we could index an array of tags: ~~~ { "tag": [ "search", "nosql" ]} ~~~ There is no special mapping required for arrays. Any field can contain zero,one or more values, in the same way as a full text field is analyzed toproduce multiple terms. By implication, this means that _all of the values of an array must beof the same datatype_. You can't mix dates with strings. If you createa new field by indexing an array, Elasticsearch will use thedatatype of the first value in the array to determine the `type` of thenew field. The elements inside an array are not ordered. You cannot refer to `the firstelement'' or`the last element''. Rather think of an array as a _bag ofvalues_. ==== Empty fields Arrays can, of course, be empty. This is the equivalent of having zerovalues. In fact, there is no way of storing a `null` value in Lucene, soa field with a `null` value is also considered to be an emptyfield. These four fields would all be considered to be empty, and would not beindexed: ~~~ "empty_string": "","null_value": null,"empty_array": [],"array_with_null_value": [ null ] ~~~ ==== Multi-level objects The last native JSON datatype that we need to discuss is the _object_-- known in other languages as hashes, hashmaps, dictionaries orassociative arrays. _Inner objects_ are often used to embed one entity or object insideanother. For instance, instead of having fields called `user_name`and `user_id` inside our `tweet` document, we could write it as: ```js{ "tweet": "Elasticsearch is very flexible", "user": { "id": "@johnsmith", "gender": "male", "age": 26, "name": { "full": "John Smith", "first": "John", "last": "Smith" } } ### } ==== Mapping for inner objects Elasticsearch will detect new object fields dynamically and map them astype `object`, with each inner field listed under `properties`: ### [source,js] { "gb": { "tweet": { <1> "properties": { "tweet": { "type": "string" }, "user": { <2> "type": "object", "properties": { "id": { "type": "string" }, "gender": { "type": "string" }, "age": { "type": "long" }, "name": { <2> "type": "object", "properties": { "full": { "type": "string" }, "first": { "type": "string" }, "last": { "type": "string" } } } } } } } } ### } <1> Root object. <2> Inner objects. The mapping for the `user` and `name` fields have a similar structureto the mapping for the `tweet` type itself. In fact, the `type` mappingis just a special type of `object` mapping, which we refer to as the_root object_. It is just the same as any other object, except that it hassome special top-level fields for document metadata, like `_source`,the `_all` field etc. ==== How inner objects are indexed Lucene doesn't understand inner objects. A Lucene document consists of a flatlist of key-value pairs. In order for Elasticsearch to index inner objectsusefully, it converts our document into something like this: ### [source,js] { "tweet": [elasticsearch, flexible, very], "user.id": [@johnsmith], "user.gender": [male], "user.age": [26], "user.name.full": [john, smith], "user.name.first": [john], "user.name.last": [smith] ### } _Inner fields_ can be referred to by name, eg `"first"`. To distinguishbetween two fields that have the same name we can use the full _path_,eg `"user.name.first"` or even the `type` name plusthe path: `"tweet.user.name.first"`. NOTE: In the simple flattened document above, there is no field called `user`and no field called `user.name`. Lucene only indexes scalar or simple values,not complex datastructures. ==== Arrays of inner objects Finally, consider how an array containing inner objects would be indexed.Let's say we have a `followers` array which looks like this: ### [source,js] { "followers": [ { "age": 35, "name": "Mary White"}, { "age": 26, "name": "Alex Jones"}, { "age": 19, "name": "Lisa Smith"} ] ### } This document will be flattened as we described above, but theresult will look like this: ### [source,js] { "followers.age": [19, 26, 35], "followers.name": [alex, jones, lisa, smith, mary, white] ### } The correlation between `{age: 35}` and `{name: Mary White}` has been lost aseach multi-value field is just a bag of values, not an ordered array. This issufficient for us to ask: - _Is there a follower who is 26 years old?_ but we can't get an accurate answer to: - _Is there a follower who is 26 years old **and who is called Alex Jones?**_ Correlated inner objects, which are able to answer queries like these,are called _nested_ objects, and we will discuss them later on in<>.