# 数据模型 SDK

## 初始化 SDK

参考 [引入和初始化数据模型 SDK](/minigame/dev/wxcloud/guide/model/init-sdk)

## 数据模型命名空间

### `models`

在初始化 SDK 之后，会自动在 `models` 上挂载针对当前云开发环境下的数据模型的操作方法

通过 `models.<model_name>` 可以访问某个模型的操作方法

例如：

```js
// const wxCloud = client.init(wx.cloud);
// const models = wxCloud.models;

models..create() // 创建单条数据
models..createMany()  // 创建多条数据
models..update() // 更新单条数据
models..updateMany() // 更新多条数据
models..delete() // 删除单条数据
models..deleteMany() // 删除多条数据
models..get() // 查询单条数据
models..list() // 查询多条数据
```

数据模型的方法可以在云函数中和小程序中均可使用

- 在小程序端运行时，默认是当前登录用户的身份调用
- 在云函数中运行时，默认是管理员权限，后续会支持选择以客户端小程序用户身份调用

## 模型查询方法

### `create()`

`create` 方法会创建一条新的数据

> (`params`) => `Promise`>>

#### 参数

| 属性          | 类型 | 默认值 | 示例              | 必填 | 说明                   |
| :------------ | ---- | ------ | ----------------- | ---- | ---------------------- |
| `params.data` | `T`  | 无     | `{name: "hello"}` | 是   | 对应数据模型的字段结构 |

#### 返回值

`Promise`>>

| 属性      | 类型     | 默认值 | 示例           | 必填 | 说明                          |
| :-------- | -------- | ------ | -------------- | ---- | ----------------------------- |
| `data.id` | `string` | 无     | `"7L5G32U9PE"` | 是   | `id` 为对应创建数据对应的标识 |

#### 示例

创建新文章：

```javascript
const { data } = await models.post.create({
  data: {
    body: "你好，世界nnfrom china",
    title: "你好，世界",
    slug: "hello-world-cn",
  },
});

// 返回创建的文章 id
console.log(data);
// { id: "7d8ff72c665eb6c30243b6313aa8539e"}
```

### `createMany()`

`createMany` 方法用于创建多条新的数据记录。

> (`params`) => `Promise`>>

#### 参数

| 属性          | 类型  | 默认值 | 示例                                 | 必填 | 说明                             |
| :------------ | ----- | ------ | ------------------------------------ | ---- | -------------------------------- |
| `params.data` | `T`[] | 无     | `[{name: "hello"}, {name: "world"}]` | 是   | 不能为空数组, 传入空对象会被忽略 |

#### 返回值

`Promise`>>

| 属性          | 类型               | 默认值 | 示例             | 说明                                  |
| :------------ | ------------------ | ------ | ---------------- | ------------------------------------- |
| `data.idList` | `idList: string[]` | 无     | `["7L5G32U9PE"]` | `idList` 为对应创建数据对应的标识列表 |

#### 示例

创建新文章：

```javascript
const { data } = await models.post.createMany({
  data: [
    {
      title: "Hello World",
    },
    {
      title: "Hola Mundo ",
    },
    {
      title: "你好，世界",
    },
    {
      title: "Bonjour le Monde",
    },
  ],
});

// 返回创建的文章 idList
console.log(data);
// {
//   "idList": [
//       "7d8ff72c665ebe5c02442a1a7b29685e",
//       "7d8ff72c665ebe5c02442a1b77feba4b",
//       "7d8ff72c665ebe5c02442a1c48263dc6",
//       "7d8ff72c665ebe5c02442a1d53c311b0"
//   ]
// }
```

### `update()`

`update` 方法用于更新一条已存在的数据记录。

> (`params`) => `Promise`>>

#### 参数

| 属性            | 类型                                         | 默认值 | 示例                                   | 必填 | 说明                   |
| :-------------- | -------------------------------------------- | ------ | -------------------------------------- | ---- | ---------------------- |
| `params.data`   | `T`                                          | 无     | `{name: "hello"}`                      | 是   | 对应数据模型的字段结构 |
| `params.filter` | [`FilterParams`](../client/globals#filterparamst) | 无     | `{filter: {where: {_id:{$eq:"foo"}}}}` | 是   | 过滤条件               |

> [`FilterParams`](../client/globals#filterparamst) 为查询参数的结构，请参考查询参数说明
> 该方法为单条更新方法，如果使用筛选条件，筛选得到多条数据，则不能用这个方法更新

#### 返回值

`Promise`>>

| 属性         | 类型            | 默认值 | 示例 | 说明                                  |
| :----------- | --------------- | ------ | ---- | ------------------------------------- |
| `data.count` | `count: 0 or 1` | 无     | `1`  | 变更的条数，返回非 0 值代表更新成功。 |

#### 示例

更新文章内容：

```javascript
const { data } = await models.post.update({
  data: {
    title: "Hello World",
    body: "Hello World",
  },
  filter: {
    where: {
      _id: {
        $eq: "xxxx", // 推荐传入_id数据标识进行操作
      },
    },
  },
});

// 返回更新成功的条数
console.log(data);
// { count: 1}
```

### `upsert()`

`upsert` 方法用于创建或更新一条记录

> (`params`) => `Promise`\\>\>

#### 参数

| 属性            | 类型                                         | 默认值 | 示例                                   | 必填 | 说明                                                 |
| :-------------- | -------------------------------------------- | ------ | -------------------------------------- | ---- | ---------------------------------------------------- |
| `params.update` | `T`                                          | 无     | `{name: "hello", _id: "foo" }`         | 是   | 对应数据模型的字段结构, 更新记录时会用根据这个来更新 |
| `params.create` | `T`                                          | 无     | `{name: "hello", _id: "foo"}`          | 是   | 对应数据模型的字段结构，新增记录是会根据这个来新增   |
| `params.filter` | [`FilterParams`](../client/globals#filterparamst) | 无     | `{filter: {where: {_id:{$eq:"foo"}}}}` | 是   | 过滤条件                                             |

> [`FilterParams`](../client/globals#filterparamst) 为查询参数的结构，请参考查询参数说明
> 该方法为单条更新方法，如果使用筛选条件，筛选得到多条数据，则不能用这个方法更新

#### 返回值

`Promise`\\>\>

| 属性         | 类型            | 默认值 | 示例 | 说明                                  |
| :----------- | --------------- | ------ | ---- | ------------------------------------- |
| `data.count` | `count: 0 or 1` | 无     | `1`  | 变更的条数，返回非 0 值代表更新成功。 |
| `data.id`    | `string`        | 无     | `1`  | 新增的记录的 id                       |

#### 示例

创建或更新文章内容：

```javascript
const post = {
  title: "Hello World",
  body: "Hello World",
  _id: "foo",
};
const { data } = await models.post.upsert({
  create: post,
  update: post,
  filter: {
    where: {
      _id: {
        $eq: post._id,
      },
    },
  },
});
console.log(data);
// 新增时返回
//   {
//     "count": 0,
//     "id": "foo"
// }

// 更新时返回
//   {
//     "count": 1,
//     "id": ""
// }
```

### `updateMany()`

`updateMany` 方法用于更新多条匹配特定条件的数据记录。

> (`params`) => `Promise`>>

#### 参数

| 属性            | 类型                                         | 默认值 | 示例                    | 必填 | 说明                   |
| :-------------- | -------------------------------------------- | ------ | ----------------------- | ---- | ---------------------- |
| `params.data`   | `T`                                          | 无     | `{name: "hello"}`       | 是   | 对应数据模型的字段结构 |
| `params.filter` | [`FilterParams`](../client/globals#filterparamst) | 无     | `{filter: {where: {}}}` | 是   | 查询条件               |

> [`FilterParams`](../client/globals#filterparamst) 为查询参数的结构，请参考查询参数说明
> 批量更新一次最多只能更新 200 条

#### 返回值

`Promise`>>

| 属性         | 类型            | 默认值 | 示例 | 说明                                |
| :----------- | --------------- | ------ | ---- | ----------------------------------- |
| `data.count` | `count: 0 or 1` | 无     | `1`  | 变更的条数，返回非 0 值代表更新成功 |

#### 示例

例如筛选内容不为空的数据，更新文章内容和别名：

```javascript
const { data } = await models.post.updateMany({
  data: {
    slug: "hello-world",
    body: `
        "你好世界"可以翻译成以下几种语言：

        英语：Hello World 西班牙语：Hola Mundo 法语：Bonjour le Monde 德语：Hallo Welt 意大利语：Ciao Mondo 葡萄牙语：Olá Mundo 荷兰语：Hallo Wereld 日语：こんにちは、世界 (Konnichiwa, Sekai) 韩语：안녕하세요, 세계 (Annyeonghaseyo, Segye)`,
  },
  filter: {
    where: {
      title: {
        $nempty: 1, // 不为空的数据
      },
    },
  },
});

// 返回更新成功的条数
console.log(data);
// {
//   "count": 33
// }
```

### `delete()`

`delete` 方法用于删除一条数据记录。

> (`params`) => `Promise`>>

#### 参数

| 属性            | 类型                                         | 默认值 | 示例                    | 必填 | 说明     |
| :-------------- | -------------------------------------------- | ------ | ----------------------- | ---- | -------- |
| `params.filter` | [`FilterParams`](../client/globals#filterparamst) | 无     | `{filter: {where: {}}}` | 是   | 查询结构 |

> [`FilterParams`](../client/globals#filterparamst) 为查询参数的结构，请参考查询参数说明
> 数据源筛选条件满足条数为 1 以上进行时进行单条更新会出错

#### 返回值

`Promise`>>

| 属性         | 类型            | 默认值 | 示例 | 说明                                |
| :----------- | --------------- | ------ | ---- | ----------------------------------- |
| `data.count` | `count: 0 or 1` | 无     | `1`  | 变更的条数，返回非 0 值代表更新成功 |

#### 示例

删除指定 `_id` 的文章：

```javascript
const { data } = await models.post.delete({
  filter: {
    where: {
      _id: {
        $eq: "xxx", // 推荐传入_id数据标识进行操作
      },
    },
  },
});

// 返回删除成功的条数
console.log(data);
// {
//   "count": 1
// }
```

### `deleteMany()`

`deleteMany` 方法用于删除多条匹配特定条件的数据记录。

> (`params`) => `Promise`>>

#### 参数

| 属性            | 类型                                         | 默认值 | 示例                    | 必填 | 说明         |
| :-------------- | -------------------------------------------- | ------ | ----------------------- | ---- | ------------ |
| `params.filter` | [`FilterParams`](../client/globals#filterparamst) | 无     | `{filter: {where: {}}}` | 是   | 查询过滤参数 |

> [`FilterParams`](../client/globals#filterparamst) 为查询参数的结构，请参考查询参数说明
> 批量删除一次最多只能删除 200 条

#### 返回值

`Promise`>>

| 属性         | 类型            | 默认值 | 示例 | 说明                                |
| :----------- | --------------- | ------ | ---- | ----------------------------------- |
| `data.count` | `count: 0 or 1` | 无     | `1`  | 变更的条数，返回非 0 值代表更新成功 |

#### 示例

例如删除标题为 'Hello World' 的文章：

```javascript
const { data } = await models.post.deleteMany({
  filter: {
    where: {
      title: {
        $eq: "Hello World",
      },
    },
  },
});

console.log(data);
// 返回删除成功的条数
// {
//   "count": 7
// }
```

### `get()`

`get` 方法用于获取一条数据记录。

> (`params`) => `Promise`>

#### 参数

| 属性            | 类型                                         | 默认值 | 示例                    | 必填 | 说明                                                                                 |
| :-------------- | -------------------------------------------- | ------ | ----------------------- | ---- | ------------------------------------------------------------------------------------ |
| `params.filter` | [`FilterParams`](../client/globals#filterparamst) | 无     | `{filter: {where: {}}}` | 是   | 查询参数                                                                             |
| `params.select` | [`SelectParams`](../client/globals#selectparamst) | 无     | `{ $master: true }`     | 是   | 可以指定返回本表或者关联表的字段，如果想查询本表所有字段，请使用 `{ $master: true }` |

#### 返回值

`Promise`>

| 属性   | 类型 | 默认值 | 示例                | 说明                         |
| :----- | ---- | ------ | ------------------- | ---------------------------- |
| `data` | `T`  | 无     | `{ name: "hello" }` | 返回满足筛选条件的模型行记录 |

#### 示例

获取特定文章：

```javascript
const { data } = await models.post.get({
  filter: {
    where: {
      _id: {
        $eq: _id, // 推荐传入_id数据标识进行操作
      },
    },
  },
});

// 返回查询到的数据
console.log(data);
// {
//     "owner": "Anonymous(95fblM7nvPi01yQmYxBvBg)",
//     "createdAt": 1717488585078,
//     "createBy": "Anonymous(95fblM7nvPi01yQmYxBvBg)",
//     "updateBy": "Anonymous(95fblM7nvPi01yQmYxBvBg)",
//     "_openid": "95fblM7nvPi01yQmYxBvBg",
//     "_id": "e2764d2d665ecbc9024b058f1d6b33a4",
//     "title": "你好，世界",
//     "body": ""你好世界"可以翻译成以下几种语言：nn英语：Hello Worldn西班牙语：Hola Mundon法语：Bonjour le Monden德语：Hallo Weltn意大利语：Ciao Mondon葡萄牙语：Olá Mundon荷兰语：Hallo Wereldn日语：こんにちは、世界 (Konnichiwa, Sekai)n韩语：안녕하세요, 세계 (Annyeonghaseyo, Segye)",
//     "slug": "hello-world",
//     "updatedAt": 1717490751944
// }
```

### `list()`

`list` 方法用于获取多条数据记录。

> (`params`) => `Promise`[`<MethodResponse`](../client/globals#methodresponset)>

#### 参数

入参结构

| 属性                 | 类型                                         | 默认值  | 必填 | 说明                                                                                                     |
| :------------------- | -------------------------------------------- | ------- | ---- | -------------------------------------------------------------------------------------------------------- |
| `params.filter`?     | [`FilterParams`](../client/globals#filterparamst) | 无      | 是   | 查询过滤参数                                                                                             |
| `params.select`?     | [`SelectParams`](../client/globals#selectparamst) | 无      | 是   | `{ $master: true }` 可以指定返回本表或者关联表的字段，如果想查询本表所有字段，请使用 `{ $master: true }` |
| `params.getCount`?   | `boolean`                                    | `false` | 否   | 获取 filter 命中条件的查询条数                                                                           |
| `params.pageSize`?   | `number`                                     | 10      | 否   | 分页大小，建议指定，如需设置为其它值，需要和 pageNo 配合使用，两者同时指定才会生效                       |
| `params.pageNumber`? | `number`                                     | 1       | 否   | 分页数目                                                                                                 |
| `params.orderBy`?    | [`OrderByParams`](../client/globals#orderbyparams)[]   | 无      | 否   | 排序，当前仅支持最多 3 字段排序                                                                          |

> 对于`orderBy`的排序，在默认情况下以底层数据库查询结果为准，不存在新建数据一定会排在最前的情况。
> 查询请求单次最大仅支持 200 条

#### 返回值

`Promise`[`<MethodResponse`](../client/globals#methodresponset)>

| 属性           | 类型     | 默认值 | 示例                                     | 说明                                                                                                                                                                               |
| :------------- | -------- | ------ | ---------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `data.records` | `T[]`    | 无     | `[{ name: "hello" }, { name: 'world"'}]` | records 内的数组为对应的数据源数据对象                                                                                                                                             |
| `data.total`?  | `number` | 无     | `3`                                      | 入参配置了 `getCount` 为 `true` 时会返回满足筛选查询条件的大小，注意，此字段不代表返回 records 的长度，可以用作页面大小的计算。当`getCount`为`false`的时候，不应该对此值做任何期待 |

#### 示例

获取所有文章，返回查询到的数据列表 `records` 和 总数 `total`

```javascript
const { data } = await models.post.list({
  filter: {
    where: {},
  },
  pageSize: 10, // 分页大小，建议指定，如需设置为其它值，需要和 pageNo 配合使用，两者同时指定才会生效
  pageNo: 1, // 第几页
  getCount: true, // 开启用来获取总数
});

// 返回查询到的数据列表 `records` 和 总数 `total`
console.log(data);
// {
//     "records": [
//         {
//             "owner": "Anonymous(95fblM7nvPi01yQmYxBvBg)",
//             "createdAt": 1717488585078,
//             "createBy": "Anonymous(95fblM7nvPi01yQmYxBvBg)",
//             "updateBy": "Anonymous(95fblM7nvPi01yQmYxBvBg)",
//             "_openid": "95fblM7nvPi01yQmYxBvBg",
//             "_id": "e2764d2d665ecbc9024b058f1d6b33a4",
//             "title": "你好，世界",
//             "body": ""你好世界"可以翻译成以下几种语言：nn英语：Hello Worldn西班牙语：Hola Mundon法语：Bonjour le Monden德语：Hallo Weltn意大利语：Ciao Mondon葡萄牙语：Olá Mundon荷兰语：Hallo Wereldn日语：こんにちは、世界 (Konnichiwa, Sekai)n韩语：안녕하세요, 세계 (Annyeonghaseyo, Segye)",
//             "slug": "hello-world",
//             "updatedAt": 1717490751944
//         },
//         {
//          ...
//		   },
//     ],
//     "total": 51
// }
```

## 模型查询参数

### `filter`

用于指定记录选择的条件，支持多种过滤方式。

具体可参考 [`FilterParams`](../client/globals#filterparamst)

### `select`

`select` 指定返回数据对象中应包含的字段。

具体可参考 [`SelectParams`](../client/globals#selectparamst)

> ✅ 建议仅选择需要的字段和关系来减少响应数据的大小并提高查询速度。

#### 示例

##### 返回主模型字段

例如，我们在查询单篇文章详情的时候，默认查询了文章主模型的所有字段

```javascript
const { data } = await models.post.get({
  select: {
    $master: true, // 查询主模型所有的字段
  },
  filter: {
    where: {
      _id: {
        $eq: _id, // 推荐传入_id数据标识进行操作
      },
    },
  },
});
// 返回查询到的数据
console.log(data);
// {
//     "owner": "Anonymous(95fblM7nvPi01yQmYxBvBg)",
//     "createdAt": 1717488585078,
//     "createBy": "Anonymous(95fblM7nvPi01yQmYxBvBg)",
//     "updateBy": "Anonymous(95fblM7nvPi01yQmYxBvBg)",
//     "_openid": "95fblM7nvPi01yQmYxBvBg",
//     "_id": "e2764d2d665ecbc9024b058f1d6b33a4",
//     "title": "你好，世界",
//     "body": ""你好世界"可以翻译成以下几种语言：nn英语：Hello Worldn西班牙语：Hola Mundon法语：Bonjour le Monden德语：Hallo Weltn意大利语：Ciao Mondon葡萄牙语：Olá Mundon荷兰语：Hallo Wereldn日语：こんにちは、世界 (Konnichiwa, Sekai)n韩语：안녕하세요, 세계 (Annyeonghaseyo, Segye)",
//     "slug": "hello-world",
//     "updatedAt": 1717490751944
// }
```

##### 返回关联模型字段

筛选关联模型字段，筛选新关联关系

##### 返回指定字段

在查询文章列表的时候，一般情况下我们并不需要查询所有的列表数据，只需要返回必要的字段即可。

```javascript
const { data } = await models.post.list({
  // 只查询必要的字段
  select: {
    _id: true,
    title: true,
    updatedAt: true,
  },
  filter: {
    where: {},
  },
  getCount: true, // 开启用来获取总数
});

// 返回查询到的数据列表 `records` 和 总数 `total
// 返回的内容明显是经过了数据库的过滤，只返回了_id, title, updatedAt三个字段
console.log(data);
// {
//     "records": [
//         {
//             "_id": "e2764d2d665ecbc9024b058f1d6b33a4",
//             "title": "你好，世界",
//             "updatedAt": 1717492882289
//         },
//         {
//             "_id": "e2764d2d665ecbc9024b05905902dab4",
//             "title": "Bonjour le Monde",
//             "updatedAt": 1717488585078
//         },
//         {
//             "_id": "e2764d2d665ecbc9024b058e75abbbeb",
//             "title": "Hola Mundo ",
//             "updatedAt": 1717488585078
//         },
//         {
//             "_id": "e2764d2d665ecbc9024b058d064d8a51",
//             "title": "Hello World",
//             "updatedAt": 1717488585078
//         },
//         {
//             "_id": "e8da0808665ecba20249254f15c211b0",
//             "title": "Bonjour le Monde",
//             "updatedAt": 1717488546718
//         },
//         {
//             "_id": "e8da0808665ecba20249254e2f20781e",
//             "title": "你好，世界",
//             "updatedAt": 1717488546718
//         },
//         {
//             "_id": "e8da0808665ecba20249254d6151ab80",
//             "title": "Hola Mundo ",
//             "updatedAt": 1717488546718
//         },
//         {
//             "_id": "e8da0808665ecba20249254c75aa3e64",
//             "title": "Hello World",
//             "updatedAt": 1717488546718
//         },
//         {
//             "_id": "57bdf48a665ecb9f004fe19b6398933c",
//             "title": "Bonjour le Monde",
//             "updatedAt": 1717488543827
//         },
//         {
//             "_id": "57bdf48a665ecb9f004fe1997b57f79a",
//             "title": "Hola Mundo ",
//             "updatedAt": 1717488543827
//         }
//     ],
//     "total": 51
// }
```

### `getCount`

布尔值，表示是否返回匹配过滤条件的记录总数。

### `orderBy`

指定返回记录的排序方式。

具体可参考 [`OrderByParams`](../client/globals#orderbyparams)[]

#### 示例

```javascript
[
  {
    title: "asc", // 升序
  },
  {
    createdAt: "desc", // 降序
  },
];
```

### `pageSize`

在列表中可指定分页大小，建议指定，如需设置为其它值，需要和 pageNo 配合使用，两者同时指定才会生效

### `pageNo`

指定第几页，在列表查询中可以指定分页

## 数据库原始查询

数据模型 SDK 提供了模型化的数据操作接口，为了进一步满足用户在特定场景下的需求：

- 利用数据库特定的功能或优化策略
- 数据模型 SDK 尚未提供的功能
  - 对于底层为 NoSQL 云数据库类型的模型，我们推荐使用云数据库的 SDK 中的 [聚合搜索](/minigame/dev/wxcloud/guide/database/aggregation/aggregation)、[事务](/minigame/dev/wxcloud/guide/database/transaction) 等来完成数据模型尚未提供的功能，使用数据模型创建的 NoSQL 类型的模型，仍可以使用云数据库的所有能力。

对于底层为 MySQL 数据库类型的模型，我们提供了 MySQL 类型的原生数据库查询语句。

MySQL 数据库类型的模型，数据模型 SDK 提供两种模式的查询方法：

- `$runSQL`: 预编译模式, 通过参数化查询来避免 SQL 注入风险
- `$runSQLRaw` 原始模式, 更加灵活的模式，SQL 语句会当做原始字符串进行查询，存在 SQL 注入的风险

> 注意：
>
> 1. `runSQL`、`runSQLRaw` 接口仅支持在服务端调用，如云函数/云托管/服务器等场景，不支持小程序/web 端直接调用
> 2. 建议优先采用预编译模式，避免 SQL 注入风险
> 3. 当前仅开放了 `select` 语句，如果有其他 SQL 语句需求，请通过官方社群联系我们

### `$runSQL`

此方法仅支持服务端调用

预编译模式下使用参数化查询设计，结合静态模板语法和动态运行时参数，以实现灵活的数据交互。

允许开发者通过 Mustache 变量绑定语法（`{{ }}`）直接在 SQL 查询中嵌入静态参数，同时也支持在运行时通过 `$runSQL()` 方法执行时动态传递参数，可以避免直接拼接字符串导致 SQL 注入的风险。

> (`sql`, `params`?, `config`?) => `Promise`\\>

#### 参数

| 参数      | 类型                                           | 描述         |
| :-------- | :--------------------------------------------- | :----------- |
| `sql`     | `string`                                       | sql 语句     |
| `params`? | `Record`\                    | sql 模版变量 |
| `config`? | [`SQLCommandParams`](../client/globals#sqlcommandparams) | 配置         |

#### 返回值

`Promise`\\>

| 属性                     | 类型       | 默认值 | 示例                                 | 必填 | 说明         |
| :----------------------- | ---------- | ------ | ------------------------------------ | ---- | ------------ |
| `data.total`             | `number`   | 无     | `1`                                  | 是   | 返回数据数   |
| `data.executeResultList` | `array`    | 无     | `[{"read_num":997,"title":"hello"}]` | 是   | 返回数据详情 |
| `data.backendExecute`    | `"string"` | 无     | `27`                                 | 是   | 后端执行时间 |

#### 示例

1. **查询标题为"hello"的记录**：

   ```js
   const result = await models.$runSQL(
     "SELECT * FROM `lcap-wzcs_iuujo7p` WHERE title = {{title}} limit 10",
     {
       title: "hello",
     }
   );

   console.log(result);
   // {"data":{"total":1,"executeResultList":[{"owner":"1739272568342245378","is_published":true,"author_web":"https://qq.com","banner":"cloud://lowcode-0gr8x3i8cd1c6771.6c6f-lowcode-0gr8x3i8cd1c6771-1307578329/weda-uploader/ec687de371d4ad064efd0a424a69e969-logo (1).png","auto_no":"1000","body":"
hello world
","title":"hello","type":"[\"test\",\"test\"]","author_tel":"18588881111","createdAt":1719475245475,"createBy":"1739272568342245378","read_num":997,"updateBy":"1739272568342245378","_openid":"1739272568342245378","extra":"{}","markdown":"# aa\n\n\n\n","author_email":"a@qq.com","json":"{\"a\":\"1\"}","_id":"9JXU7BWFZJ","region":"北京市","updatedAt":1719475245475}],"backendExecute":"27"},"requestId":"16244844-19fe-4946-8924-d35408ced576"}
   ```

2. **查询阅读次数大于 1000 的记录**：

   ```js
   const result = await models.$runSQL(
     "SELECT * FROM `lcap-wzcs_iuujo7p` WHERE read_num > {{num}} limit 10",
     {
       num: 1000,
     }
   );

   console.log(result);
   // {"data":{"total":0,"executeResultList":[],"backendExecute":"23"},"requestId":"2f06b68f-e869-45cb-bb0d-82d50b3dcde0"}
   ```

3. **查询最后更新时间在某个特定时间戳之后的记录**（例如：2024-06-01 00:00:00）：

   ```js
   const result = await models.$runSQL(
     "SELECT * FROM `lcap-wzcs_iuujo7p` WHERE updatedAt > UNIX_TIMESTAMP({{timestamp}})",
     {
       timestamp: "2024-06-01 00:00:00",
     }
   );

   console.log(result);
   // {"data":{"total":1,"executeResultList":[{"owner":"1739272568342245378","is_published":true,"author_web":"https://qq.com","banner":"cloud://lowcode-0gr8x3i8cd1c6771.6c6f-lowcode-0gr8x3i8cd1c6771-1307578329/weda-uploader/ec687de371d4ad064efd0a424a69e969-logo (1).png","auto_no":"1000","body":"
hello world
","title":"hello","type":"[\"test\",\"test\"]","author_tel":"18588881111","createdAt":1719475245475,"createBy":"1739272568342245378","read_num":997,"updateBy":"1739272568342245378","_openid":"1739272568342245378","extra":"{}","markdown":"# aa\n\n\n\n","author_email":"a@qq.com","json":"{\"a\":\"1\"}","_id":"9JXU7BWFZJ","region":"北京市","updatedAt":1719475245475}],"backendExecute":"28"},"requestId":"0d4c98c3-a3ff-4c55-93cc-d0f5c835f82c"}
   ```

4. **查询拥有特定 banner 图片的记录**：

   ```js
   const result = await models.$runSQL(
     "SELECT * FROM `lcap-wzcs_iuujo7p` WHERE banner = '{{url}}';",
     {
       url: "cloud://lowcode-0gr8x3i8cd1c6771.6c6f-lowcode-0gr8x3i8cd1c6771-1307578329/weda-uploader/ec687de371d4ad064efd0a424a69e969-logo (1).png",
     }
   );

   console.log(result);
   // {"data":{"total":1,"executeResultList":[{"owner":"1739272568342245378","is_published":true,"author_web":"https://qq.com","banner":"cloud://lowcode-0gr8x3i8cd1c6771.6c6f-lowcode-0gr8x3i8cd1c6771-1307578329/weda-uploader/ec687de371d4ad064efd0a424a69e969-logo (1).png","auto_no":"1000","body":"
hello world
","title":"hello","type":"[\"test\",\"test\"]","author_tel":"18588881111","createdAt":1719475245475,"createBy":"1739272568342245378","read_num":997,"updateBy":"1739272568342245378","_openid":"1739272568342245378","extra":"{}","markdown":"# aa\n\n\n\n","author_email":"a@qq.com","json":"{\"a\":\"1\"}","_id":"9JXU7BWFZJ","region":"北京市","updatedAt":1719475245475}],"backendExecute":"28"},"requestId":"0d4c98c3-a3ff-4c55-93cc-d0f5c835f82c"}
   ```

5. **查询作者联系电话以"1858"开头的记录**：

   ```js
   const result = await models.$runSQL(
     "SELECT * FROM `lcap-wzcs_iuujo7p` WHERE author_tel LIKE '{{tel}}';",
     {
       tel: "1858%",
     }
   );

   console.log(result);
   // {"data":{"total":1,"executeResultList":[{"owner":"1739272568342245378","is_published":true,"author_web":"https://qq.com","banner":"cloud://lowcode-0gr8x3i8cd1c6771.6c6f-lowcode-0gr8x3i8cd1c6771-1307578329/weda-uploader/ec687de371d4ad064efd0a424a69e969-logo (1).png","auto_no":"1000","body":"
hello world
","title":"hello","type":"[\"test\",\"test\"]","author_tel":"18588881111","createdAt":1719475245475,"createBy":"1739272568342245378","read_num":997,"updateBy":"1739272568342245378","_openid":"1739272568342245378","extra":"{}","markdown":"# aa\n\n\n\n","author_email":"a@qq.com","json":"{\"a\":\"1\"}","_id":"9JXU7BWFZJ","region":"北京市","updatedAt":1719475245475}],"backendExecute":"28"},"requestId":"0d4c98c3-a3ff-4c55-93cc-d0f5c835f82c"}
   ```

6. **查询并计算发布状态为 true 的记录数量**：

   ```js
   const result = await models.$runSQL(
     "SELECT COUNT(*) FROM `lcap-wzcs_iuujo7p` WHERE is_published = {{isPublished}};",
     {
       isPublished: true,
     }
   );

   console.log(result);
   // {"data":{"total":1,"executeResultList":[{"COUNT(*)":1}],"backendExecute":"1717"},"requestId":"f323d96a-8863-48db-a132-ed0fb3fbc727"}
   ```

7. **查询并返回所有记录的标题和阅读次数**：

   ```js
   const result = await models.$runSQL(
     "SELECT read_num,title FROM `lcap-wzcs_iuujo7p`"
   );

   console.log(result);
   // {"data":{"total":1,"executeResultList":[{"read_num":997,"title":"hello"}],"backendExecute":"1916"},"requestId":"845d3fd4-05ce-4277-9a73-2cdd9b5ce04f"}
   ```

### `$runSQLRaw`

此方法仅支持服务端调用

在某些情况（例如动态表名等）下可能希望关闭预编译模式，我们也支持直接传入原始的 SQL 语句的方式来执行 SQL，这种情况下需要自行处理 SQL 注入的防范。

> (`sql`, `config`?) => `Promise`\\>

#### 参数

| 参数      | 类型                                           | 描述     |
| :-------- | :--------------------------------------------- | :------- |
| `sql`     | `string`                                       | sql 语句 |
| `config`? | [`SQLCommandParams`](../client/globals#sqlcommandparams) | 配置     |

#### 返回值

`Promise`\\>

| 属性                     | 类型       | 默认值 | 示例                                 | 必填 | 说明         |
| :----------------------- | ---------- | ------ | ------------------------------------ | ---- | ------------ |
| `data.total`             | `number`   | 无     | `1`                                  | 是   | 返回数据数   |
| `data.executeResultList` | `array`    | 无     | `[{"read_num":997,"title":"hello"}]` | 是   | 返回数据详情 |
| `data.backendExecute`    | `"string"` | 无     | `27`                                 | 是   | 后端执行时间 |

#### 示例

1. **查询标题为"hello"的记录**：

   ```js
   const result = await models.$runSQLRaw(
     "SELECT * FROM `lcap-wzcs_iuujo7p` WHERE title = 'hello' limit 10"
   );

   console.log(result);
   // {"data":{"total":1,"executeResultList":[{"owner":"1739272568342245378","is_published":true,"author_web":"https://qq.com","banner":"cloud://lowcode-0gr8x3i8cd1c6771.6c6f-lowcode-0gr8x3i8cd1c6771-1307578329/weda-uploader/ec687de371d4ad064efd0a424a69e969-logo (1).png","auto_no":"1000","body":"
hello world
","title":"hello","type":"[\"test\",\"test\"]","author_tel":"18588881111","createdAt":1719475245475,"createBy":"1739272568342245378","read_num":997,"updateBy":"1739272568342245378","_openid":"1739272568342245378","extra":"{}","markdown":"# aa\n\n\n\n","author_email":"a@qq.com","json":"{\"a\":\"1\"}","_id":"9JXU7BWFZJ","region":"北京市","updatedAt":1719475245475}],"backendExecute":"27"},"requestId":"16244844-19fe-4946-8924-d35408ced576"}
   ```

2. **查询阅读次数大于 1000 的记录**：

   ```js
   const result = await models.$runSQLRaw(
     "SELECT * FROM `lcap-wzcs_iuujo7p` WHERE read_num > 1000 limit 10"
   );

   console.log(result);
   // {"data":{"total":0,"executeResultList":[],"backendExecute":"23"},"requestId":"2f06b68f-e869-45cb-bb0d-82d50b3dcde0"}
   ```

3. **查询最后更新时间在某个特定时间戳之后的记录**（例如：2024-06-01 00:00:00）：

   ```js
   const result = await models.$runSQLRaw(
     "SELECT * FROM `lcap-wzcs_iuujo7p` WHERE updatedAt > UNIX_TIMESTAMP('2024-06-01 00:00:00')"
   );

   console.log(result);
   // {"data":{"total":1,"executeResultList":[{"owner":"1739272568342245378","is_published":true,"author_web":"https://qq.com","banner":"cloud://lowcode-0gr8x3i8cd1c6771.6c6f-lowcode-0gr8x3i8cd1c6771-1307578329/weda-uploader/ec687de371d4ad064efd0a424a69e969-logo (1).png","auto_no":"1000","body":"
hello world
","title":"hello","type":"[\"test\",\"test\"]","author_tel":"18588881111","createdAt":1719475245475,"createBy":"1739272568342245378","read_num":997,"updateBy":"1739272568342245378","_openid":"1739272568342245378","extra":"{}","markdown":"# aa\n\n\n\n","author_email":"a@qq.com","json":"{\"a\":\"1\"}","_id":"9JXU7BWFZJ","region":"北京市","updatedAt":1719475245475}],"backendExecute":"28"},"requestId":"0d4c98c3-a3ff-4c55-93cc-d0f5c835f82c"}
   ```

4. **查询拥有特定 banner 图片的记录**：

   ```js
   const result = await models.$runSQLRaw(
     "SELECT * FROM `lcap-wzcs_iuujo7p` WHERE banner = 'cloud://lowcode-0gr8x3i8cd1c6771.6c6f-lowcode-0gr8x3i8cd1c6771-1307578329/weda-uploader/ec687de371d4ad064efd0a424a69e969-logo (1).png';"
   );

   console.log(result);
   // {"data":{"total":1,"executeResultList":[{"owner":"1739272568342245378","is_published":true,"author_web":"https://qq.com","banner":"cloud://lowcode-0gr8x3i8cd1c6771.6c6f-lowcode-0gr8x3i8cd1c6771-1307578329/weda-uploader/ec687de371d4ad064efd0a424a69e969-logo (1).png","auto_no":"1000","body":"
hello world
","title":"hello","type":"[\"test\",\"test\"]","author_tel":"18588881111","createdAt":1719475245475,"createBy":"1739272568342245378","read_num":997,"updateBy":"1739272568342245378","_openid":"1739272568342245378","extra":"{}","markdown":"# aa\n\n\n\n","author_email":"a@qq.com","json":"{\"a\":\"1\"}","_id":"9JXU7BWFZJ","region":"北京市","updatedAt":1719475245475}],"backendExecute":"28"},"requestId":"0d4c98c3-a3ff-4c55-93cc-d0f5c835f82c"}
   ```

5. **查询作者联系电话以"1858"开头的记录**：

   ```js
   const result = await models.$runSQLRaw(
     "SELECT * FROM `lcap-wzcs_iuujo7p` WHERE author_tel LIKE '1858%';"
   );

   console.log(result);
   // {"data":{"total":1,"executeResultList":[{"owner":"1739272568342245378","is_published":true,"author_web":"https://qq.com","banner":"cloud://lowcode-0gr8x3i8cd1c6771.6c6f-lowcode-0gr8x3i8cd1c6771-1307578329/weda-uploader/ec687de371d4ad064efd0a424a69e969-logo (1).png","auto_no":"1000","body":"
hello world
","title":"hello","type":"[\"test\",\"test\"]","author_tel":"18588881111","createdAt":1719475245475,"createBy":"1739272568342245378","read_num":997,"updateBy":"1739272568342245378","_openid":"1739272568342245378","extra":"{}","markdown":"# aa\n\n\n\n","author_email":"a@qq.com","json":"{\"a\":\"1\"}","_id":"9JXU7BWFZJ","region":"北京市","updatedAt":1719475245475}],"backendExecute":"28"},"requestId":"0d4c98c3-a3ff-4c55-93cc-d0f5c835f82c"}
   ```

6. **查询并计算发布状态为 true 的记录数量**：

   ```js
   const result = await models.$runSQLRaw(
     "SELECT COUNT(*) FROM `lcap-wzcs_iuujo7p` WHERE is_published = TRUE;",
     {
       isPublished: true,
     }
   );

   console.log(result);
   // {"data":{"total":1,"executeResultList":[{"COUNT(*)":1}],"backendExecute":"1717"},"requestId":"f323d96a-8863-48db-a132-ed0fb3fbc727"}
   ```

7. **查询并返回所有记录的标题和阅读次数**：

   ```js
   const result = await models.$runSQLRaw(
     "SELECT read_num,title FROM `lcap-wzcs_iuujo7p`"
   );

   console.log(result);
   // {"data":{"total":1,"executeResultList":[{"read_num":997,"title":"hello"}],"backendExecute":"1916"},"requestId":"845d3fd4-05ce-4277-9a73-2cdd9b5ce04f"}
   ```

## 过滤条件和运算符

### 逻辑运算符

#### `$and`

使用逻辑 AND 连接条件。

#### `$or`

使用逻辑 OR 连接条件。

### 比较运算符

#### `$eq`

匹配等于指定值的值。

#### `$neq`

匹配不等于指定值的所有值。

#### `$gt`

匹配大于指定值的值。

#### `$gte`

匹配大于或等于指定值的值。

#### `$lt`

匹配小于指定值的值。

#### `$lte`

匹配小于或等于指定值的值。

#### `$in`

匹配数组中指定的任何值。

#### `$nin`

不匹配数组中指定的任何值。

### 特殊运算符

#### `$search`

对字符串字段执行模糊查询。

#### `$nsearch`

查找不包含指定字符串值的记录。

#### `$empty`

数据为 null

#### `nempty`

数据不为 null
