LeanCloud 迁移到 Parse Server (1)

January 26, 2026

#工作 #数据库

LeanCloud 突然宣布停止对外运营,限期一年要求所有用户都迁移走,没有办法只能硬着头皮上了。

看了一些方案后,觉得还是采用开源的 Parse Server 更适合我,因为 LeanCloud 最开始就是模仿这个的,整体使用的方式上手很轻松。

今天第一篇帖子,先写下数据迁移工作,希望能帮助到其他人。

1️⃣ 准备工作

  1. 前往 LeanCloud 下载 Schema 文件和数据导出的 BSON 文件

  2. 将所有的 BSON 文件命名都修改。例如你的 Class 是 _User.xXXXX.bson.gz,统一修改格式为 _UserTemp.bson.gz(后续会讲解原因)

2️⃣ Parse Server

  1. 编写数据库 Schema 文件,建议直接 Clone parse-server-example 项目到本地(Dashboard 需要自行再配置),基础的构建都是准备好的,Schema.ts 文件你可以直接找到。

  2. 如果你有购买 AI Coding Plan,那么你可以让 AI 帮你读取 LeanCloud 下载的 Schema,并且完善 Parse Server Schema 的编写。

这里有一个坑,你在 Schema 里添加 Class index 时候,如果你某个字段是 Pointer 类型,比如 Notification Class 下有个 user pointer,那么你的索引字段需要是 _p_user,而不是 user

这是因为 Parse 针对 Pointer 字段,在数据库中字段名必须是 _p_XXX 形式的。

最终的 Schema.ts 大概是这样的:

export const schemaDefinitions = [
    {
        className: 'LeanCloud Class Name',
        fields: {
            name: { type: 'String' },
            rank: { type: 'Number' },
            owner: { type: 'Pointer', targetClass: 'ClassName' },
        },
        classLevelPermissions: {
            find: { '*': true },
            count: { '*': true },
            get: { '*': true },
            update: { '*': true },
            create: { '*': true },
            delete: { '*': true },
        },
        indexes: [
            { _p_owner: 1 },
        ],
    }
]
  1. Schema 编写完成后,那么接下来就是恢复 MongoDB 的 BSON 数据。

执行

mongorestore --gzip --dir ~/Downloads/database/ --db dev

// `~/Downloads/database/` 是你下载并且重命名好的数据文件夹。

// `dev` 是你要恢复的数据库名称。
  1. 编写脚本。如果你有 TablePlus 这样的软件,可以通过 GUI 界面看到已经恢复的数据,但是类的名称都是 ClassName + Temp 形式的,这样在 Parse Dashboard 还是看不到。

新建一个 js 文件,mongosh 支持执行 js 文件的。

db = connect('mongodb://localhost:27017/dev'); // 确认你的数据库名称

// 获取 class name,更新 Pointer 字段以兼容 Parse Server
// LeanCloud 中 owner Pointer 字段,是个 Object 类型,是 DBRef 格式的。
// 但是在 Parse Server 中,Pointer 类型是 String 类型,并且通过数据库中的字段名结合来判断,必须是 `_p_xxx` 名称,数据格式必须是 `ClassName$id`
// 这段代码的内容是:
// 1. 查询拥有 owner 字段的数据
// 2. 新增 `_p_owner` 字段
// 3. 读取 `owner` 字段,获取 oid,组成新的 `_p_owner` 字段
// 你写一个后,可以让 AI 帮你通过 Schema 文件,按照这个形式,完善其余的 Class 迁移
db.getCollection("NotificationTemp").updateMany(
    { "owner": { "$exists": true } },
    [
        {
            $set: {
                "_p_owner": {
                    $let: {
                        vars: {
                            ownerId: { $getField: { field: { $literal: "$id" }, input: "$owner" } }
                        },
                        in: {
                            $cond: [
                                { $gt: ["$$ownerId", null] },
                                { $concat: ["_User$", { $toString: "$$ownerId" }] },
                                "$$REMOVE"
                            ]
                        }
                    }
                },
            }
        }
    ]
);

db.getCollection("NotificationTemp").aggregate([
    {
        "$addFields": {
            "_id": { "$toString": "$_id" }, // LeanCloud 使用的 ID 是 ObjectId 类型,Parse Server 需要 String,所以这里做个迁移
        }
    },
    {
        "$project": { // 这段是删除不需要的字段
            "ACL": 0, // 因为我都是通过 Server 查询数据,所以 LeanCloud 的 ACL 我直接移除了,毕竟客户端也不查数据,后台可以用 master key。
            "_w": 0, // _w 和 _r  这 2 个字段会导致 Parse Dashboard 无法正常运行,直接移除,目前也没发现有什么特别用处。
            "_r": 0,
            "owner": 0, // 迁移完成,不需要了
        }
    },
    {
        "$out": "Notification" // 因为 id 从 Object 修改为 String,是不支持在原有表上修改的,所以做一次表的迁移,把 NotificationTemp 迁移到 Notification,完成 id 属性的修改。
        // 所以前面针对 BSON 文件做了一次重命名。
    }
]).toArray();

db.getCollection("NotificationTemp").drop(); // 删除 NotificationTemp 表

当你完成了所有 Class 的迁移脚本后,运行:

mongosh --file migration.js

建议:

每个 Class 都单独写一个 migration 文件的。

最后放到一个统一的 js 文件中。

load('path/classNameMigration.js')
load('path/classNameMigration.js')
load('path/classNameMigration.js')
load('path/classNameMigration.js')
load('path/classNameMigration.js')

然后执行 mongosh --file all-migration.js,这样方便单独修改某个 class,也方便单独执行某个迁移测试。


经过上述的操作,基本上就可以在本地运行起来,通过 Parse Dashboard 查看你的数据,就像在 LeanCloud Console 查看一样的效果了。