uni-app:uni-cloud操作数据库

初始化 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
}

×

喜欢就点赞,疼爱就打赏