不能直接用 remove() 做软删除,必须用 update() 修改 deletedat 或 isdeleted 字段实现;字段需设默认值,权限与查询须过滤 deletedat 为 null,批量操作要分批并服务端生成时间戳。

不能直接用 remove() 做软删除——它物理删库,没留痕也没回滚余地。 软删除本质是改字段(比如加 isDeleted: true 或 deletedAt),不是删文档。UniCloud 本身不提供“软删除”内置命令,得靠 update() 配合条件批量更新来实现。
为什么不能用 collection.where().remove() 实现软删除
因为 remove() 是硬删除:数据从磁盘级移除,_id 失效,事务日志里也不保留原始内容。一旦误操作,连数据库快照都救不回来。软删除必须保留文档结构、权限关系、关联日志,只标记状态。所以所有“软删”动作,底层都得走 update()。
软删除字段设计与权限配置要点
字段名建议统一用 deletedAt(Date 类型)或 isDeleted(Boolean),别用 status: 0 这类模糊值——后续查未删数据时容易漏判、聚合统计也难过滤。
-
deletedAt更推荐:值为null表示未删,有时间戳即为软删,天然支持按删除时间排序/清理 - 数据库 Schema 中必须给该字段设默认值(如
"default": null),否则新插入文档可能缺失该字段,导致查询逻辑出错 - 在
permission配置里,read权限要加条件:{"deletedAt": {"$eq": null}},防止前端意外读到已软删数据 - 云函数内做软删时,不要依赖前端传来的
deletedAt值,必须服务端生成:new Date()
云函数中执行批量软删除的正确写法
核心是用 update() + where(),且必须显式指定要更新的字段,避免覆盖其他业务字段:
exports.main = async (event, context) => {
const db = uniCloud.database()
const collection = db.collection('articles')
// 安全起见,先校验 event.ids 是否为数组且非空
if (!Array.isArray(event.ids) || event.ids.length === 0) {
return { code: 400, msg: 'ids 不能为空' }
}
const res = await collection.where({
_id: db.command.in(event.ids)
}).update({
deletedAt: new Date(), // 服务端写入时间
updatedAt: new Date() // 同步更新时间戳
})
return { updated: res.updated }
}
- 别用
set或inc等原子操作替代update—— 它们不支持批量条件更新 - 如果要支持“恢复删除”,只需再调一次
update()把deletedAt设为null - 大数量(如 >500 条)时,
db.command.in()可能触发阿里云单次查询 ID 数上限(目前 600),此时需分批调用或改用where({ status: 'pending' })等业务字段筛选
前端列表页如何安全展示“未删除”数据
不能只靠前端过滤 deletedAt == null,必须让数据库层就过滤掉——否则用户抓包改请求、绕过页面直接调云函数,就能看到已软删数据。
- 云函数查列表时,
where条件必须包含:{ deletedAt: null }或{ deletedAt: db.command.eq(null) } - 使用
<unicloud-db></unicloud-db>组件时,在where属性里写死条件:where="deletedAt==null",别用动态变量拼接 - 本地 mock 或调试时容易忽略这个条件,上线前务必检查控制台 SQL 日志是否含
deletedAt IS NULL类似语句
软删除真正麻烦的不是代码,而是后续所有查询、统计、导出、API 对接都得主动排除 deletedAt 非空的数据——漏一处,就等于没删。











