# 查询、更新数组/嵌套对象

我们可以对对象、对象中的元素、数组、数组中的元素进行匹配查询，甚至还可以对数组和对象相互嵌套的字段进行匹配查询/更新，下面我们从普通匹配开始讲讲如何进行匹配。

* 普通匹配
* 匹配记录中的嵌套字段
* 匹配数组
  * 匹配数组
  * 匹配数组中元素
  * 匹配数组第 n 项元素
  * 结合查询指令进行匹配
  * 匹配并更新数组中的元素
* 匹配多重嵌套的数组和对象

### 普通匹配

传入的对象的每个 `<key, value>` 构成一个筛选条件，有多个 `<key, value>` 则表示需同时满足这些条件，是`与`的关系，如果需要`或`关系，可使用 [`command.or`](/minigame/dev/wxcloud/reference-sdk-api/database/command/Command.or)

比如找出未完成的进度 50 的待办事项：

```js
db.collection('todos').where({
  done: false,
  progress: 50
}).get()
```

### 匹配记录中的嵌套字段

假设在集合中有如下一个记录：

```json
{
  "style": {
    "color": "red"
  }
}
```

如果我们想要找出集合中 `style.color` 为 `red` 的记录，那么可以传入相同结构的对象做查询条件或使用「点表示法」查询：

```js
// 方式一
db.collection('todos').where({
  style: {
    color: 'red'
  }
}).get()

// 方式二
db.collection('todos').where({
  'style.color': 'red'
}).get()
```

### 匹配数组

#### 匹配数组

假设在集合中有如下一个记录：

```json
{
  "numbers": [10, 20, 30]
}
```

可以传入一个完全相同的数组来筛选出这条记录：

```js
db.collection('todos').where({
  numbers: [10, 20, 30]
}).get()
```

#### 匹配数组中的元素

如果想找出数组字段中数组值包含某个值的记录，那可以在匹配数组字段时传入想要匹配的值。如对上面的例子，可传入一个数组中存在的元素来筛选出所有 `numbers` 字段的值包含 20 的记录：

```js
db.collection('todos').where({
  numbers: 20
}).get()
```

#### 匹配数组第 n 项元素

如果想找出数组字段中数组的第 n 个元素等于某个值的记录，那在 `<key, value>` 匹配中可以以 `字段.下标` 为 `key`，目标值为 `value` 来做匹配。如对上面的例子，如果想找出 `number` 字段第二项的值为 20 的记录，可以如下查询（注意：数组下标从 0 开始）：

```js
db.collection('todos').where({
  'numbers.1': 20
}).get()
```

更新也是类似，比如我们要更新 `_id` 为 `test` 的记录的 `numbers` 字段的第二项元素至 30：

```js
db.collection('todos').doc('test').update({
  data: {
    'numbers.1': 30
  },
})
```

#### 结合查询指令进行匹配

在对数组字段进行匹配时，也可以使用如 `lt`, `gt` 等指令，来筛选出字段数组中存在满足给定比较条件的记录。如对上面的例子，可查找出所有 `numbers` 字段的数组值中存在包含大于 25 的值的记录：

```js
const _ = db.command
db.collection('todos').where({
  numbers: _.gt(25)
}).get()
```

查询指令也可以通过逻辑指令组合条件，比如找出所有 `numbers` 数组中存在包含大于 25 的值、同时也存在小于 15 的值的记录：

```js
const _ = db.command
db.collection('todos').where({
  numbers: _.gt(25).and(_.lt(15))
}).get()
```

#### 匹配并更新数组中的元素

如果想要匹配并更新数组中的元素，而不是替换整个数组，除了指定数组下标外，还可以：

**1. 更新数组中第一个匹配到的元素**

更新数组字段的时候可以用 `字段路径.$` 的表示法来更新数组字段的第一个满足查询匹配条件的元素。注意使用这种更新时，查询条件**必须**包含该数组字段。

假如有如下记录：

```json
{
  "_id": "doc1",
  "scores": [10, 20, 30]
}
{
  "_id": "doc2",
  "scores": [20, 20, 40]
}
```

让所有 `scores` 中的第一个 20 的元素更新为 25：

```js
// 注意：批量更新需在云函数中进行
const _ = db.command
db.collection('todos').where({
  scores: 20
}).update({
  data: {
    'scores.$': 25
  }
})
```

如果记录是对象数组的话也可以做到，路径如 `字段路径.$.字段路径`。

注意事项：

* 不支持用在数组嵌套数组
* 如果用 `unset` 更新操作符，不会从数组中去除该元素，而是置为 null
* 如果数组元素不是对象、且查询条件用了 `neq`、`not` 或 `nin`，则不能使用 `$`

**2. 更新数组中所有匹配的元素**

更新数组字段的时候可以用 `字段路径.$[]` 的表示法来更新数组字段的所有元素。

假如有如下记录：

```json
{
  "_id": "doc1",
  "scores": {
    "math": [10, 20, 30]
  }
}
```

比如让 `scores.math` 字段所有数字加 10：

```js
const _ = db.command
db.collection('todos').doc('doc1').update({
  data: {
    'scores.math.$[]': _.inc(10)
  }
})
```

更新后 `scores.math` 数组从 `[10, 20, 30]` 变为 `[20, 30, 40]`。

如果数组是对象数组也是可以的，假如有如下记录：

```json
{
  "_id": "doc1",
  "scores": {
    "math": [
      { "examId": 1, "score": 10 },
      { "examId": 2, "score": 20 },
      { "examId": 3, "score": 30 }
    ]
  }
}
```

可以更新 `scores.math` 下各个元素的 `score` 原子自增 10：

```js
const _ = db.command
db.collection('todos').doc('doc1').update({
  data: {
    'scores.math.$[].score': _.inc(10)
  }
})
```

### 匹配多重嵌套的数组和对象

上面所讲述的所有规则都可以嵌套使用的，假设我们在集合中有如下一个记录：

```json
{
  "root": {
    "objects": [
      {
        "numbers": [10, 20, 30]
      },
      {
        "numbers": [50, 60, 70]
      }
    ]
  }
}
```

我们可以如下找出集合中所有的满足 `root.objects 字段数组的第二项的 numbers 字段的第三项等于 70` 的记录：

```js
db.collection('todos').where({
  'root.objects.1.numbers.2': 70
}).get()
```

注意，指定下标不是必须的，比如可以如下找出集合中所有的满足 `root.objects 字段数组中任意一项的 numbers 字段包含 30` 的记录：

```js
db.collection('todos').where({
  'root.objects.numbers': 30
}).get()
```

更新操作也是类似，比如我们要更新 `_id` 为 `test` 的 `root.objects 字段数组的第二项的 numbers 字段的第三项` 为 80：

```js
db.collection('todos').doc('test').update({
  data: {
    'root.objects.1.numbers.2': 80
  },
})
```
