# 3.2.1.2.复合数据类型

|     分类     | 数据类型                             |
| :--------: | -------------------------------- |
|  Array 数组型 | 支持数组形式，不需要一个专有的字段数据类型            |
| Object 对象型 | object数据类型：表现形式其实就是单一的JSON对象     |
| Nested 嵌套型 | nested数据类型：表现形式是多个Object型组成的一个数组 |

## Array 数组型

&#x5728;***Elasticsearch***&#x4E2D;，没有特定&#x7684;***array***&#x7C7B;型。默认情况下，任何字段都可以包含0个或者更多值，但是，所&#x6709;***array***&#x4E2D;的值**必须具有相同的数据类型**，例如：

* 字符串数组：`[“one”, “two”]`
* 整数数组：`[1,2]`
* 数组的数组：`[1, [2, 3]]`，等价于`[1,2,3]`
* 对象数组：`[ { "name": "Mary", "age": 12 }, { "name": "John", "age": 10 }]`

> 注意
>
> 对象 数组并不能像你期望的那样工作：你不能独立于数组中的其它对象来查询每一个对象。如果想达到这个目的，你应该使用 ***nest*** 数据类型代替 ***object*** 数据类型

当自动添加一个字段，***array***&#x7684;第一个值决定了字段的类型。所有接下来的值必须使用相同的数据类型或者必须至少能将他们转换为与它相同的类型

数组不支持混合的数据类型：`[10, “some string”]`

数组可以包&#x542B;***null***&#x503C;，这些值可以由配置&#x7684;***null\_value***&#x66FF;换或完全跳过。一个空的`array []`被视为不存在的字段-无值的字段。

在文档中使用

***array***&#x7C7B;型不需要提前做任何配置，天生就支持。

```
PUT my_index/my_type/1
{
  "message": "some arrays in this document...",
  "tags":  [ "elasticsearch", "wow" ],     #1
  "lists": [                               #2
    {
      "name": "prog_list",
      "description": "programming list"
    },
    {
      "name": "cool_list",
      "description": "cool stuff list"
    }
  ]
}

PUT my_index/my_type/2                      #3
{
  "message": "no arrays in this document...",
  "tags":  "elasticsearch",
  "lists": {
    "name": "prog_list",
    "description": "programming list"
  }
}

GET my_index/_search                          #4
{
  "query": {
    "match": {
      "tags": "elasticsearch"
    }
  }
}
```

| 1 | tags 字段会动态添加为一个 string 字段                  |
| - | ------------------------------------------ |
| 2 | lists 字段动态添加为一个 object 字段                  |
| 3 | 第二个文档不包含 arrays，但是可以索隐为相同字段                |
| 4 | 这个查询，在 tags 字段查找 elasticsearch，会同时匹配到这2个文档 |

### 多值字段和倒排索引

事实上，所有字段类型天生支持多值字段源于 ***Luence***。***Lucene*** 设计为一个全文检索引擎。为了在一个大的文本快中查找特定单词，***Lucene***&#x5C06;文本标记为特定的术语，将每一个术语分别添加到倒排索引中。

这意味着即使是一个简单的文本也必须默认支持多值。当其它数据类型如数字或者日期被添加的时，他们使用&#x548C;***strings***&#x4E00;样的数据结构，因此自然变为多值。

## Object 对象型

JSON文档本质上是分层的：文档包含内部对象，内部对象本身还包含内部对象。

```
curl -XPUT 'localhost:9200/my_index/my_type/1?pretty' -d'
{   #1
  "region": "US",
  "manager": {  #2
    "age":     30,
    "name": {   #3
      "first": "John",
      "last":  "Smith"
    }
  }
}'
```

| 1 | 外层的文档是 JSON 对象               |
| - | ---------------------------- |
| 2 | 包含称为`manager`的内部对象           |
| 3 | `manager`对象还包含一个内部对象称为`name` |

在内部，这个文档被索引为一个简单的、扁平的键值对列表，如下所示：

```
{
  "region":             "US",
  "manager.age":        30,
  "manager.name.first": "John",
  "manager.name.last":  "Smith"
}
```

上面文档的显式映射可以长这样：

```
curl -XPUT 'localhost:9200/my_index?pretty' -d'
{
  "mappings": {
    "my_type": {    # 1
      "properties": {
        "region": {
          "type": "keyword"
        },
        "manager": {    # 2
          "properties": {
            "age":  { "type": "integer" },
            "name": {   # 3
              "properties": {
                "first": { "type": "text" },
                "last":  { "type": "text" }
              }
            }
          }
        }
      }
    }
  }
}'
```

| 1 | 映射的类型是一个对象类型，具有一个`properties`字段              |
| - | -------------------------------------------- |
| 2 | `manager`字段是一个内部`object`字段                   |
| 3 | `manager.name`字段是`manager`字段中的一个内部`object`字段 |

不需要显式地将字段类型设置为`object`类型，因为这是默认的类型。

### `object`字段的参数

| 参数 | 说明                                                  |
| -- | --------------------------------------------------- |
| 1  | 新属性是否应动态添加到现有对象。接受 true(默认)，false 和 strict          |
| 2  | 是否应该对对象字段给出的JSON值进行解析和索引(true，默认)或完全忽略(false)       |
| 3  | 为对象中的所有属性设置默认的`include_in_all`值，对象本身没有添加到 \_all 字段。 |
| 4  | 对象内的字段，可以是任何数据类型，包括对象。可以将新属性添加到现有对象。                |

> 注意
>
> 如果你需要索引"对象的数组"而不是"单个的对象"，可以使用`nested`数据类型。

## Nested 嵌套型

`nested`类型是一种对象类型的特殊版本，它允许索引对象数组，独立地索引每个对象。

### 如何使对象数组变扁平

内部类对象数组并不以你预料的方式工作。Lucene没有内部对象的概念，所以Elasticsearch将对象层次扁平化，转化成字段名字和值构成的简单列表。比如，以下的文档：

```
curl -XPUT 'localhost:9200/my_index/my_type/1?pretty' -d'
{
  "group" : "fans",
  "user" : [    # 1
    {
      "first" : "John",
      "last" :  "Smith"
    },
    {
      "first" : "Alice",
      "last" :  "White"
    }
  ]
}'
```

| 1 | user字段作为对象动态添加 |
| - | -------------- |

在内部被转化成如下格式的文档：

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

`user.first`和 `user.last` 扁平化为多值字段，alice 和 white 的关联关系丢失了。**导致这个文档错误地匹配对 alice 和 smith 的查询**

```
curl -XGET 'localhost:9200/my_index/_search?pretty' -d'
{
  "query": {
    "bool": {
      "must": [
        { "match": { "user.first": "Alice" }},
        { "match": { "user.last":  "Smith" }}
      ]
    }
  }
}'
```

返回的两条数据

```
{
  "took": 18,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "failed": 0
  },
  "hits": {
    "total": 1,
    "max_score": 0.51623213,
    "hits": [
      {
        "_index": "range_index",
        "_type": "my_type",
        "_id": "1",
        "_score": 0.51623213,
        "_source": {
          "group": "fans",
          "user": [
            {
              "first": "John",
              "last": "Smith"
            },
            {
              "first": "Alice",
              "last": "White"
            }
          ]
        }
      }
    ]
  }
}
```

### 使用`nested`字段对应`object`数组

如果你需要索引对象数组，并且保持数组中每个对象的独立性，你应该使用`nested`对象类型而不是`object`类型。`nested`对象将数组中每个对象作为独立隐藏文档来索引，这意味着每个嵌套对象都可以独立被搜索：

```
curl -XPUT 'localhost:9200/my_index?pretty' -d'
{
  "mappings": {
    "my_type": {
      "properties": {
        "user": {
          "type": "nested"  # 1
        }
      }
    }
  }
}'
curl -XPUT 'localhost:9200/my_index/my_type/1?pretty' -d'
{
  "group" : "fans",
  "user" : [
    {
      "first" : "John",
      "last" :  "Smith"
    },
    {
      "first" : "Alice",
      "last" :  "White"
    }
  ]
}'
curl -XGET 'localhost:9200/my_index/_search?pretty' -d'
{
  "query": {
    "nested": {
      "path": "user",
      "query": {
        "bool": {
          "must": [
            { "match": { "user.first": "Alice" }},
            { "match": { "user.last":  "Smith" }}   # 2
          ]
        }
      }
    }
  }
}'
curl -XGET 'localhost:9200/my_index/_search?pretty' -d'
{
  "query": {
    "nested": {
      "path": "user",
      "query": {
        "bool": {
          "must": [
            { "match": { "user.first": "Alice" }},
            { "match": { "user.last":  "White" }}   # 3
          ]
        }
      },
      "inner_hits": {   # 4
        "highlight": {
          "fields": {
            "user.first": {}
          }
        }
      }
    }
  }
}'
```

| 1 | `user`字段映射为`nested`类型而不是`objec t`类型 |
| - | ----------------------------------- |
| 2 | 该查询没有匹配，因为`Alice`和`Smith`不在同一个嵌套类中  |
| 3 | 该查询有匹配，因为`Alice`和`White`在同一个嵌套类中    |
| 4 | `inner_hits`可以高亮匹配的嵌套文档             |

嵌套文档可以：

* 使用[`nested`](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-nested-query.html)查询来搜索
* 使用[`nested`](https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-bucket-nested-aggregation.html)和[`reverse_nested`](https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-bucket-reverse-nested-aggregation.html)聚合来分析
* 使用[nested sorting](https://www.elastic.co/guide/en/elasticsearch/reference/current/search-request-sort.html#nested-sorting)来排序&#x20;
* 使用[nested inner hits](https://www.elastic.co/guide/en/elasticsearch/reference/current/search-request-inner-hits.html#nested-inner-hits)来检索和高亮

### `nested`字段参数

| 参数               | 说明                                                  |
| ---------------- | --------------------------------------------------- |
| dynamic          | 新属性是否应动态添加到现有对象。接受 true (默认)， false 和 strict。       |
| include\_in\_all | 为对象中的所有属性设置默认的`include_in_all`值，对象本身没有添加到 \_all 字段。 |
| properties       | 对象内的字段，可以是任何数据类型，包括对象。可以将新属性添加到现有对象。                |

> 注意\
> 因为嵌套文档是作为单独的文档被索引的，所以嵌套文档只能被 nested 查询、nested / reverse\_nested或者 nested inner hits 访问。 比如，一个 string 字段包含嵌套文档，嵌套文档中 index\_options 设置为 offsets 以使用 postings highlighter，这些偏移量在主要的高亮阶段是不可用的。必须通过 nested inner hits 来进行高亮操作。

### 限制`nested`字段的数量

索引一个包含 100 个 `nested`字段的文档实际上就是索引 101 个文档，每个嵌套文档都作为一个独立文档来索引。为了防止过度定义嵌套字段的数量，每个索引可以定义的嵌套字段被限制在 50 个。


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://xiaoxiami.gitbook.io/elasticsearch/ji-chu/mapping/zi-duan-de-shu-ju-lei-xing/3212fu-he-shu-ju-lei-xing.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
