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

複合型

警告

複合型はMongoDBでのみ利用可能です。

複合型は、MongoDBでは埋め込みドキュメントとして知られており、レコード内にレコードを埋め込むことができます。

複合型はv3.12.0で一般提供となりました。以前はv3.10.0からプレビューで利用可能でした。

このページでは、以下の方法について説明します。

  • findFirst および findMany を使用して複合型を含むレコードを検索する
  • create および createMany を使用して複合型を持つ新しいレコードを作成する
  • update および updateMany を使用して既存のレコード内で複合型を更新する
  • delete および deleteMany を使用して複合型を持つレコードを削除する

スキーマ例

以降の例では、このスキーマを使用します。

schema.prisma
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で複合型を使用する際には、現在いくつかの制限事項があります。

複合型の必須フィールドのデフォルト値

バージョン4.0.0以降では、次の条件がすべて満たされている場合に複合型でデータベース読み取りを実行すると、Prisma Clientはデフォルト値を結果に挿入します。

条件

  • 複合型のフィールドが必須であり、
  • このフィールドにデフォルト値があり、
  • このフィールドが返されたドキュメントに存在しない場合。

注記

  • これは、モデルフィールドと同じ動作です。
  • 読み取り操作では、Prisma Clientはデフォルト値を結果に挿入しますが、データベースにデフォルト値を挿入しません。

スキーマ例では、photoに必須フィールドを追加すると仮定します。このフィールドbitDepthにはデフォルト値があります。

schema.prisma
...
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つの複合型のフィルタリング

isequalsisNot、および 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,
},
},
})

多数の複合型のフィルタリング

equalsisEmptyeverysome、および none 操作を使用して、複数の複合型をフィルタリングします。

  • equals: リストの完全一致をチェックします
  • isEmpty: リストが空かどうかをチェックします
  • every: リスト内のすべての項目が条件に一致する必要があります
  • some: リスト内の1つ以上の項目が条件に一致する必要があります
  • none: リスト内のどの項目も条件に一致してはいけません
  • isSet : オプションのフィールドをフィルタリングして、設定されている結果のみを含めます(値が設定されているか、明示的に null に設定されているかのいずれか)。このフィルターを true に設定すると、まったく設定されていない undefined の結果は除外されます。

たとえば、equals を使用して特定の写真リストを持つ製品を見つけることができます(すべての urlheight、および 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 メソッド内で設定、更新、または削除できます。次のセクションでは、単一の型または複数の型を一度に更新するために利用可能な操作について説明し、それぞれの例を示します。

単一の複合型の変更

setunsetupdate、および 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',
},
},
},
})

複数の複合型の変更

setpushupdateMany、および 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 を使用して、url1.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.ioaddresses に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はレコード内のユニーク値を強制します。MailboxAddress モデルの間にはリレーションがあります。また、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つのアドレスを追加することは許可されません。