> ## Documentation Index
> Fetch the complete documentation index at: https://docs.devin.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# 从 MongoDB 迁移到 Postgres

export const UseCaseHero = ({title, description, prompt, category, features, devinUrl, agent, intent, playbookId, type}) => {
  const encodedPrompt = encodeURIComponent(prompt || '');
  const tag = 'docs-use-case-gallery';
  const utm = 'utm_source=docs&utm_medium=use-case-gallery&utm_campaign=hero-cta';
  const agentParams = (agent ? '&agent=' + agent : '') + (intent ? '&intent=' + intent : '') + (playbookId ? '&playbookId=' + playbookId : '');
  const devinHref = type === 'schedule' ? 'https://app.devin.ai/settings/schedules/create?' + utm + agentParams + (prompt ? '&prompt=' + encodedPrompt : '') : type === 'review' ? 'https://app.devin.ai/review?' + utm : agent === 'ada' ? 'https://app.devin.ai/search?' + utm + '&noSubmit=true' + (prompt ? '&prompt=' + encodedPrompt : '') : devinUrl ? devinUrl.includes('?') ? devinUrl + '&' + utm + agentParams : devinUrl + '?' + utm + agentParams : prompt ? 'https://app.devin.ai/?tags=' + tag + '&' + utm + agentParams + '&prompt=' + encodedPrompt : 'https://app.devin.ai/?' + utm + agentParams;
  const buttonLabel = type === 'schedule' ? 'Schedule in Devin ↗' : type === 'review' ? 'Set Up Devin Review ↗' : agent === 'advanced' ? 'Try in Devin ↗' : agent === 'dana' ? 'Try in Dana ↗' : agent === 'ada' ? 'Try in Ask Devin ↗' : 'Try in Devin ↗';
  const featureList = features ? features.split(',').map(f => f.trim()) : [];
  return <div className="uc-hero">
      <div className="uc-hero-inner">
        <div className="uc-hero-left">
          <h1 className="uc-hero-title">{title}</h1>
          <p className="uc-hero-desc">{description}</p>
          <div>
            <a href={devinHref} target="_blank" rel="noopener noreferrer" className="try-in-devin-btn">
              {buttonLabel}
            </a>
          </div>
        </div>
        <div className="uc-hero-meta">
          <div className="uc-meta-item">
            <span className="uc-meta-label">Author</span>
            <span className="uc-meta-value">Cognition</span>
          </div>
          <div className="uc-meta-item">
            <span className="uc-meta-label">Category</span>
            <span className="uc-meta-value">{category}</span>
          </div>
          {featureList.length > 0 && <div className="uc-meta-item">
              <span className="uc-meta-label">Features</span>
              <span className="uc-meta-value">{featureList.join(', ')}</span>
            </div>}
        </div>
      </div>
    </div>;
};

export const PromptBlock = ({children, type, agent, intent, playbookId}) => {
  var utm = 'utm_source=docs&utm_medium=use-case-gallery&utm_campaign=prompt-block';
  var tag = 'docs-use-case-gallery';
  var agentParams = (agent ? '&agent=' + agent : '') + (intent ? '&intent=' + intent : '') + (playbookId ? '&playbookId=' + playbookId : '');
  var label = type === 'schedule' ? 'Schedule in Devin' : type === 'playbook' ? 'Create Playbook' : type === 'knowledge' ? 'Add to Knowledge' : agent === 'advanced' ? 'Try in Devin' : agent === 'dana' ? 'Try in Dana' : agent === 'ada' ? 'Try in Ask Devin' : 'Try in Devin';
  var buildUrl = function (text) {
    var encoded = encodeURIComponent(text);
    if (type === 'schedule') return 'https://app.devin.ai/settings/schedules/create?' + utm + agentParams + '&prompt=' + encoded;
    if (type === 'playbook') return 'https://app.devin.ai/settings/playbooks/create?' + utm + '&body=' + encoded;
    if (type === 'knowledge') return 'https://app.devin.ai/knowledge?' + utm + '&body=' + encoded;
    if (agent === 'ada') return 'https://app.devin.ai/search?' + utm + '&noSubmit=true&prompt=' + encoded;
    return 'https://app.devin.ai/?tags=' + tag + '&' + utm + agentParams + '&prompt=' + encoded;
  };
  const ref = React.useRef(null);
  const [href, setHref] = React.useState('#');
  React.useEffect(() => {
    if (!ref.current) return;
    var codeEl = ref.current.querySelector('pre code');
    if (codeEl) {
      var text = codeEl.textContent.trim();
      if (text) setHref(buildUrl(text));
    }
    var header = ref.current.querySelector('[data-component-part="code-block-header"]');
    if (header && !header.querySelector('.prompt-block-devin-link')) {
      var link = document.createElement('a');
      link.href = href;
      link.target = '_blank';
      link.rel = 'noopener noreferrer';
      link.className = 'prompt-block-devin-link';
      link.style.cssText = 'display:inline-flex;align-items:center;gap:6px;text-decoration:none;color:#fff;font-size:11px;font-weight:500;padding:4px 10px;border-radius:6px;white-space:nowrap;background:#317CFF;transition:background 0.2s;margin-left:8px;';
      link.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg> ' + label;
      link.onmouseenter = function () {
        link.style.background = '#2968D9';
      };
      link.onmouseleave = function () {
        link.style.background = '#317CFF';
      };
      header.appendChild(link);
    }
    var existingLink = ref.current.querySelector('.prompt-block-devin-link');
    if (existingLink && href !== '#') existingLink.href = href;
  });
  return <div className="prompt-block" ref={ref}>{children}</div>;
};

<UseCaseHero title="从 MongoDB 迁移到 Postgres" description="将您的应用程序从 MongoDB 迁移到 Postgres——将文档架构转换为关系表，重写查询以使用 Postgres 客户端，并迁移数据。" prompt="将我们的应用程序从 MongoDB 迁移到 Postgres。分析现有的 MongoDB 集合和文档架构，将它们转换为具有正确关系的 Postgres 表，重写所有数据库查询以使用 Postgres 客户端（例如 Prisma、Drizzle 或 node-postgres），创建数据迁移脚本，并验证端到端流程均可正常运行。" category="迁移" features="Playbooks" agent="devin" intent="create" />

<div className="uc-detail-wrapper">
  <Tip>不想手动配置？将本页链接粘贴到 Devin 会话中，让它为你完成所有设置。</Tip>

  <Steps>
    <Step title="设计 Postgres 数据库模式">
      分析你的 MongoDB 集合并设计对应的关系型数据库结构。如果你的数据涉及多个存在关联关系的集合，请先创建实体关系图 (Entity-Relationship Diagram，ERD) ，在编写任何 SQL 之前验证规范化决策。

      关键转换模式：

      * **Document IDs**：将 MongoDB `_id` 字段转换为 UUID 或 SERIAL 主键
      * **Nested objects**：扁平化为带有外键关系的独立表
      * **Arrays**：使用 Postgres 数组类型或单独的关联表
      * **Embedded documents**：抽取到经过适当规范化的关联表中

      <PromptBlock>
        ```txt Analyze MongoDB schema and design Postgres tables theme={null}
        分析我们现有的 MongoDB 集合和文档 schema。
        对每个集合：
        1. 设计一个等价的 PostgreSQL 表，并选择合适的列类型
        2. 识别关系并创建外键
        3. 在合适的情况下，将嵌套对象拆分到独立表中
        4. 创建一个 ERD，展示所有实体及其关系
        5. 将 MongoDB ObjectId 转换为 UUID
        6. 编写用于创建该 schema 的 SQL 迁移脚本
        ```
      </PromptBlock>

      schema 准备就绪后，配置安全性：创建具有适当权限的 Postgres 角色，并在需要行级访问控制的表上启用行级安全策略 (Row Level Security，RLS) 。
    </Step>

    <Step title="迁移后端查询及依赖项">
      将你的 MongoDB 驱动 (例如 `mongoose`、`mongodb`) 替换为 Postgres 客户端库——根据你的技术栈选择 Prisma、Drizzle、TypeORM 或 `node-postgres`。在环境配置中使用 `DATABASE_URL` 或等价的连接凭据进行更新。

      查询迁移模式参考：

      * **Find operations** → SQL `SELECT` 查询或 ORM 的 `.findMany()` / `.select()`
      * **Aggregation pipelines** → 使用 `JOIN`、`GROUP BY`、子查询、窗口函数
      * **Updates** → SQL `UPDATE` 语句
      * **Inserts** → SQL `INSERT` 或批量插入方法

      <PromptBlock>
        ```txt Migrate backend from MongoDB to Postgres theme={null}
        Replace our MongoDB driver with [Prisma / Drizzle / node-postgres].
        For each backend module:
        1. Replace MongoDB imports and connection setup
        2. Rewrite all queries to use the new Postgres client
        3. Update error handling (e.g. unique constraint violations are Postgres code 23505)
        4. Preserve all existing API response formats
        5. Run tests after each module to verify nothing breaks
        ```
      </PromptBlock>

      如果你的应用使用基于 MongoDB 用户存储的自定义 JWT 鉴权，请将用户查询改为使用 Postgres，同时保持现有的令牌生成和校验逻辑不变。
    </Step>

    <Step title="更新前端服务层">
      更新前端服务以处理迁移带来的任何数据结构变更——最常见的是 `_id` 变为 `id`。保持现有服务方法签名不变，这样组件就不需要修改。

      * 如果身份验证流程发生变化，请更新身份验证服务
      * 根据新的数据结构调整 HTTP 客户端的请求/响应处理
      * 如果数据获取模式发生变化，请更新路由守卫和解析器
    </Step>

    <Step title="迁移数据并执行端到端测试">
      导出 MongoDB 数据，将其转换为与 Postgres 模式兼容的结构，然后导入。

      <PromptBlock>
        ```txt Create a data migration script theme={null}
        Write a migration script that:
        1. Exports data from our MongoDB collections
        2. Transforms documents to match the PostgreSQL schema
           (ObjectId → UUID, flatten embedded docs, convert dates)
        3. Imports into PostgreSQL preserving foreign key relationships
        4. Verifies data integrity: row counts, key relationships, spot-checks
        ```
      </PromptBlock>

      导入完成后，在 Postgres 后端上运行完整测试套件。验证所有 CRUD 操作、身份验证流程以及基于角色的访问控制是否都能正常工作。
    </Step>

    <Step title="部署和优化">
      使用功能开关 (feature flags) 进行逐步发布。在迁移过渡期间保留 MongoDB 实例作为回退方案。

      部署后检查清单：

      * 为高频查询的列添加索引
      * 使用 `EXPLAIN ANALYZE` 来识别慢查询
      * 设置连接池 (例如 PgBouncer)
      * 监控查询性能和连接使用率
      * 为关键问题编写回滚流程文档

      <PromptBlock>
        ```txt Post-migration optimization theme={null}
        The MongoDB to Postgres migration is deployed. Run EXPLAIN ANALYZE
        on our most frequent queries and suggest indexes. Check for:
        1. Sequential scans on large tables that should use indexes
        2. Missing indexes on foreign key columns
        3. Queries that could benefit from partial or composite indexes
        4. Connection pool sizing recommendations based on our workload
        ```
      </PromptBlock>

      在一切稳定并完成验证后，停用 MongoDB 实例。
    </Step>

    <Step title="通过 playbook 实现可重复执行">
      如果你需要在多个服务或代码仓库中执行此迁移模式，请将其保存为 [playbook](/zh/product-guides/creating-playbooks)，以确保每次会话都遵循相同的流程。以下是一个将 MongoDB 迁移至 Postgres 的 playbook 示例：

      <PromptBlock type="playbook">
        ```txt MongoDB to PostgreSQL Migration theme={null}
        ## 概述
        将项目从 MongoDB 迁移至 PostgreSQL。本文涵盖 Schema 设计、后端 API 迁移、前端服务层更新、数据迁移及部署。

        ## 用户需提供的信息
        - 源 MongoDB 数据库的访问权限（连接字符串或导出文件）
        - 目标 PostgreSQL 数据库凭据（host、port、database、user、password）
        - 应用程序数据模型及关键查询的说明
        - 新系统的身份验证/授权要求

        ## 第一阶段：数据库 Schema 设计与配置

        ### 步骤 1：项目配置
        1. 创建 PostgreSQL 数据库并记录连接凭据
        2. 分析现有 MongoDB 集合及文档 Schema
        3. 设计具有适当关联关系的 PostgreSQL 等效表结构

        ### 步骤 2：创建 ERD
        若源 MongoDB Schema 涉及多个具有关联关系的集合（嵌入文档、通过 ObjectId 引用或联结模式），请在设计 PostgreSQL 表之前创建实体关系图（ERD）：
        1. 从 MongoDB 集合中识别所有实体及其属性
        2. 梳理实体间的关联关系（一对一、一对多、多对多）
        3. 使用 dbdiagram.io、Mermaid 或 draw.io 等工具生成 ERD
        4. 在编写任何 SQL 之前，使用 ERD 验证规范化决策和外键设计

        若迁移仅涉及单个集合或少量无实质关联关系的独立集合，可跳过此步骤。

        ### 步骤 3：Schema 转换模式
        - 文档 ID：将 MongoDB 的 _id 字段转换为 PostgreSQL 的 UUID 或 SERIAL 主键
        - 嵌套对象：拆分为具有外键关联的独立表
        - 数组：使用 PostgreSQL 数组类型或独立联结表
        - 嵌入文档：提取为经过适当规范化的关联表

        ### 步骤 4：安全配置
        1. 创建具有适当权限的 PostgreSQL 角色和用户（只读、读写、管理员）
        2. 使用 GRANT/REVOKE 按角色限制表和列的访问权限
        3. 若应用程序需要行级访问控制，请在相关表上启用行级安全（RLS）策略

        ### 步骤 5：数据库函数与触发器
        1. 创建用于自动生成记录的用户管理触发器（例如审计时间戳、默认值）
        2. 在适当情况下，使用 PL/pgSQL 实现业务逻辑函数以替代应用层校验

        ## 第二阶段：后端 API 迁移

        ### 步骤 1：依赖管理
        1. 将 MongoDB 驱动（如 mongodb、mongoose）替换为 PostgreSQL 客户端库（如 Node.js 的 pg、Python 的 psycopg2，或 Prisma/Sequelize/TypeORM 等 ORM）
        2. 使用 PostgreSQL 连接凭据更新环境配置（DATABASE_URL 或独立的 host/port/db/user/password 变量）
        3. 移除或替换所有 MongoDB 专用中间件或工具

        ### 步骤 2：身份验证系统更新
        1. 若现有应用使用自定义 JWT 身份验证，保留该方式并将用户查询更新为使用 PostgreSQL
        2. 若迁移至新的身份验证系统，相应替换令牌生成和验证中间件
        3. 保持现有 API 响应格式以确保前端兼容性

        ### 步骤 3：查询迁移模式
        - 查找操作：将 collection.find() 转换为 SQL SELECT 查询（或 ORM 等效方法，如 .findMany()、.select()）
        - 聚合管道：替换为 PostgreSQL 的 JOIN、GROUP BY、子查询和窗口函数
        - 更新操作：将 updateOne/updateMany 转换为 SQL UPDATE 语句
        - 插入操作：将 insertOne/insertMany 替换为 SQL INSERT 语句或批量插入方法

        ### 步骤 4：错误处理更新
        1. 将 MongoDB 专用错误码替换为 PostgreSQL 错误处理（例如，唯一约束冲突的错误码为 23505）
        2. 为前端服务保持一致的错误响应格式

        ## 第三阶段：前端服务层更新

        ### 步骤 1：身份验证服务迁移
        1. 更新用户身份验证服务以适配新的后端身份验证端点
        2. 若身份验证方式发生变更，相应更新登录/登出流程及令牌管理
        3. 修改用户对象创建逻辑以适配 PostgreSQL 的新用户数据格式

        ### 步骤 2：HTTP 客户端更新
        1. 若后端结构发生变更，更新 API 端点 URL
        2. 若数据结构发生变化（例如 _id 变为 id），调整请求/响应处理逻辑
        3. 保持现有服务方法签名以确保组件兼容性

        ### 步骤 3：路由守卫与解析器更新
        1. 更新身份验证守卫以适配新的身份验证系统
        2. 若数据获取模式发生变化，修改路由解析器
        3. 测试现有组件数据流是否保持完整

        ## 第四阶段：数据迁移与测试

        ### 步骤 1：数据导出与转换
        1. 使用 mongoexport 或自定义脚本导出 MongoDB 集合
        2. 转换数据以符合 PostgreSQL Schema 要求
        3. 处理数据类型转换（ObjectId 转 UUID、日期、嵌入文档转外键等）

        ### 步骤 2：数据导入流程
        1. 使用 psql \copy、pg_restore 或自定义迁移脚本导入数据
        2. 导入后验证外键关联关系和约束
        3. 执行行数统计和抽样检查以确认数据完整性

        ### 步骤 3：端到端测试
        1. 使用真实数据测试所有 CRUD 操作
        2. 验证身份验证流程是否正常运行
        3. 测试管理员功能和基于角色的访问控制

        ## 第五阶段：部署与监控

        ### 步骤 1：部署策略
        1. 使用功能标志灰度发布后端变更
        2. 更新前端配置以指向生产环境的 PostgreSQL 后端端点
        3. 配置数据库连接池（如 PgBouncer）并启用监控

        ### 步骤 2：性能优化
        1. 为频繁查询的列添加数据库索引
        2. 使用 EXPLAIN ANALYZE 识别并优化慢查询
        3. 监控查询性能和连接池使用率

        ### 步骤 3：回滚规划
        1. 在过渡期间保留 MongoDB 实例作为备份
        2. 记录关键问题的回滚流程
        3. 根据需要创建数据同步脚本

        ## 关键迁移模式

        ### 身份验证迁移
        - 迁移前：使用 MongoDB 用户存储的自定义 JWT
        - 迁移后：使用 PostgreSQL 用户存储的自定义 JWT（或框架身份验证）
        - 保留：现有用户对象接口和组件契约

        ### 数据库查询迁移
        - 迁移前：MongoDB 聚合管道和文档查询
        - 迁移后：使用 JOIN、GROUP BY 和窗口函数的 SQL 查询
        - 保留：API 响应格式和数据结构

        ### 安全迁移
        - 迁移前：基于 MongoDB 的应用层授权
        - 迁移后：PostgreSQL 角色/权限加可选 RLS 策略
        - 保留：用户角色和权限逻辑

        ## 规范要求
        - 必须保留所有现有 API 契约和响应格式
        - 迁移后必须验证数据完整性（行数、关键关联关系、抽样检查）
        - 在过渡期间，MongoDB 实例应保持可用以作回退
        - 验证：针对 PostgreSQL 后端端到端运行应用程序测试套件，并确认所有测试通过
        ```
      </PromptBlock>
    </Step>
  </Steps>
</div>
