初始化 JQL
// 先获取数据库实例:
const db = uniCloud.databaseForJQL()
// 获取要操作的数据库表的引用
const collection = db.collection('表名');
增加
const db = uniCloud.databaseForJQL();
// 1. 新增单条数据
const addRes = await db.collection('user').add({
name: '李四',
age: 28,
city: '北京'
});
console.log('新增成功,记录ID:', addRes.id); // 新增记录的 _id
// 2. 批量新增
const batchRes = await db.collection('user').add([
{ name: '王五', age: 30 },
{ name: '赵六', age: 22 }
]);
console.log('批量新增成功,ID列表:', batchRes.ids); // 所有新增记录的 _id 数组
修改
更新数据可以通过 doc(指定ID)或 where(指定条件)来定位记录。
const db = uniCloud.databaseForJQL();
// 1. 通过 ID 更新单条记录
const updateRes1 = await db.collection('user')
.doc('具体的记录_id_here') // 指定要更新的记录ID
.update({
age: 31,
city: '上海'
});
console.log('成功更新条数:', updateRes1.updated);
// 2. 条件更新 (更新所有符合条件的记录)
// 将所有 city 为 "北京" 的用户的 age 设为 30
const updateRes2 = await db.collection('user')
.where('city == "北京"')
.update({
age: 30
});
删除
const db = uniCloud.databaseForJQL();
// 1. 通过 ID 删除单条记录
const deleteRes1 = await db.collection('user')
.doc('具体的记录_id_here')
.remove();
console.log('成功删除条数:', deleteRes1.deleted);
// 2. 条件删除 (删除所有符合条件的记录)
// 删除所有 age 小于 18 的用户
const deleteRes2 = await db.collection('user')
.where('age < 18')
.remove();
查询
基础查询方法
where - 条件查询
字符串语法(推荐):
// 简单条件
const res = await db.collection('user')
.where('name == "张三"') // 注意:字符串要用双引号
.get();
// 组合条件
const res = await db.collection('user')
.where('age > 18 && city == "北京" || status == "vip"')
.get();
// 模糊查询(正则)
const res = await db.collection('user')
.where('/^张/.test(name)') // 姓张的用户
.get();
对象语法:
const dbCmd = db.command;
const res = await db.collection('user')
.where({
age: dbCmd.gt(18), // age > 18
city: dbCmd.in(['北京', '上海']), // 城市在北京或上海
status: 'active' // status == 'active'
})
.get();
field - 指定返回字段
// 只返回指定字段
const res = await db.collection('user')
.field('name, age, city') // 只返回name、age、city
.get();
// 排除某些字段
const res = await db.collection('user')
.field('password, secret_info', true) // true表示排除这些字段
.get();
// 注意:_id 默认总是返回,除非显式排除
const res = await db.collection('user')
.field('_id, name, age') // 返回_id、name、age
.get();
orderBy - 排序
// 单字段排序
const res = await db.collection('article')
.orderBy('publishTime desc') // 降序
.get();
// 多字段排序
const res = await db.collection('article')
.orderBy('category asc, publishTime desc') // 先按分类升序,再按时间降序
.get();
// 对象语法(传统方式)
const res = await db.collection('article')
.orderBy('publishTime', 'desc')
.get();
skip/limit - 分页
const pageIndex = 2;
const pageSize = 10;
const res = await db.collection('article')
.where('status == "published"')
.orderBy('publishTime desc')
.skip((pageIndex - 1) * pageSize) // 跳过前10条
.limit(pageSize) // 取10条
.get();
// 同时获取总数
const countRes = await db.collection('article')
.where('status == "published"')
.count();
console.log('总条数:', countRes.total);
console.log('当前页数据:', res.data);
高级查询功能
正则表达式查询
// 使用正则表达式
const res = await db.collection('user')
.where('/^1[3456789]\\d{9}$/.test(phone)') // 手机号格式
.get();
// 模糊搜索
const keyword = '张';
const res = await db.collection('user')
.where(`new RegExp("${keyword}", "i").test(name)`) // 不区分大小写
.get();
数组字段查询
数组字段 指的是字段的值是一个 数组(列表),数组元素可以是基本类型或对象
出现在uniCloud/MongoDB(文档型数据库)的结构
// 1. 基本类型数组
{
"tags": ["前端", "Vue", "小程序"], // 字符串数组
"scores": [95, 87, 92], // 数字数组
"flags": [true, false, true] // 布尔数组
}
// 2. 对象数组(数组元素是对象)
{
"orders": [ // 对象数组
{
"orderId": "001",
"amount": 100,
"status": "paid"
},
{
"orderId": "002",
"amount": 200,
"status": "pending"
}
]
}
// 3. 混合数组(不推荐,但允许)
{
"mixed": [ // 混合类型数组
"字符串",
123,
true,
{ "key": "value" }
]
}
数组字段的查询方式(JQL)
// 1. 查询数组包含某个值
const res = await db.collection('article')
.where('tags.indexOf("Vue") > -1') // tags数组包含"Vue"
.get();
// 2. 查询数组长度
const res = await db.collection('article')
.where('tags.length > 3') // 标签数量大于3
.get();
// 3. 查询数组第一个元素
const res = await db.collection('article')
.where('tags[0] == "推荐"') // 第一个标签是"推荐"
.get();
// 4. 对象数组查询(复杂)
const res = await db.collection('user')
.where('orders[0].amount > 100') // 第一个订单金额大于100
.get();
// 5. 使用 db.command(传统API风格)
const dbCmd = db.command;
const res = await db.collection('article')
.where({
tags: dbCmd.in(['Vue', 'React']) // tags包含Vue或React
})
.get();
对象字段查询
对象字段 指的是字段的值是一个 JSON对象(键值对集合),可以无限嵌套。
出现在uniCloud/MongoDB(文档型数据库)的结构
// 1. 简单对象字段
{
"address": { // address 是对象字段
"city": "北京",
"district": "朝阳"
}
}
// 2. 多层嵌套对象字段
{
"personal": { // personal 是对象字段
"basic": { // basic 也是对象字段
"name": "张三",
"age": 25
},
"contact": { // contact 也是对象字段
"phone": "13800138000",
"email": "zhangsan@example.com"
}
}
}
// 3. 混合嵌套
{
"company": { // 对象字段
"name": "科技公司",
"address": { // 对象字段中的对象字段
"city": "北京",
"building": {
"name": "科技大厦", // 继续嵌套
"floor": 20
}
}
}
}
对象字段的查询方式(JQL):
// 使用点号(.)访问嵌套属性
const res = await db.collection('user')
.where('address.city == "北京"') // 访问address对象的city属性
.get();
// 多层嵌套
const res = await db.collection('user')
.where('personal.contact.phone == "13800138000"')
.get();
// 对象字段作为条件
const res = await db.collection('user')
.where('company.address.building.floor > 10')
.get();
联表查询
JQL 自动识别关联
// 假设有文章表和评论表:
// 文章表 article:_id, title, content
// 评论表 comment:_id, content, article_id, user_id
// 当执行这个查询时
const articleTemp = db.collection('article')
.where('status == "published"')
.field('_id, title')
.getTemp();
const commentTemp = db.collection('comment')
.where('status == "approved"')
.field('content, article_id, user_id')
.getTemp();
// JQL 会自动:
// 1. 查看 commentTemp 中哪些字段配置了 foreignKey
// 2. 发现 article_id 关联到 article._id
// 3. 自动将 articleTemp 和 commentTemp 进行关联
const res = await db.collection(articleTemp, commentTemp).get();
自动关联的前提是comment 表的 article_id 字段上配置了 "foreignKey": "article._id"
相当于告诉 JQL:comment 表中的 article_id 字段关联到 article 表的 _id 字段,这是一种 外键关系
// 如果想自定义关联条件,可以在 where 中指定
const res = await db.collection(articleTemp, commentTemp)
.where('comment.article_id == article._id') // 显式指定关联条件
.get();
三表联查(文章 + 评论 + 用户)
// 文章临时表
const articleTemp = db.collection('article')
.where('status == "published"')
.field('_id, title, user_id') // 文章的 author_id
.getTemp();
// 评论临时表
const commentTemp = db.collection('comment')
.where('status == "approved"')
.field('content, article_id, user_id')
.getTemp();
// 用户临时表
const userTemp = db.collection('uni-id-users')
.field('_id, username, avatar')
.getTemp();
// 联表查询:文章包含评论,评论包含用户信息
const res = await db.collection(articleTemp, commentTemp, userTemp).get();
// 返回结果结构:
{
data: [{
_id: "article1",
title: "文章标题",
comments: [{
content: "评论内容",
user: { // 自动关联的用户信息
_id: "user1",
username: "张三",
avatar: "avatar.jpg"
}
}]
}]
}
参数顺序与嵌套关系
第一个参数是主表,后面的都是子表
// 文章是主表,评论和用户是子表
const res = await db.collection(articleTemp, commentTemp, userTemp).get();
// 结果:以文章为主,评论嵌套在文章下,用户嵌套在评论下
// 如果把评论放在第一位
const res = await db.collection(commentTemp, articleTemp, userTemp).get();
// 结果:以评论为主,文章和用户嵌套在评论下
第一个参数是主表,它的每条记录都会成为结果数组中的一个顶级元素。
- 第二个表会嵌套在第一个表的结果中
- 第三个表会嵌套在第二个表的结果中
如果是这么安排:collection(userTemp, commentTemp, articleTemp)
const res = await db.collection(userTemp, commentTemp, articleTemp).get();
// 输出结果:
[
{
"_id": "u1",
"name": "张三",
"comments": [ // 用户的所有评论
{
"_id": "c1",
"content": "评论1",
"article": { // 评论对应的文章
"_id": "a1",
"title": "文章1"
}
},
{
"_id": "c3",
"content": "评论3",
"article": {
"_id": "a2",
"title": "文章2"
}
}
]
}
]
聚合查询
分组统计
// 使用传统API方式(JQL暂不支持聚合管道)
const db = uniCloud.database(); // 注意:这里用传统database
const res = await db.collection('order')
.aggregate()
.group({
_id: '$product_id',
totalAmount: db.command.aggregate.sum('$amount'),
avgAmount: db.command.aggregate.avg('$amount'),
count: db.command.aggregate.sum(1)
})
.end();
联表统计(使用 getTemp + JQL)
// 统计每篇文章的评论数
const articleTemp = db.collection('article')
.field('_id, title')
.getTemp();
const commentTemp = db.collection('comment')
.field('article_id')
.groupBy('article_id') // JQL扩展语法
.groupField('count(*) as commentCount')
.getTemp();
const res = await db.collection(articleTemp, commentTemp)
.get();
返回值说明
正常请求返回结果
不同数据库操作返回的结果结构不一样
查询数据
{
errCode: 0,
errMsg: '',
data: []
}
批量发送数据库查询请求
只能批量发送查询请求
{
errCode: 0,
errMsg: '',
dataList: [] // dataList内每一项都是一个查询数据的响应结果 {errCode: 0, errMsg: '', data: []}
}
新增数据
新增单条
{
errCode: 0,
errMsg: '',
id: '' // 新增数据的id
}
新增多条
{
errCode: 0,
errMsg: '',
ids: [], // 新增数据的id列表
inserted: 3 // 新增成功的条数
}
删除数据
{
errCode: 0,
errMsg: '',
deleted: 1 // 删除的条数
}
更新数据
{
errCode: 0,
errMsg: '',
updated: 1 // 更新的条数,数据更新前后无变化则更新条数为0
}
请求报错返回err格式
{
errCode: "", // 错误码
errMsg: "", // 错误信息
}
err.errCode错误码列表
| 错误码 | 描述 |
|---|---|
| TOKEN_INVALID_INVALID_CLIENTID | token校验未通过(设备特征校验未通过) |
| TOKEN_INVALID | token校验未通过(云端已不包含此token) |
| TOKEN_INVALID_TOKEN_EXPIRED | token校验未通过(token已过期) |
| TOKEN_INVALID_WRONG_TOKEN | token校验未通过(token校验未通过) |
| TOKEN_INVALID_ANONYMOUS_USER | token校验未通过(当前用户为匿名用户) |
| SYNTAX_ERROR | 语法错误 |
| PERMISSION_ERROR | 权限校验未通过 |
| VALIDATION_ERROR | 数据格式未通过 |
| DUPLICATE_KEY | 索引冲突 |
| SYSTEM_ERROR | 系统错误 |
其他知识点
聚合操作符:arrayElemAt
arrayElemAt(数组表达式, 下标) 从数组中取出指定位置的元素。其中”数组表达式”可以是
- 直接数组字段:
arrayElemAt(tags, 0)取 tags 数组的第一个元素 - 字段路径表达式:
arrayElemAt(users.name, 0)会先将 users 数组映射为 name 值组成的新数组,再取第一个元素 - 任何能产生数组的表达式
把它想象成两个步骤的函数
// 实际上的执行过程
function arrayElemAt(expr, index) {
// 第一步:解析表达式,得到一个数组
let arr = evaluate(expr); // expr 可能是 users 或 users.name
// 第二步:返回指定下标的元素
return arr[index];
}
// 所以:
arrayElemAt(users, 0) → evaluate(users) → [obj1,obj2,obj3] → [0] → obj1
arrayElemAt(users.name, 0) → evaluate(users.name) → ["a","b","c"] → [0] → "a"
形式1:直接是数组字段
// 数据:{ tags: ["前端", "Vue", "小程序"] }
arrayElemAt(tags, 1) → "Vue"
形式2:是”字段路径”(关键在这里!)
// 数据:{ users: [{name:"张三"},{name:"李四"}] }
arrayElemAt(users.name, 0) → "张三"
// 这等价于:先把 users 数组映射成 name 数组,再取第0个
// 步骤:users.name → ["张三","李四"] → arrayElemAt(...,0) → "张三"
getCount:true 查询记录的同时返回计数
使用JQL的API方式时,可以在get方法内传入参数getCount:true来同时返回总数
// 这以上面的order表数据为例
const db = uniCloud.databaseForJQL()
db.collection('order')
.get({
getCount:true
})
.then(res => {
console.log(res);
}).catch(err => {
console.error(err)
})
返回结果为
{
"code": "",
"message": "",
"data": [{
"_id": "b8df3bd65f8f0d06018fdc250a5688bb",
"book": "3",
"quantity": 555
}],
"count": 5
}