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 を使用する場合と同じです。引き続き以下が可能です。
- Prisma スキーマ言語でデータベースをモデル化する
mongodb
データベースコネクタを使用してデータベースに接続する- 既存の MongoDB データベースが既にある場合は、既存のプロジェクトにイントロスペクションを使用する
db push
を使用して、スキーマの変更をデータベースにプッシュする- Prisma スキーマに基づいて型安全な方法でデータベースをクエリするために、アプリケーションでPrisma Clientを使用する
考慮すべき相違点
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 コレクションのパフォーマンスの問題に対処するには、次のアプローチを検討してください。
-
大規模なコレクションの場合は、
count()
の代わりに MongoDB のestimatedDocumentCount()
を使用することを検討してください。このメソッドは、コレクションに関するメタデータを使用するため、はるかに高速です。Prisma のrunCommandRaw
メソッドを使用して、このコマンドを実行できます。 -
頻繁にアクセスされるカウントの場合は、カウンターキャッシュの実装を検討してください。これには、ドキュメントが追加または削除されるたびに更新する、事前に計算されたカウントを含む別のドキュメントを維持することが含まれます。
Prisma ORM を MongoDB で使用する方法
このセクションでは、MongoDB に固有の手順が必要なタスクを実行する方法について説明します。
Prisma スキーマに一致するように既存のデータを移行する方法
データベースを時間の経過とともに移行することは、開発サイクルにおいて重要な部分です。開発中に、Prisma スキーマを更新し(たとえば、新しいフィールドを追加するため)、開発環境のデータベース内のデータを更新し、最終的に更新されたスキーマと新しいデータを本番データベースにプッシュする必要があります。
MongoDB を使用する場合、スキーマとデータベース間の「結合」は、SQL データベースよりも柔軟になるように意図的に設計されていることに注意してください。MongoDB はスキーマを強制しないため、データ整合性を検証する必要があります。
スキーマとデータベースを更新するこれらの反復タスクにより、スキーマとデータベース内の実際のデータとの間に不整合が発生する可能性があります。これが起こりうるシナリオの 1 つを見て、これらの不整合を処理するためにあなたとあなたのチームが検討すべきいくつかの戦略を検討してみましょう。
シナリオ: ユーザーの電話番号とメールアドレスを含める必要があります。現在、schema.prisma
ファイルに次の User
モデルがあります。
model User {
id String @id @default(auto()) @map("_id") @db.ObjectId
email String
}
このスキーマを移行するために使用できる戦略はいくつかあります。
-
「オンデマンド」更新: この戦略では、あなたとあなたのチームは、必要に応じてスキーマを更新できることに同意しました。ただし、データとスキーマ間の不整合による移行の失敗を回避するために、チーム内で、追加された新しいフィールドはすべて明示的にオプションとして定義されるという合意があります。
上記のシナリオでは、Prisma スキーマの
User
モデルにオプションのphoneNumber
フィールドを追加できます。prisma/schema.prismamodel User {
id String @id @default(auto()) @map("_id") @db.ObjectId
email String
phoneNumber String?
}次に、
npx prisma generate
コマンドを使用して Prisma Client を再生成します。次に、新しいフィールドを反映するようにアプリケーションを更新し、アプリを再デプロイします。
phoneNumber
フィールドはオプションであるため、電話番号が定義されていない古いユーザーをクエリできます。データベース内のレコードは、アプリケーションのユーザーが新しいフィールドに電話番号を入力し始めると、「オンデマンド」で更新されます。別のオプションは、必須フィールドにデフォルト値を追加することです。例:
prisma/schema.prismamodel 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 を使用すると、コレクション間のリレーションをエミュレートできます。
例として、User
と Post
の 2 つのコレクションを持つ MongoDB データベースを取り上げます。これらのコレクションのデータ形式は次のとおりで、userId
フィールドがユーザーを投稿にリンクしています。
User
コレクション
objectId
型の_id
フィールドstring
型のemail
フィールド
Post
コレクション
objectId
型の_id
フィールドstring
型のtitle
フィールドobjectID
型のuserId
db pull
を使用したイントロスペクションでは、これは 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
フィールドを手動で追加し、userId
を fields
値として使用して @relation
属性を追加し、User
モデルにリンクし、User
モデルにバックリレーションとして posts
フィールドを追加します。
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)
{
id: '6242c4ae032bc76da250b207',
email: 'user1@prisma.io',
name: null
}
MongoDB データベースを直接確認すると、name
が null
に設定された新しいレコードも表示されます。
{
"_id": "6242c4af032bc76da250b207",
"email": "user1@prisma.io",
"name": null
}
次に、name
フィールドを明示的に設定せずにレコードを作成してみます。
const createMissing = await prisma.user.create({
data: {
email: 'user2@prisma.io',
},
})
console.log(createMissing)
{
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)
[
{
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)
[
{
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 スキーマで構成します。
datasource db {
provider = "mongodb"
url = env("DATABASE_URL")
}
datasource
ブロックに渡されるフィールドは次のとおりです。
provider
:mongodb
データソースコネクタを指定します。url
: MongoDB サーバーの接続 URL を指定します。この場合、環境変数が使用されます 接続 URL を提供します。
接続の詳細
接続 URL
MongoDB 接続 URL は、データベースのホスト方法に応じてさまざまな方法で構成できます。標準構成は、次のコンポーネントで構成されています。
ベース URL とパス
接続 URL のベース URL およびパスセクションは、認証資格情報、ホスト(およびオプションでポート番号)、およびデータベースで構成されています。
mongodb://USERNAME:PASSWORD@HOST/DATABASE
次のコンポーネントがデータベースのベース URL を構成しています。
名前 | プレースホルダー | 説明 |
---|---|---|
ユーザー | USERNAME | データベースユーザーの名前 (例: janedoe ) |
パスワード | PASSWORD | データベースユーザーのパスワード |
ホスト | HOST | mongod インスタンスが実行されているホスト。シャーディングされたクラスターを実行している場合、これは 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
}
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 ORM | MongoDB |
---|---|
String | string |
Boolean | bool |
Int | int |
BigInt | long |
Float | double |
Decimal | 現在サポートされていません |
DateTime | timestamp |
Bytes | binData |
Json |
現在サポートされていない MongoDB 型
Decimal128
Undefined
DBPointer
Null
Symbol
MinKey
MaxKey
オブジェクト
Javascript
JavascriptWithScope
正規表現
イントロスペクション時の MongoDB から Prisma ORM 型へのマッピング
MongoDB データベースをイントロスペクションする場合、Prisma ORM は関連するスカラー型を使用します。 一部の特殊な型には、ネイティブ型の追加アノテーションも付与されます。
MongoDB (型 | エイリアス) | Prisma ORM | サポート | ネイティブデータベース型属性 | 注記 |
---|---|---|---|---|
objectId | String | ✔️ | @db.ObjectId |
イントロスペクションは、Unsupported
フィールドとしてまだサポートされていないネイティブデータベース型を追加します。
model Example {
id String @id @default(auto()) @map("_id") @db.ObjectId
name String
regex Unsupported("RegularExpression")
}