複合型
複合型はMongoDBでのみ利用可能です。
複合型は、MongoDBでは埋め込みドキュメントとして知られており、レコード内にレコードを埋め込むことができます。
複合型はv3.12.0で一般提供となりました。以前はv3.10.0からプレビューで利用可能でした。
このページでは、以下の方法について説明します。
- findFirst および
findMany
を使用して複合型を含むレコードを検索する - create および
createMany
を使用して複合型を持つ新しいレコードを作成する - update および
updateMany
を使用して既存のレコード内で複合型を更新する - delete および
deleteMany
を使用して複合型を持つレコードを削除する
スキーマ例
以降の例では、このスキーマを使用します。
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "mongodb"
url = env("DATABASE_URL")
}
model Product {
id String @id @default(auto()) @map("_id") @db.ObjectId
name String @unique
price Float
colors Color[]
sizes Size[]
photos Photo[]
orders Order[]
}
model Order {
id String @id @default(auto()) @map("_id") @db.ObjectId
product Product @relation(fields: [productId], references: [id])
color Color
size Size
shippingAddress Address
billingAddress Address?
productId String @db.ObjectId
}
enum Color {
Red
Green
Blue
}
enum Size {
Small
Medium
Large
XLarge
}
type Photo {
height Int @default(200)
width Int @default(100)
url String
}
type Address {
street String
city String
zip String
}
このスキーマでは、Product
モデルはPhoto[]
複合型を持ち、Order
モデルは2つの複合Address
型を持ちます。shippingAddress
は必須ですが、billingAddress
はオプションです。
複合型を使用する際の考慮事項
Prisma Clientで複合型を使用する際には、現在いくつかの制限事項があります。
findUnique()
は複合型でフィルタリングできませんaggregate
、groupBy()
、count
は複合操作をサポートしていません
複合型の必須フィールドのデフォルト値
バージョン4.0.0以降では、次の条件がすべて満たされている場合に複合型でデータベース読み取りを実行すると、Prisma Clientはデフォルト値を結果に挿入します。
条件
注記
- これは、モデルフィールドと同じ動作です。
- 読み取り操作では、Prisma Clientはデフォルト値を結果に挿入しますが、データベースにデフォルト値を挿入しません。
スキーマ例では、photo
に必須フィールドを追加すると仮定します。このフィールドbitDepth
にはデフォルト値があります。
...
type Photo {
...
bitDepth Int @default(8)
}
...
次に、npx prisma db push
を実行してデータベースを更新し、npx prisma generate
でPrisma Clientを再生成すると仮定します。次に、次のアプリケーションコードを実行します。
console.dir(await prisma.product.findMany({}), { depth: Infinity })
bitDepth
フィールドにはコンテンツがありません。これは、このフィールドを追加したばかりであるため、クエリはデフォルト値の8
を返します。
** 以前のバージョン **
バージョン4.0.0より前では、Prisma ORMは次のようなP2032エラーをスローしていました。
Error converting field "bitDepth" of expected non-nullable
type "int", found incompatible value of "null".
find
および findMany
を使用して複合型を含むレコードを検索する
レコードは、where
操作内で複合型によってフィルタリングできます。
次のセクションでは、単一の型または複数の型でフィルタリングするために利用可能な操作について説明し、それぞれの例を示します。
1つの複合型のフィルタリング
is
、equals
、isNot
、および isSet
操作を使用して、単一の複合型を変更します。
is
: 一致する複合型で結果をフィルタリングします。1つ以上のフィールドが存在する必要があります(例:配送先住所の番地名で注文をフィルタリングする)equals
: 一致する複合型で結果をフィルタリングします。すべてのフィールドが存在する必要があります。(例:配送先住所全体で注文をフィルタリングする)isNot
: 一致しない複合型で結果をフィルタリングしますisSet
: オプションのフィールドをフィルタリングして、設定されている結果のみを含めます(値が設定されているか、明示的にnull
に設定されているかのいずれか)。このフィルターをtrue
に設定すると、まったく設定されていないundefined
の結果は除外されます。
たとえば、is
を使用して番地名が '555 Candy Cane Lane'
の注文をフィルタリングします。
const orders = await prisma.order.findMany({
where: {
shippingAddress: {
is: {
street: '555 Candy Cane Lane',
},
},
},
})
equals
を使用して、配送先住所のすべてのフィールドが一致する注文をフィルタリングします。
const orders = await prisma.order.findMany({
where: {
shippingAddress: {
equals: {
street: '555 Candy Cane Lane',
city: 'Wonderland',
zip: '52337',
},
},
},
})
このクエリの短縮表記を使用することもできます。その場合、equals
を省略します。
const orders = await prisma.order.findMany({
where: {
shippingAddress: {
street: '555 Candy Cane Lane',
city: 'Wonderland',
zip: '52337',
},
},
})
isNot
を使用して、zip
コードが '52337'
でない注文をフィルタリングします。
const orders = await prisma.order.findMany({
where: {
shippingAddress: {
isNot: {
zip: '52337',
},
},
},
})
isSet
を使用して、オプションの billingAddress
が設定されている注文(値または null
のいずれかに設定)をフィルタリングします。
const orders = await prisma.order.findMany({
where: {
billingAddress: {
isSet: true,
},
},
})
多数の複合型のフィルタリング
equals
、isEmpty
、every
、some
、および none
操作を使用して、複数の複合型をフィルタリングします。
equals
: リストの完全一致をチェックしますisEmpty
: リストが空かどうかをチェックしますevery
: リスト内のすべての項目が条件に一致する必要がありますsome
: リスト内の1つ以上の項目が条件に一致する必要がありますnone
: リスト内のどの項目も条件に一致してはいけませんisSet
: オプションのフィールドをフィルタリングして、設定されている結果のみを含めます(値が設定されているか、明示的にnull
に設定されているかのいずれか)。このフィルターをtrue
に設定すると、まったく設定されていないundefined
の結果は除外されます。
たとえば、equals
を使用して特定の写真リストを持つ製品を見つけることができます(すべての url
、height
、および width
フィールドが一致する必要があります)。
const product = prisma.product.findMany({
where: {
photos: {
equals: [
{
url: '1.jpg',
height: 200,
width: 100,
},
{
url: '2.jpg',
height: 200,
width: 100,
},
],
},
},
})
このクエリの短縮表記を使用することもできます。その場合、equals
を省略し、フィルタリングするフィールドのみを指定します。
const product = prisma.product.findMany({
where: {
photos: [
{
url: '1.jpg',
height: 200,
width: 100,
},
{
url: '2.jpg',
height: 200,
width: 100,
},
],
},
})
isEmpty
を使用して、写真がない製品をフィルタリングします。
const product = prisma.product.findMany({
where: {
photos: {
isEmpty: true,
},
},
})
some
を使用して、1つ以上の写真の url
が "2.jpg"
である製品をフィルタリングします。
const product = prisma.product.findFirst({
where: {
photos: {
some: {
url: '2.jpg',
},
},
},
})
none
を使用して、写真の url
が "2.jpg"
である写真がない製品をフィルタリングします。
const product = prisma.product.findFirst({
where: {
photos: {
none: {
url: '2.jpg',
},
},
},
})
create
および createMany
を使用して複合型を持つレコードを作成する
ユニーク制約を持つ複合型を持つレコードを作成する場合、MongoDBはレコード内のユニーク値を強制しないことに注意してください。詳細はこちら。
複合型は、set
操作を使用して、create
または createMany
メソッド内で作成できます。たとえば、create
内で set
を使用して、Order
内に Address
複合型を作成できます。
const order = await prisma.order.create({
data: {
// Normal relation
product: { connect: { id: 'some-object-id' } },
color: 'Red',
size: 'Large',
// Composite type
shippingAddress: {
set: {
street: '1084 Candycane Lane',
city: 'Silverlake',
zip: '84323',
},
},
},
})
set
を省略して、作成するフィールドのみを指定する短縮表記を使用することもできます。
const order = await prisma.order.create({
data: {
// Normal relation
product: { connect: { id: 'some-object-id' } },
color: 'Red',
size: 'Large',
// Composite type
shippingAddress: {
street: '1084 Candycane Lane',
city: 'Silverlake',
zip: '84323',
},
},
})
オプションの型(billingAddress
など)の場合、値を null
に設定することもできます。
const order = await prisma.order.create({
data: {
// Normal relation
product: { connect: { id: 'some-object-id' } },
color: 'Red',
size: 'Large',
// Composite type
shippingAddress: {
street: '1084 Candycane Lane',
city: 'Silverlake',
zip: '84323',
},
// Embedded optional type, set to null
billingAddress: {
set: null,
},
},
})
product
に複数の photos
のリストが含まれるケースをモデル化するには、一度に複数の複合型を set
できます。
const product = await prisma.product.create({
data: {
name: 'Forest Runners',
price: 59.99,
colors: ['Red', 'Green'],
sizes: ['Small', 'Medium', 'Large'],
// New composite type
photos: {
set: [
{ height: 100, width: 200, url: '1.jpg' },
{ height: 100, width: 200, url: '2.jpg' },
],
},
},
})
set
を省略して、作成するフィールドのみを指定する短縮表記を使用することもできます。
const product = await prisma.product.create({
data: {
name: 'Forest Runners',
price: 59.99,
// Scalar lists that we already support
colors: ['Red', 'Green'],
sizes: ['Small', 'Medium', 'Large'],
// New composite type
photos: [
{ height: 100, width: 200, url: '1.jpg' },
{ height: 100, width: 200, url: '2.jpg' },
],
},
})
これらの操作は、createMany
メソッド内でも機能します。たとえば、それぞれが photos
のリストを含む複数の product
を作成できます。
const product = await prisma.product.createMany({
data: [
{
name: 'Forest Runners',
price: 59.99,
colors: ['Red', 'Green'],
sizes: ['Small', 'Medium', 'Large'],
photos: [
{ height: 100, width: 200, url: '1.jpg' },
{ height: 100, width: 200, url: '2.jpg' },
],
},
{
name: 'Alpine Blazers',
price: 85.99,
colors: ['Blue', 'Red'],
sizes: ['Large', 'XLarge'],
photos: [
{ height: 100, width: 200, url: '1.jpg' },
{ height: 150, width: 200, url: '4.jpg' },
{ height: 200, width: 200, url: '5.jpg' },
],
},
],
})
update
および updateMany
内で複合型を変更する
ユニーク制約を持つ複合型を持つレコードを更新する場合、MongoDBはレコード内のユニーク値を強制しないことに注意してください。詳細はこちら。
複合型は、update
または updateMany
メソッド内で設定、更新、または削除できます。次のセクションでは、単一の型または複数の型を一度に更新するために利用可能な操作について説明し、それぞれの例を示します。
単一の複合型の変更
set
、unset
、update
、および upsert
操作を使用して、単一の複合型を変更します。
set
を使用して複合型を設定し、既存の値を上書きします。unset
を使用して複合型をunsetします。set: null
とは異なり、unset
はフィールドを完全に削除します。update
を使用して複合型を更新します。upsert
を使用して、既存の複合型が存在する場合はupdate
し、それ以外の場合は複合型をset
します。
たとえば、update
を使用して、Order
内の Address
複合型で必須の shippingAddress
を更新します。
const order = await prisma.order.update({
where: {
id: 'some-object-id',
},
data: {
shippingAddress: {
// Update just the zip field
update: {
zip: '41232',
},
},
},
})
オプションの埋め込み型(billingAddress
など)の場合、upsert
を使用して、新しいレコードが存在しない場合は作成し、レコードが存在する場合は更新します。
const order = await prisma.order.update({
where: {
id: 'some-object-id',
},
data: {
billingAddress: {
// Create the address if it doesn't exist,
// otherwise update it
upsert: {
set: {
street: '1084 Candycane Lane',
city: 'Silverlake',
zip: '84323',
},
update: {
zip: '84323',
},
},
},
},
})
unset
操作を使用して、オプションの埋め込み型を削除することもできます。次の例では、unset
を使用して Order
から billingAddress
を削除します。
const order = await prisma.order.update({
where: {
id: 'some-object-id',
},
data: {
billingAddress: {
// Unset the billing address
// Removes "billingAddress" field from order
unset: true,
},
},
})
フィルターを updateMany
内で使用して、複合型に一致するすべてのレコードを更新できます。次の例では、is
フィルターを使用して、注文リストの配送先住所から番地名を照合します。
const orders = await prisma.order.updateMany({
where: {
shippingAddress: {
is: {
street: '555 Candy Cane Lane',
},
},
},
data: {
shippingAddress: {
update: {
street: '111 Candy Cane Drive',
},
},
},
})
複数の複合型の変更
set
、push
、updateMany
、および deleteMany
操作を使用して、複合型のリストを変更します。
set
: 埋め込み複合型リストを設定し、既存のリストを上書きします。push
: 埋め込み複合型リストの末尾に値を追加します。updateMany
: 多数の複合型を一度に更新します。deleteMany
: 多数の複合型を一度に削除します。
たとえば、push
を使用して、新しい写真を photos
リストに追加します。
const product = prisma.product.update({
where: {
id: '62de6d328a65d8fffdae2c18',
},
data: {
photos: {
// Push a photo to the end of the photos list
push: [{ height: 100, width: 200, url: '1.jpg' }],
},
},
})
updateMany
を使用して、url
が 1.jpg
または 2.png
の写真を更新します。
const product = prisma.product.update({
where: {
id: '62de6d328a65d8fffdae2c18',
},
data: {
photos: {
updateMany: {
where: {
url: '1.jpg',
},
data: {
url: '2.png',
},
},
},
},
})
次の例では、deleteMany
を使用して、height
が 100 のすべての写真を削除します。
const product = prisma.product.update({
where: {
id: '62de6d328a65d8fffdae2c18',
},
data: {
photos: {
deleteMany: {
where: {
height: 100,
},
},
},
},
})
upsert
による複合型のUpsert
ユニーク制約を持つ複合型の値を作成または更新する場合、MongoDBはレコード内のユニーク値を強制しないことに注意してください。詳細はこちら。
複合型を作成または更新するには、upsert
メソッドを使用します。上記の create
および update
メソッドと同じ複合操作を使用できます。
たとえば、upsert
を使用して、新しい製品を作成するか、既存の製品に写真を追加します。
const product = await prisma.product.upsert({
where: {
name: 'Forest Runners',
},
create: {
name: 'Forest Runners',
price: 59.99,
colors: ['Red', 'Green'],
sizes: ['Small', 'Medium', 'Large'],
photos: [
{ height: 100, width: 200, url: '1.jpg' },
{ height: 100, width: 200, url: '2.jpg' },
],
},
update: {
photos: {
push: { height: 300, width: 400, url: '3.jpg' },
},
},
})
delete
および deleteMany
を使用して複合型を含むレコードを削除する
複合型を埋め込むレコードを削除するには、delete
または deleteMany
メソッドを使用します。これにより、埋め込まれた複合型も削除されます。
たとえば、deleteMany
を使用して、size
が "Small"
のすべての製品を削除します。これにより、埋め込まれた photos
もすべて削除されます。
const deleteProduct = await prisma.product.deleteMany({
where: {
sizes: {
equals: 'Small',
},
},
})
フィルターを使用して、複合型に一致するレコードを削除することもできます。以下の例では、some
フィルターを使用して、特定の写真を含む製品を削除します。
const product = await prisma.product.deleteMany({
where: {
photos: {
some: {
url: '2.jpg',
},
},
},
})
複合型の順序付け
orderBy
操作を使用して、結果を昇順または降順で並べ替えることができます。
たとえば、次のコマンドは、すべての注文を検索し、配送先住所の都市名で昇順に並べ替えます。
const orders = await prisma.order.findMany({
orderBy: {
shippingAddress: {
city: 'asc',
},
},
})
複合型のユニークフィールド内の重複値
ユニーク制約を持つ複合型を持つレコードに対して、次の操作のいずれかを実行する場合は注意してください。この状況では、MongoDBはレコード内のユニーク値を強制しません。
- レコードを作成するとき
- レコードにデータを追加するとき
- レコード内のデータを更新するとき
スキーマに @@unique
制約を持つ複合型がある場合、MongoDBは、この複合型を含む2つ以上のレコードで、制約された値に同じ値を格納することを防ぎます。ただし、MongoDBは、単一のレコードに同じフィールド値の複数のコピーを格納することを防ぎません。
Prisma ORMリレーションを使用して、この問題を回避できます。
たとえば、次のスキーマでは、MailBox
には複合型 addresses
があり、email
フィールドに @@unique
制約があります。
type Address {
email String
}
model MailBox {
name String
addresses Address[]
@@unique([addresses.email])
}
次のコードは、address
に2つの同一の値を持つレコードを作成します。MongoDBはこの状況でエラーをスローせず、alice@prisma.io
を addresses
に2回格納します。
await prisma.MailBox.createMany({
data: [
{
name: 'Alice',
addresses: {
set: [
{
address: 'alice@prisma.io', // Not unique
},
{
address: 'alice@prisma.io', // Not unique
},
],
},
},
],
})
注: MongoDBは、2つの別々のレコードに同じ値を格納しようとするとエラーをスローします。上記の例では、ユーザーアリスとユーザーボブに対してメールアドレス alice@prisma.io
を格納しようとすると、MongoDBはデータを格納せず、エラーをスローします。
Prisma ORMリレーションを使用してレコード内のユニーク値を強制する
上記の例では、MongoDBはネストされたアドレス名のユニーク制約を強制しませんでした。ただし、レコード内のユニーク値を強制するために、データのモデル化方法を変更できます。これを行うには、Prisma ORM リレーションを使用して、複合型をコレクションに変えます。このコレクションへのリレーションシップを設定し、ユニークにしたいフィールドにユニーク制約を配置します。
次の例では、MongoDBはレコード内のユニーク値を強制します。Mailbox
と Address
モデルの間にはリレーションがあります。また、Address
モデルの name
フィールドにはユニーク制約があります。
model Address {
id String @id @default(auto()) @map("_id") @db.ObjectId
name String
mailbox Mailbox? @relation(fields: [mailboxId], references: [id])
mailboxId String? @db.ObjectId
@@unique([name])
}
model Mailbox {
id String @id @default(auto()) @map("_id") @db.ObjectId
name String
addresses Address[] @relation
}
await prisma.MailBox.create({
data: {
name: 'Alice',
addresses: {
create: [
{ name: 'alice@prisma.io' }, // Not unique
{ name: 'alice@prisma.io' }, // Not unique
],
},
},
})
上記のコードを実行すると、MongoDBはユニーク制約を強制します。アプリケーションが名前 alice@prisma.io
を持つ2つのアドレスを追加することは許可されません。