> ## 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 コレクションを分析し、対応するリレーショナルスキーマを設計します。データが複数のコレクション間の関連を伴う場合は、SQL を書き始める前に、まずエンティティ・リレーションシップ図 (ERD) を作成して、正規化方針を検証してください。

      主な変換パターンは次のとおりです:

      * **Document IDs**: MongoDB の `_id` フィールドを UUID または SERIAL 型の主キーに変換する
      * **Nested objects**: 別テーブルに分割し、外部キーで関連付ける
      * **Arrays**: Postgres の配列型、または中間 (ジャンクション) テーブルを使用する
      * **Embedded documents**: 正規化に沿って別の関連テーブルとして切り出す

      <PromptBlock>
        ```txt Analyze MongoDB schema and design Postgres tables theme={null}
        Analyze our existing MongoDB collections and document schemas.
        For each collection:
        1. Design a PostgreSQL table equivalent with proper column types
        2. Identify relationships and create foreign keys
        3. Flatten nested objects into separate tables where appropriate
        4. Create an ERD showing all entities and relationships
        5. Convert MongoDB ObjectIds to UUIDs
        6. Write the SQL migration to create the schema
        ```
      </PromptBlock>

      スキーマの設計ができたら、セキュリティを設定します。適切な権限を持つ Postgres ロールを作成し、行レベルのアクセス制御が必要なテーブルに対して Row-Level Security (RLS) ポリシーを有効化します。
    </Step>

    <Step title="バックエンドのクエリおよび依存関係を移行する">
      MongoDB ドライバ (例: `mongoose`, `mongodb`) を、スタックに応じて Prisma、Drizzle、TypeORM、または `node-postgres` などの 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}
        MongoDB ドライバを [Prisma / Drizzle / node-postgres] に置き換えてください。
        各バックエンドモジュールごとに:
        1. MongoDB の import と接続セットアップを置き換える
        2. すべてのクエリを書き換えて新しい Postgres クライアントを使うようにする
        3. エラーハンドリングを更新する（例: 一意制約違反は Postgres コード 23505）
        4. 既存のすべての API レスポンス形式を維持する
        5. 各モジュールごとにテストを実行し、不具合がないことを確認する
        ```
      </PromptBlock>

      アプリが MongoDB のユーザーストアを使ったカスタム JWT 認証を利用している場合は、トークンの生成と検証ロジックはそのままに、ユーザー検索クエリだけを Postgres を使うように更新してください。
    </Step>

    <Step title="フロントエンドのサービス層を更新する">
      マイグレーションによるデータ形式の変更 (もっとも一般的なのは `_id` が `id` になるケース) に対応できるよう、フロントエンドサービスを更新します。既存のサービスメソッドのシグネチャは維持し、コンポーネント側に変更が不要なようにします。

      * 認証フローが変わった場合は、認証サービスを更新する
      * 新しいデータ形式に合わせて HTTP クライアントのリクエスト／レスポンス処理を調整する
      * データ取得パターンが変わった場合は、ルートガードやリゾルバを更新する
    </Step>

    <Step title="データの移行とエンドツーエンドテストの実行">
      MongoDB のデータをエクスポートし、Postgres のスキーマに合わせて変換してからインポートします。

      <PromptBlock>
        ```txt データ移行スクリプトを作成する theme={null}
        次の処理を行うマイグレーションスクリプトを作成してください:
        1. MongoDB コレクションからデータをエクスポートする
        2. ドキュメントを PostgreSQL のスキーマに合うように変換する
           (ObjectId → UUID、埋め込みドキュメントのフラット化、日付の変換)
        3. 外部キー制約を保持したまま PostgreSQL にインポートする
        4. データ整合性を検証する: 行数、キー関係、スポットチェック
        ```
      </PromptBlock>

      インポート後、Postgres バックエンドに対してフルテストスイートを実行します。すべての CRUD 操作、認証フロー、ロールベースのアクセス制御が正しく動作することを確認します。
    </Step>

    <Step title="デプロイと最適化">
      機能フラグを使って段階的にデプロイします。移行期間中は MongoDB インスタンスをフォールバックとして維持してください。

      デプロイ後チェックリスト:

      * 頻繁にクエリされるカラムにインデックスを追加する
      * `EXPLAIN ANALYZE` を使用して遅いクエリを特定する
      * 接続プーリング (例: PgBouncer) をセットアップする
      * クエリパフォーマンスと接続利用状況を監視する
      * 重大な問題が発生した場合のロールバック手順を文書化する

      <PromptBlock>
        ```txt Post-migration optimization theme={null}
        MongoDB から Postgres への移行が完了しています。最も頻度の高いクエリに対して EXPLAIN ANALYZE を実行し、インデックスを提案してください。次の点を確認します:
        1. 本来インデックスを使うべき大規模テーブルに対するシーケンシャルスキャン
        2. 外部キー列に対して欠落しているインデックス
        3. パーシャルインデックスまたは複合インデックスの適用で性能改善が見込めるクエリ
        4. ワークロードに基づく接続プールサイズの推奨値
        ```
      </PromptBlock>

      すべてが安定し検証できたら、MongoDB インスタンスを廃止します。
    </Step>

    <Step title="プレイブック化して再現可能にする">
      この移行パターンを複数のサービスやリポジトリで実行する必要がある場合は、[プレイブック](/ja/product-guides/creating-playbooks)として保存しておくと、すべてのセッションで同じプロセスを実行できます。以下は、MongoDB から Postgres への移行に関するプレイブックの例です。

      <PromptBlock type="playbook">
        ```txt MongoDB to PostgreSQL Migration theme={null}
        ## 概要
        プロジェクトをMongoDBからPostgreSQLへ移行します。スキーマ設計、バックエンドAPIの移行、フロントエンドサービス層の更新、データ移行、およびデプロイを対象とします。

        ## ユーザーから必要な情報
        - 移行元MongoDBデータベースへのアクセス（接続文字列またはエクスポート）
        - 移行先PostgreSQLデータベースの認証情報（ホスト、ポート、データベース、ユーザー、パスワード）
        - アプリケーションのデータモデルと主要クエリの説明
        - 新システムに対する認証・認可の要件

        ## フェーズ1: データベーススキーマ設計とセットアップ

        ### ステップ1: プロジェクトのセットアップ
        1. PostgreSQLデータベースをプロビジョニングし、接続認証情報を記録する
        2. 既存のMongoDBコレクションとドキュメントスキーマを分析する
        3. 適切なリレーションシップを持つPostgreSQLテーブルの対応設計を行う

        ### ステップ2: ERD作成
        移行元のMongoDBスキーマが複数のコレクション間のリレーションシップ（埋め込みドキュメント、ObjectIdによる参照、または中間テーブルパターン）を含む場合、PostgreSQLテーブルを設計する前にER図（ERD）を作成してください：
        1. MongoDBコレクションからすべてのエンティティとその属性を洗い出す
        2. エンティティ間のリレーションシップを整理する（一対一、一対多、多対多）
        3. dbdiagram.io、Mermaid、draw.ioなどのツールを使用してERDを作成する
        4. SQLを記述する前に、ERDを使用して正規化の判断と外部キー設計を検証する

        移行対象が単一のコレクション、または意味のあるリレーションシップを持たない少数の独立したコレクションのみの場合は、このステップをスキップしてください。

        ### ステップ3: スキーマ変換パターン
        - ドキュメントID: MongoDBの_idフィールドをPostgreSQLのUUIDまたはSERIAL主キーに変換する
        - ネストされたオブジェクト: 外部キーリレーションシップを持つ別テーブルにフラット化する
        - 配列: PostgreSQLの配列型または別の中間テーブルを使用する
        - 埋め込みドキュメント: 適切な正規化を行い、関連テーブルに切り出す

        ### ステップ4: セキュリティ設定
        1. 適切な権限（読み取り専用、読み書き、管理者）を持つPostgreSQLロールとユーザーを作成する
        2. GRANT/REVOKEを使用して、ロールごとにテーブルおよびカラムへのアクセスを制限する
        3. アプリケーションに行レベルのアクセス制御が必要な場合、対象テーブルに行レベルセキュリティ（RLS）ポリシーを有効化する

        ### ステップ5: データベース関数とトリガー
        1. レコードの自動作成（監査タイムスタンプ、デフォルト値など）のためのユーザー管理トリガーを作成する
        2. 必要に応じてアプリケーションレベルのバリデーションを置き換えるビジネスロジック関数をPL/pgSQLで実装する

        ## フェーズ2: バックエンド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. フロントエンドサービス向けに一貫したエラーレスポンス形式を維持する

        ## フェーズ3: フロントエンドサービス層の更新

        ### ステップ1: 認証サービスの移行
        1. 新しいバックエンド認証エンドポイントと連携するようにユーザー認証サービスを更新する
        2. 認証方式が変更された場合、ログイン/ログアウトフローとトークン管理を適宜更新する
        3. PostgreSQLからの新しいユーザーデータ形式に対応するようにユーザーオブジェクトの生成処理を修正する

        ### ステップ2: HTTPクライアントの更新
        1. バックエンド構造が変更された場合、APIエンドポイントURLを更新する
        2. データ形式が変更された場合（例：_idがidになるなど）、リクエスト/レスポンスの処理を調整する
        3. コンポーネントとの互換性のため、既存のサービスメソッドのシグネチャを維持する

        ### ステップ3: ルートガードとリゾルバーの更新
        1. 新しい認証システムと連携するように認証ガードを更新する
        2. データ取得パターンが変更された場合、ルートリゾルバーを修正する
        3. 既存のコンポーネントのデータフローが損なわれていないことを確認する

        ## フェーズ4: データ移行とテスト

        ### ステップ1: データのエクスポートと変換
        1. mongoexportまたはカスタムスクリプトを使用してMongoDBコレクションをエクスポートする
        2. PostgreSQLスキーマ要件に合わせてデータを変換する
        3. データ型の変換（ObjectIdからUUID、日付、埋め込みドキュメントから外部キーなど）を処理する

        ### ステップ2: データインポートの手順
        1. psql \copy、pg_restore、またはカスタム移行スクリプトを使用してデータをインポートする
        2. インポート後に外部キーのリレーションシップと制約を検証する
        3. データの整合性と完全性を確認するため、レコード数の確認とスポットチェックを実施する

        ### ステップ3: エンドツーエンドテスト
        1. 実データを使用してすべてのCRUD操作をテストする
        2. 認証フローが正常に動作することを確認する
        3. 管理者機能とロールベースのアクセス制御をテストする

        ## フェーズ5: デプロイと監視

        ### ステップ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>
