メインコンテンツにスキップ

MongoDB

このガイドでは、Prisma ORM と MongoDB の使用に関する概念について説明し、MongoDB と他のデータベースプロバイダーとの間の共通点と相違点を説明し、Prisma ORM を使用して MongoDB と統合するようにアプリケーションを構成するプロセスを順を追って説明します。

情報

Prisma ORM を MongoDB に接続するには、はじめにドキュメントを参照してください。

MongoDB とは?

MongoDB は、BSON 形式(キーと値のペアでデータを格納するために設計された JSON ライクなドキュメント形式)でデータを格納する NoSQL データベースです。ドキュメントモデルがアプリケーションコードのオブジェクトに簡単にマッピングでき、高可用性と水平方向のスケーリングが組み込まれてサポートされているため、JavaScript アプリケーション開発で一般的に使用されています。

MongoDB は、リレーショナルデータベースのテーブルで行うように、事前にスキーマを定義する必要のないコレクションにデータを格納します。各コレクションの構造も時間の経過とともに変更できます。この柔軟性により、データモデルの迅速な反復が可能になりますが、Prisma ORM を使用して MongoDB データベースを操作する場合、いくつかの違いがあることを意味します。

他のデータベースプロバイダーとの共通点

MongoDB で Prisma ORM を使用する場合の一部の側面は、リレーショナルデータベースで Prisma ORM を使用する場合と同じです。引き続き以下が可能です。

考慮すべき相違点

MongoDB のドキュメントベースの構造と柔軟なスキーマは、MongoDB で Prisma ORM を使用することが、リレーショナルデータベースで使用する場合と多くの点で異なることを意味します。これらは、注意する必要がある相違点がある領域の一部です。

  • ID の定義: MongoDB ドキュメントには、_id フィールド (ObjectID を含むことが多い) があります。Prisma ORM は _ で始まるフィールドをサポートしていないため、@map 属性を使用して Prisma ORM フィールドにマッピングする必要があります。詳細については、MongoDB での ID の定義を参照してください。

  • Prisma スキーマに一致するように既存のデータを移行する: リレーショナルデータベースでは、すべてのデータがスキーマと一致する必要があります。移行時にスキーマ内の特定のフィールドの型を変更すると、すべてのデータも一致するように更新する必要があります。対照的に、MongoDB は特定のスキーマを強制しないため、移行時には注意が必要です。詳細については、古いデータを新しいスキーマに移行する方法を参照してください。

  • イントロスペクションと Prisma ORM リレーション: 既存の MongoDB データベースをイントロスペクトすると、リレーションのないスキーマが取得され、手動で不足しているリレーションを追加する必要があります。詳細については、イントロスペクション後に不足しているリレーションを追加する方法を参照してください。

  • null および欠落フィールドのフィルタリング: MongoDB は、フィールドを null に設定することと、まったく設定しないことを区別しますが、これはリレーショナルデータベースには存在しません。Prisma ORM は現在、この区別を表現していません。つまり、null および欠落フィールドをフィルタリングするときは注意が必要です。詳細については、null および欠落フィールドをフィルタリングする方法を参照してください。

  • レプリケーションの有効化: Prisma ORM は、ネストされたクエリでの部分的な書き込みを回避するために、内部的にMongoDB トランザクションを使用します。トランザクションを使用する場合、MongoDB ではデータセットのレプリケーションを有効にする必要があります。これを行うには、レプリカセット を構成する必要があります。これは、同じデータセットを維持する MongoDB プロセスのグループです。ノードが 1 つしかないレプリカセットを作成することで、単一のデータベースを使用することもできます。MongoDB のAtlas ホスティングサービスを使用する場合、レプリカセットは構成済みですが、MongoDB をローカルで実行している場合は、自分でレプリカセットをセットアップする必要があります。詳細については、MongoDB のレプリカセットのデプロイガイドを参照してください。

大規模コレクションのパフォーマンスに関する考慮事項

問題

Prisma を介して大規模な MongoDB コレクションを操作する場合、特定の操作が遅くなり、リソースを大量に消費する可能性があります。特に、count() など、コレクション全体のスキャンを必要とする操作は、クエリ実行時間の制限に達し、データセットが大きくなるにつれてパフォーマンスに大きな影響を与える可能性があります。

解決策

大規模な MongoDB コレクションのパフォーマンスの問題に対処するには、次のアプローチを検討してください。

  1. 大規模なコレクションの場合は、count() の代わりに MongoDB の estimatedDocumentCount() を使用することを検討してください。このメソッドは、コレクションに関するメタデータを使用するため、はるかに高速です。Prisma の runCommandRaw メソッドを使用して、このコマンドを実行できます。

  2. 頻繁にアクセスされるカウントの場合は、カウンターキャッシュの実装を検討してください。これには、ドキュメントが追加または削除されるたびに更新する、事前に計算されたカウントを含む別のドキュメントを維持することが含まれます。

Prisma ORM を MongoDB で使用する方法

このセクションでは、MongoDB に固有の手順が必要なタスクを実行する方法について説明します。

Prisma スキーマに一致するように既存のデータを移行する方法

データベースを時間の経過とともに移行することは、開発サイクルにおいて重要な部分です。開発中に、Prisma スキーマを更新し(たとえば、新しいフィールドを追加するため)、開発環境のデータベース内のデータを更新し、最終的に更新されたスキーマと新しいデータを本番データベースにプッシュする必要があります。

情報

MongoDB を使用する場合、スキーマとデータベース間の「結合」は、SQL データベースよりも柔軟になるように意図的に設計されていることに注意してください。MongoDB はスキーマを強制しないため、データ整合性を検証する必要があります。

スキーマとデータベースを更新するこれらの反復タスクにより、スキーマとデータベース内の実際のデータとの間に不整合が発生する可能性があります。これが起こりうるシナリオの 1 つを見て、これらの不整合を処理するためにあなたとあなたのチームが検討すべきいくつかの戦略を検討してみましょう。

シナリオ: ユーザーの電話番号とメールアドレスを含める必要があります。現在、schema.prisma ファイルに次の User モデルがあります。

prisma/schema.prisma
model User {
id String @id @default(auto()) @map("_id") @db.ObjectId
email String
}

このスキーマを移行するために使用できる戦略はいくつかあります。

  • 「オンデマンド」更新: この戦略では、あなたとあなたのチームは、必要に応じてスキーマを更新できることに同意しました。ただし、データとスキーマ間の不整合による移行の失敗を回避するために、チーム内で、追加された新しいフィールドはすべて明示的にオプションとして定義されるという合意があります。

    上記のシナリオでは、Prisma スキーマの User モデルにオプションの phoneNumber フィールドを追加できます。

    prisma/schema.prisma
    model User {
    id String @id @default(auto()) @map("_id") @db.ObjectId
    email String
    phoneNumber String?
    }

    次に、npx prisma generate コマンドを使用して Prisma Client を再生成します。

    次に、新しいフィールドを反映するようにアプリケーションを更新し、アプリを再デプロイします。

    phoneNumber フィールドはオプションであるため、電話番号が定義されていない古いユーザーをクエリできます。データベース内のレコードは、アプリケーションのユーザーが新しいフィールドに電話番号を入力し始めると、「オンデマンド」で更新されます。

    別のオプションは、必須フィールドにデフォルト値を追加することです。例:

    prisma/schema.prisma
    model User {
    id String @id @default(auto()) @map("_id") @db.ObjectId
    email String
    phoneNumber String @default("000-000-0000")
    }

    次に、phoneNumber が欠落している場合、値は 000-000-0000 に強制変換されます。

  • 「非破壊的変更」更新: この戦略は最初の戦略に基づいており、チーム内で、フィールドの名前変更や削除は行わず、新しいフィールドのみを追加し、常に新しいフィールドをオプションとして定義するというさらなる合意があります。このポリシーは、スキーマへの後方互換性のない変更がないことを検証するために、CI/CD プロセスにチェックを追加することで強化できます。

  • 「オールアットワンス」更新: この戦略は、リレーショナルデータベースの従来の移行に似ており、すべてのデータが新しいスキーマを反映するように更新されます。上記のシナリオでは、データベース内のすべての既存のユーザーに電話番号フィールドの値を追加するスクリプトを作成します。スキーマとデータが一致しているため、フィールドをアプリケーションの必須フィールドにすることができます。

イントロスペクション後に不足しているリレーションを追加する方法

既存の MongoDB データベースをイントロスペクトした後、モデル間のリレーションを手動で追加する必要があります。MongoDB には、リレーショナルデータベースの場合のように、外部キーを介してリレーションを定義するという概念はありません。ただし、MongoDB に「外部キーのような」フィールドがあり、別のコレクションの ID フィールドと一致するコレクションがある場合、Prisma ORM を使用すると、コレクション間のリレーションをエミュレートできます。

例として、UserPost の 2 つのコレクションを持つ MongoDB データベースを取り上げます。これらのコレクションのデータ形式は次のとおりで、userId フィールドがユーザーを投稿にリンクしています。

User コレクション

  • objectId 型の _id フィールド
  • string 型の email フィールド

Post コレクション

  • objectId 型の _id フィールド
  • string 型の title フィールド
  • objectID 型の userId

db pull を使用したイントロスペクションでは、これは Prisma スキーマに次のようにプルされます。

prisma/schema.prisma
model Post {
id String @id @default(auto()) @map("_id") @db.ObjectId
title String
userId String @db.ObjectId
}

model User {
id String @id @default(auto()) @map("_id") @db.ObjectId
email String
}

これは、User モデルと Post モデル間のリレーションが欠落しています。これを修正するには、Post モデルに user フィールドを手動で追加し、userIdfields 値として使用して @relation 属性を追加し、User モデルにリンクし、User モデルにバックリレーションとして posts フィールドを追加します。

prisma/schema.prisma
model Post {
id String @id @default(auto()) @map("_id") @db.ObjectId
title String
userId String @db.ObjectId
user User @relation(fields: [userId], references: [id])
}

model User {
id String @id @default(auto()) @map("_id") @db.ObjectId
email String
posts Post[]
}

Prisma ORM でのリレーションの使用方法の詳細については、ドキュメントを参照してください。

null および欠落フィールドをフィルタリングする方法

MongoDB が null フィールドと欠落フィールドを区別する方法を理解するために、オプションの name フィールドを持つ User モデルの例を考えてみましょう。

model User {
id String @id @default(auto()) @map("_id") @db.ObjectId
email String
name String?
}

まず、name フィールドを明示的に null に設定してレコードを作成してみます。Prisma ORM は、期待どおりに name: null を返します。

const createNull = await prisma.user.create({
data: {
email: 'user1@prisma.io',
name: null,
},
})
console.log(createNull)
表示CLI結果
{
id: '6242c4ae032bc76da250b207',
email: 'user1@prisma.io',
name: null
}

MongoDB データベースを直接確認すると、namenull に設定された新しいレコードも表示されます。

{
"_id": "6242c4af032bc76da250b207",
"email": "user1@prisma.io",
"name": null
}

次に、name フィールドを明示的に設定せずにレコードを作成してみます。

const createMissing = await prisma.user.create({
data: {
email: 'user2@prisma.io',
},
})
console.log(createMissing)
表示CLI結果
{
id: '6242c4ae032bc76da250b208',
email: 'user2@prisma.io',
name: null
}

Prisma ORM は引き続き name: null を返しますが、データベースを直接見ると、レコードには name フィールドがまったく定義されていないことがわかります。

{
"_id": "6242c4af032bc76da250b208",
"email": "user2@prisma.io"
}

Prisma ORM は両方のケースで同じ結果を返します。これは、現在、基になるデータベースで null であるフィールドと、まったく定義されていないフィールドとの間の MongoDB のこの違いを指定する方法がないためです。詳細については、この Github issueを参照してください。

これは、現在、null および欠落フィールドをフィルタリングするときは注意が必要であることを意味します。name: null でレコードをフィルタリングすると、name が明示的に null に設定された最初のレコードのみが返されます。

const findNulls = await prisma.user.findMany({
where: {
name: null,
},
})
console.log(findNulls)
表示CLI結果
[
{
id: '6242c4ae032bc76da250b207',
email: 'user1@prisma.io',
name: null
}
]

これは、name: null が等価性をチェックしているためであり、存在しないフィールドは null と等しくないためです。

欠落フィールドも含むには、isSet フィルターを使用して、null または設定されていないフィールドを明示的に検索します。これにより、両方のレコードが返されます。

const findNullOrMissing = await prisma.user.findMany({
where: {
OR: [
{
name: null,
},
{
name: {
isSet: false,
},
},
],
},
})
console.log(findNullOrMissing)
表示CLI結果
[
{
id: '6242c4ae032bc76da250b207',
email: 'user1@prisma.io',
name: null
},
{
id: '6242c4ae032bc76da250b208',
email: 'user2@prisma.io',
name: null
}
]

Prisma ORM での MongoDB の使用に関する詳細

Prisma ORM で MongoDB の使用を開始する最も速い方法は、はじめにドキュメントを参照することです。

これらのチュートリアルでは、MongoDB への接続、スキーマ変更のプッシュ、および Prisma Client の使用のプロセスについて説明します。

詳細なリファレンス情報は、MongoDB コネクタドキュメントで入手できます。

MongoDB データベースのセットアップと管理方法の詳細については、Prisma Data Guideを参照してください。

MongoDB サーバーに接続するには、datasource ブロックを Prisma スキーマで構成します。

schema.prisma
datasource db {
provider = "mongodb"
url = env("DATABASE_URL")
}

datasource ブロックに渡されるフィールドは次のとおりです。

警告

MongoDB データベースコネクタは、ネストされた書き込みをサポートするためにトランザクションを使用します。トランザクションには、レプリカセット デプロイメントが必要です。レプリカセットをデプロイする最も簡単な方法は、Atlas を使用することです。無料で開始できます。

接続の詳細

接続 URL

MongoDB 接続 URL は、データベースのホスト方法に応じてさまざまな方法で構成できます。標準構成は、次のコンポーネントで構成されています。

Structure of the MongoDB connection URL

ベース URL とパス

接続 URL のベース URL およびパスセクションは、認証資格情報、ホスト(およびオプションでポート番号)、およびデータベースで構成されています。

mongodb://USERNAME:PASSWORD@HOST/DATABASE

次のコンポーネントがデータベースのベース URL を構成しています。

名前プレースホルダー説明
ユーザーUSERNAMEデータベースユーザーの名前 (例: janedoe)
パスワードPASSWORDデータベースユーザーのパスワード
ホストHOSTmongod インスタンスが実行されているホスト。シャーディングされたクラスターを実行している場合、これは mongos インスタンスになります。これは、ホスト名、IP アドレス、または UNIX ドメインソケットにすることができます。
ポートPORTデータベースサーバーが実行されているポート (例: 1234)。指定しない場合、デフォルトの 27017 が使用されます。
データベースDATABASE使用するデータベースの名前。指定されていないが、authSource オプションが設定されている場合は、authSource データベース名が使用されます。接続文字列のデータベースも authSource オプションも指定されていない場合、デフォルトは admin になります。
情報

特殊文字をパーセントエンコードする必要があります。

引数

接続 URL は引数も取ることができます。次の例では、3 つの引数を設定しています。

  • ssl 接続
  • connectTimeoutMS
  • および maxPoolSize
mongodb://USERNAME:PASSWORD@HOST/DATABASE?ssl=true&connectTimeoutMS=5000&maxPoolSize=50

接続文字列引数の完全なリストについては、MongoDB 接続文字列ドキュメントを参照してください。Prisma ORM 固有の引数はありません。

ObjectId の使用

MongoDB ドキュメントの _id フィールドに ObjectId を含めるのが一般的な方法です。

{
"_id": { "$oid": "60d599cb001ef98000f2cad2" },
"createdAt": { "$date": { "$numberLong": "1624611275577" } },
"email": "ella@prisma.io",
"name": "Ella",
"role": "ADMIN"
}

基になるデータベースの ObjectId にマッピングされるフィールド (最も一般的なのは ID およびリレーションスカラーフィールド)

  • 型は String または Bytes である必要があります
  • @db.ObjectId 属性を含める必要があります
  • ドキュメント作成時に有効な ObjectId を自動生成するには、オプションで @default(auto()) を使用できます

String を使用する例を次に示します。

model User {
id String @id @default(auto()) @map("_id") @db.ObjectId
// Other fields
}

Bytes を使用する別の例を次に示します。

model User {
id Bytes @id @default(auto()) @map("_id") @db.ObjectId
// Other fields
}

参照: MongoDB での ID フィールドの定義

ObjectId の生成

アプリケーションで有効な ObjectId を生成するには (テスト目的または ID フィールド値を手動で設定するため)、bson パッケージを使用します。

npm install --save bson
import { ObjectId } from 'bson'

const id = new ObjectId()

リレーショナルデータベースのコネクタとの相違点

このセクションでは、MongoDB コネクタがリレーショナルデータベース用の Prisma ORM コネクタと異なる点について説明します。

Prisma Migrate の非サポート

現在、MongoDB プロジェクトは変更を余分なツールで管理する必要がある内部スキーマに依存していないため、Prisma Migrate のサポートを追加する計画はありません。@unique インデックスの管理は db push を介して実現されます。

@@id および autoincrement() の非サポート

@@id 属性 (複数のフィールドの ID) は、MongoDB の主キーは常にモデルの _id フィールドにあるため、サポートされていません。

autoincrement() 関数 (インクリメントされる @id 値を作成します) は、autoincrement() が MongoDB の _id フィールドにある ObjectID 型では機能しないため、サポートされていません。

循環参照と参照アクション

モデルに循環参照がある場合 (自己リレーションまたはモデル間のリレーションのサイクルから)、参照アクションを使用する場合は、アクションの無限ループを防ぐために NoAction の参照アクションを設定する必要があります。

詳細については、参照アクションの特別なルールを参照してください。

レプリカセット構成

MongoDB では、レプリカセットでのみトランザクションを開始できます。Prisma ORM は、ネストされたクエリでの部分的な書き込みを回避するために、内部的にトランザクションを使用します。これは、レプリカセット構成が必要になるという要件を継承することを意味します。

レプリカセットが構成されていないデプロイメントで Prisma ORM の MongoDB コネクタを使用しようとすると、Prisma ORM はメッセージ Error: Transactions are not supported by this deployment を表示します。エラーメッセージの全文は次のとおりです。

PrismaClientUnknownRequestError2 [PrismaClientUnknownRequestError]:
Invalid `prisma.post.create()` invocation in
/index.ts:9:21

6 await prisma.$connect()
7
8 // Create the first post
→ 9 await prisma.post.create(
Error in connector: Database error. error code: unknown, error message: Transactions are not supported by this deployment
at cb (/node_modules/@prisma/client/runtime/index.js:34804:17)
at processTicksAndRejections (internal/process/task_queues.js:97:5) {
clientVersion: '3.xx.0'
}

これを解決するには、デプロイメントをレプリカセットが構成されたデプロイメントに変更することをお勧めします。

これを行う簡単な方法の 1 つは、MongoDB Atlas を使用して、すぐにレプリカセットサポートを備えた無料インスタンスを起動することです。

ローカルでレプリカセットを実行するオプションもあります: https://www.mongodb.com/docs/manual/tutorial/convert-standalone-to-replica-set

MongoDB と Prisma スキーマ間の型マッピング

MongoDB コネクタは、Prisma ORM データモデルスカラー型を、次のように MongoDB のネイティブ列型にマッピングします。

または、Prisma 型で整理された型マッピングについては、Prisma スキーマ参照を参照してください。

Prisma ORM から MongoDB へのネイティブ型マッピング

Prisma ORMMongoDB
Stringstring
Booleanbool
Intint
BigIntlong
Floatdouble
Decimal現在サポートされていません
DateTimetimestamp
BytesbinData
Json

現在サポートされていない MongoDB 型

  • Decimal128
  • Undefined
  • DBPointer
  • Null
  • Symbol
  • MinKey
  • MaxKey
  • オブジェクト
  • Javascript
  • JavascriptWithScope
  • 正規表現

イントロスペクション時の MongoDB から Prisma ORM 型へのマッピング

MongoDB データベースをイントロスペクションする場合、Prisma ORM は関連するスカラー型を使用します。 一部の特殊な型には、ネイティブ型の追加アノテーションも付与されます。

MongoDB (型 | エイリアス)Prisma ORMサポートネイティブデータベース型属性注記
objectIdString✔️@db.ObjectId

イントロスペクションは、Unsupportedフィールドとしてまだサポートされていないネイティブデータベース型を追加します。

schema.prisma
model Example {
id String @id @default(auto()) @map("_id") @db.ObjectId
name String
regex Unsupported("RegularExpression")
}