Json フィールドの操作
Json
Prisma ORM フィールド型を使用して、基盤となるデータベース内の JSON 型を読み取り、書き込み、および基本的なフィルタリングを実行します。次の例では、User
モデルには extendedPetsData
という名前のオプションの Json
フィールドがあります。
model User {
id Int @id @default(autoincrement())
email String @unique
name String?
posts Post[]
extendedPetsData Json?
}
フィールド値の例
{
"pet1": {
"petName": "Claudine",
"petType": "House cat"
},
"pet2": {
"petName": "Sunny",
"petType": "Gerbil"
}
}
Json
フィールドは、string
や boolean
など、いくつかの追加の型をサポートしています。これらの追加の型は、JSON.parse()
でサポートされている型と一致させるために存在します。
export type JsonValue =
| string
| number
| boolean
| null
| JsonObject
| JsonArray
JSON フィールドのユースケース
データを関連モデルとして表現するのではなく、JSON としてデータを保存する理由には、次のようなものがあります。
- 一貫した構造を持たないデータを保存する必要がある
- 別のシステムからデータをインポートしていて、そのデータを Prisma モデルにマッピングしたくない
Json
フィールドの読み取り
Prisma.JsonArray
および Prisma.JsonObject
ユーティリティクラスを使用して、Json
フィールドの内容を操作できます。
const { PrismaClient, Prisma } = require('@prisma/client')
const user = await prisma.user.findFirst({
where: {
id: 9,
},
})
// Example extendedPetsData data:
// [{ name: 'Bob the dog' }, { name: 'Claudine the cat' }]
if (
user?.extendedPetsData &&
typeof user?.extendedPetsData === 'object' &&
Array.isArray(user?.extendedPetsData)
) {
const petsObject = user?.extendedPetsData as Prisma.JsonArray
const firstPet = petsObject[0]
}
こちらも参照: 高度な例: ネストされた JSON キー値を更新する
Json
フィールドへの書き込み
次の例では、JSON オブジェクトを extendedPetsData
フィールドに書き込みます。
var json = [
{ name: 'Bob the dog' },
{ name: 'Claudine the cat' },
] as Prisma.JsonArray
const createUser = await prisma.user.create({
data: {
email: 'birgitte@prisma.io',
extendedPetsData: json,
},
})
注: JavaScript オブジェクト (例:
{ extendedPetsData: "none"}
) は自動的に JSON に変換されます。
こちらも参照: 高度な例: ネストされた JSON キー値を更新する
Json
フィールドのフィルタリング (シンプル)
Json
型の行をフィルタリングできます。
正確なフィールド値でフィルタリング
次のクエリは、extendedPetsData
の値が json
変数と完全に一致するすべてのユーザーを返します。
var json = { [{ name: 'Bob the dog' }, { name: 'Claudine the cat' }] }
const getUsers = await prisma.user.findMany({
where: {
extendedPetsData: {
equals: json,
},
},
})
次のクエリは、extendedPetsData
の値が json
変数と完全に一致しないすべてのユーザーを返します。
var json = {
extendedPetsData: [{ name: 'Bob the dog' }, { name: 'Claudine the cat' }],
}
const getUsers = await prisma.user.findMany({
where: {
extendedPetsData: {
not: json,
},
},
})
Json
フィールドのフィルタリング (高度)
Json
フィールド内のデータで行をフィルタリングすることもできます。これを高度な Json
フィルタリングと呼びます。この機能は、PostgreSQL および MySQL でのみサポートされており、path
オプションの構文が異なります。
PostgreSQL は、配列内のオブジェクトキー値のフィルタリングをサポートしていません。
高度な Json
フィルタリングの可用性は、Prisma のバージョンによって異なります。
- v4.0.0 以降: 高度な
Json
フィルタリングは、一般提供 (GA) されています。 - v2.23.0 以降、v4.0.0 より前: 高度な
Json
フィルタリングは、プレビュー機能です。スキーマにpreviewFeatures = ["filterJson"]
を追加してください。詳細はこちら。 - v2.23.0 より前: 正確な
Json
フィールド値でフィルタリングできますが、このセクションで説明されている他の機能は使用できません。
データベースに依存する path
構文
以下のフィルターは、path
オプションを使用して、フィルタリングする Json
値の特定の部分を選択します。そのフィルタリングの実装は、コネクタによって異なります。
- MySQL コネクタは、MySQL の JSON パス実装 を使用します。
- PostgreSQL コネクタは、バージョン 12 以前でサポートされているカスタム JSON 関数と演算子 を使用します。
たとえば、以下は有効な MySQL path
値です。
$petFeatures.petName
以下は有効な PostgreSQL path
値です。
["petFeatures", "petName"]
オブジェクトプロパティでフィルタリング
JSON ブロック内の特定のプロパティでフィルタリングできます。次の例では、extendedPetsData
の値は一次元のネストされていない JSON オブジェクトです。
{
"petName": "Claudine",
"petType": "House cat"
}
次のクエリは、petName
の値が "Claudine"
であるすべてのユーザーを返します。
- PostgreSQL
- MySQL
const getUsers = await prisma.user.findMany({
where: {
extendedPetsData: {
path: ['petName'],
equals: 'Claudine',
},
},
})
const getUsers = await prisma.user.findMany({
where: {
extendedPetsData: {
path: '$.petName',
equals: 'Claudine',
},
},
})
次のクエリは、petType
の値に "cat"
が含まれるすべてのユーザーを返します。
- PostgreSQL
- MySQL
const getUsers = await prisma.user.findMany({
where: {
extendedPetsData: {
path: ['petType'],
string_contains: 'cat',
},
},
})
const getUsers = await prisma.user.findMany({
where: {
extendedPetsData: {
path: '$.petType',
string_contains: 'cat',
},
},
})
次の文字列フィルターが利用可能です。
これらで大文字と小文字を区別しないフィルターを使用するには、mode
オプションを使用できます。
- PostgreSQL
- MySQL
const getUsers = await prisma.user.findMany({
where: {
extendedPetsData: {
path: ['petType'],
string_contains: 'cat',
mode: 'insensitive'
},
},
})
const getUsers = await prisma.user.findMany({
where: {
extendedPetsData: {
path: '$.petType',
string_contains: 'cat',
mode: 'insensitive'
},
},
})
ネストされたオブジェクトプロパティでフィルタリング
ネストされた JSON プロパティでフィルタリングできます。次の例では、extendedPetsData
の値は、複数のレベルのネストを持つ JSON オブジェクトです。
{
"pet1": {
"petName": "Claudine",
"petType": "House cat"
},
"pet2": {
"petName": "Sunny",
"petType": "Gerbil",
"features": {
"eyeColor": "Brown",
"furColor": "White and black"
}
}
}
次のクエリは、"pet2"
→ "petName"
が "Sunny"
であるすべてのユーザーを返します。
- PostgreSQL
- MySQL
const getUsers = await prisma.user.findMany({
where: {
extendedPetsData: {
path: ['pet2', 'petName'],
equals: 'Sunny',
},
},
})
const getUsers = await prisma.user.findMany({
where: {
extendedPetsData: {
path: '$.pet2.petName',
equals: 'Sunny',
},
},
})
次のクエリは、すべてのユーザーを返します。ここで、
"pet2"
→"petName"
が"Sunny"
"pet2"
→"features"
→"furColor"
に"black"
が含まれる
- PostgreSQL
- MySQL
const getUsers = await prisma.user.findMany({
where: {
AND: [
{
extendedPetsData: {
path: ['pet2', 'petName'],
equals: 'Sunny',
},
},
{
extendedPetsData: {
path: ['pet2', 'features', 'furColor'],
string_contains: 'black',
},
},
],
},
})
const getUsers = await prisma.user.findMany({
where: {
AND: [
{
extendedPetsData: {
path: '$.pet2.petName',
equals: 'Sunny',
},
},
{
extendedPetsData: {
path: '$.pet2.features.furColor',
string_contains: 'black',
},
},
],
},
})
配列値でのフィルタリング
スカラ配列 (文字列、整数) 内の特定の値の存在でフィルタリングできます。次の例では、extendedPetsData
の値は文字列の配列です。
["Claudine", "Sunny"]
次のクエリは、"Claudine"
という名前のペットを持つすべてのユーザーを返します。
- PostgreSQL
- MySQL
const getUsers = await prisma.user.findMany({
where: {
extendedPetsData: {
array_contains: ['Claudine'],
},
},
})
注: PostgreSQL では、array_contains
の値は、配列に単一の値しか含まれていない場合でも、文字列ではなく配列である必要があります。
const getUsers = await prisma.user.findMany({
where: {
extendedPetsData: {
array_contains: 'Claudine',
},
},
})
次の配列フィルターが利用可能です。
ネストされた配列値でのフィルタリング
スカラ配列 (文字列、整数) 内の特定の値の存在でフィルタリングできます。次の例では、extendedPetsData
の値には、名前のネストされたスカラ配列が含まれています。
{
"cats": { "owned": ["Bob", "Sunny"], "fostering": ["Fido"] },
"dogs": { "owned": ["Ella"], "fostering": ["Prince", "Empress"] }
}
スカラ値配列
次のクエリは、"Fido"
という名前の猫を飼っているすべてのユーザーを返します。
- PostgreSQL
- MySQL
const getUsers = await prisma.user.findMany({
where: {
extendedPetsData: {
path: ['cats', 'fostering'],
array_contains: ['Fido'],
},
},
})
注: PostgreSQL では、array_contains
の値は、配列に単一の値しか含まれていない場合でも、文字列ではなく配列である必要があります。
const getUsers = await prisma.user.findMany({
where: {
extendedPetsData: {
path: '$.cats.fostering',
array_contains: 'Fido',
},
},
})
次のクエリは、"Fido"
と "Bob"
という名前の猫を飼っているすべてのユーザーを返します。
- PostgreSQL
- MySQL
const getUsers = await prisma.user.findMany({
where: {
extendedPetsData: {
path: ['cats', 'fostering'],
array_contains: ['Fido', 'Bob'],
},
},
})
const getUsers = await prisma.user.findMany({
where: {
extendedPetsData: {
path: '$.cats.fostering',
array_contains: ['Fido', 'Bob'],
},
},
})
JSON オブジェクト配列
- PostgreSQL
- MySQL
const json = [{ status: 'expired', insuranceID: 92 }]
const checkJson = await prisma.user.findMany({
where: {
extendedPetsData: {
path: ['insurances'],
array_contains: json,
},
},
})
const json = { status: 'expired', insuranceID: 92 }
const checkJson = await prisma.user.findMany({
where: {
extendedPetsData: {
path: '$.insurances',
array_contains: json,
},
},
})
-
PostgreSQL を使用している場合は、配列にオブジェクトが 1 つしか含まれていない場合でも、一致させるオブジェクトの配列を渡す必要があります。
[{ status: 'expired', insuranceID: 92 }]
// PostgreSQLMySQL を使用している場合は、一致させる単一のオブジェクトを渡す必要があります。
{ status: 'expired', insuranceID: 92 }
// MySQL -
フィルター配列に複数のオブジェクトが含まれている場合、PostgreSQL は、少なくとも 1 つのオブジェクトが存在する場合ではなく、すべてのオブジェクトが存在する場合にのみ結果を返します。
-
array_contains
を文字列ではなく、JSON オブジェクトに設定する必要があります。文字列を使用すると、Prisma Client は引用符をエスケープし、クエリは結果を返しません。例:array_contains: '[{"status": "expired", "insuranceID": 92}]'
は、データベースに次のように送信されます。
[{\"status\": \"expired\", \"insuranceID\": 92}]
インデックスによる配列要素のターゲット設定
特定の位置にある要素の値でフィルタリングできます。
{ "owned": ["Bob", "Sunny"], "fostering": ["Fido"] }
- PostgreSQL
- MySQL
const getUsers = await prisma.user.findMany({
where: {
comments: {
path: ['owned', '1'],
string_contains: 'Bob',
},
},
})
const getUsers = await prisma.user.findMany({
where: {
comments: {
path: '$.owned[1]',
string_contains: 'Bob',
},
},
})
配列内のオブジェクトキー値でのフィルタリング
プロバイダーによっては、配列内のオブジェクトのキー値でフィルタリングできます。
配列内のオブジェクトキー値でのフィルタリングは、MySQL データベースコネクタのみでサポートされています。ただし、JSON オブジェクト全体の存在をフィルタリングすることはできます。
次の例では、extendedPetsData
の値は、ネストされた insurances
配列を持つオブジェクトの配列であり、2 つのオブジェクトが含まれています。
[
{
"petName": "Claudine",
"petType": "House cat",
"insurances": [
{ "insuranceID": 92, "status": "expired" },
{ "insuranceID": 12, "status": "active" }
]
},
{
"petName": "Sunny",
"petType": "Gerbil"
},
{
"petName": "Gerald",
"petType": "Corn snake"
},
{
"petName": "Nanna",
"petType": "Moose"
}
]
次のクエリは、少なくとも 1 匹のペットがムースであるすべてのユーザーを返します。
const getUsers = await prisma.user.findMany({
where: {
extendedPetsData: {
path: '$[*].petType',
array_contains: 'Moose',
},
},
})
$[*]
は、ペットオブジェクトのルート配列です。petType
は、任意のペットオブジェクトのpetType
キーと一致します。
次のクエリは、少なくとも 1 匹のペットに期限切れの保険があるすべてのユーザーを返します。
const getUsers = await prisma.user.findMany({
where: {
extendedPetsData: {
path: '$[*].insurances[*].status',
array_contains: 'expired',
},
},
})
$[*]
は、ペットオブジェクトのルート配列です。insurances[*]
は、任意のペットオブジェクト内の任意のinsurances
配列と一致します。status
は、任意の保険オブジェクト内の任意のstatus
キーと一致します。
高度な例: ネストされた JSON キー値を更新する
次の例は、extendedPetsData
の値が次のバリエーションであることを前提としています。
{
"petName": "Claudine",
"petType": "House cat",
"insurances": [
{ "insuranceID": 92, "status": "expired" },
{ "insuranceID": 12, "status": "active" }
]
}
次の例:
- すべてのユーザーを取得
- 各保険オブジェクトの
"status"
を"expired"
に変更します。 - ID が
92
の期限切れの保険を持つすべてのユーザーを取得します。
- PostgreSQL
- MySQL
const userQueries: string | any[] = []
getUsers.forEach((user) => {
if (
user.extendedPetsData &&
typeof user.extendedPetsData === 'object' &&
!Array.isArray(user.extendedPetsData)
) {
const petsObject = user.extendedPetsData as Prisma.JsonObject
const i = petsObject['insurances']
if (i && typeof i === 'object' && Array.isArray(i)) {
const insurancesArray = i as Prisma.JsonArray
insurancesArray.forEach((i) => {
if (i && typeof i === 'object' && !Array.isArray(i)) {
const insuranceObject = i as Prisma.JsonObject
insuranceObject['status'] = 'expired'
}
})
const whereClause = Prisma.validator<Prisma.UserWhereInput>()({
id: user.id,
})
const dataClause = Prisma.validator<Prisma.UserUpdateInput>()({
extendedPetsData: petsObject,
})
userQueries.push(
prisma.user.update({
where: whereClause,
data: dataClause,
})
)
}
}
})
if (userQueries.length > 0) {
console.log(userQueries.length + ' queries to run!')
await prisma.$transaction(userQueries)
}
const json = [{ status: 'expired', insuranceID: 92 }]
const checkJson = await prisma.user.findMany({
where: {
extendedPetsData: {
path: ['insurances'],
array_contains: json,
},
},
})
console.log(checkJson.length)
const userQueries: string | any[] = []
getUsers.forEach((user) => {
if (
user.extendedPetsData &&
typeof user.extendedPetsData === 'object' &&
!Array.isArray(user.extendedPetsData)
) {
const petsObject = user.extendedPetsData as Prisma.JsonObject
const insuranceList = petsObject['insurances'] // is a Prisma.JsonArray
if (Array.isArray(insuranceList)) {
insuranceList.forEach((insuranceItem) => {
if (
insuranceItem &&
typeof insuranceItem === 'object' &&
!Array.isArray(insuranceItem)
) {
insuranceItem['status'] = 'expired' // is a Prisma.JsonObject
}
})
const whereClause = Prisma.validator<Prisma.UserWhereInput>()({
id: user.id,
})
const dataClause = Prisma.validator<Prisma.UserUpdateInput>()({
extendedPetsData: petsObject,
})
userQueries.push(
prisma.user.update({
where: whereClause,
data: dataClause,
})
)
}
}
})
if (userQueries.length > 0) {
console.log(userQueries.length + ' queries to run!')
await prisma.$transaction(userQueries)
}
const json = { status: 'expired', insuranceID: 92 }
const checkJson = await prisma.user.findMany({
where: {
extendedPetsData: {
path: '$.insurances',
array_contains: json,
},
},
})
console.log(checkJson.length)
null
値の使用
SQL データベースの JSON
フィールドには、2 種類の null
値が考えられます。
- データベース
NULL
: データベースの値はNULL
です。 - JSON
null
: データベースの値にはnull
である JSON 値が含まれています。
これらの可能性を区別するために、使用できる 3 つのnull 列挙型を導入しました。
JsonNull
: JSON のnull
値を表します。DbNull
: データベースのNULL
値を表します。AnyNull
:null
JSON 値とNULL
データベース値の両方を表します (フィルタリング時のみ)。
v4.0.0 以降、JsonNull
、DbNull
、および AnyNull
はオブジェクトです。v4.0.0 より前は、文字列でした。
- null 列挙型のいずれかを使用してフィルタリングする場合、短縮形を使用したり、
equals
演算子を省略したりすることはできません。 - これらのnull 列挙型は、MongoDB には適用されません。JSON
null
とデータベースNULL
の違いが存在しないためです。 - null 列挙型は、すべてのデータベースの
array_contains
演算子には適用されません。JSON 配列内に JSONnull
しか存在できないためです。JSON 配列内にデータベースNULL
が存在することはできないため、{ array_contains: null }
はあいまいではありません。
例:
model Log {
id Int @id
meta Json
}
AnyNull
の使用例を次に示します。
import { Prisma } from '@prisma/client'
prisma.log.findMany({
where: {
data: {
meta: {
equals: Prisma.AnyNull,
},
},
},
})
null
値の挿入
これは、create
、update
、および upsert
にも適用されます。null
値を Json
フィールドに挿入するには、次のように記述します。
import { Prisma } from '@prisma/client'
prisma.log.create({
data: {
meta: Prisma.JsonNull,
},
})
データベース NULL
を Json
フィールドに挿入するには、次のように記述します。
import { Prisma } from '@prisma/client'
prisma.log.create({
data: {
meta: Prisma.DbNull,
},
})
null
値によるフィルタリング
JsonNull
または DbNull
でフィルタリングするには、次のように記述します。
import { Prisma } from '@prisma/client'
prisma.log.findMany({
where: {
meta: {
equals: Prisma.AnyNull,
},
},
})
これらのnull 列挙型は、MongoDB には適用されません。MongoDB は JSON null
とデータベース NULL
を区別しないためです。また、すべてのデータベースの array_contains
演算子にも適用されません。JSON 配列内に JSON null
しか存在できないためです。JSON 配列内にデータベース NULL
が存在することはできないため、{ array_contains: null }
はあいまいではありません。
型付き Json
デフォルトでは、Json
フィールドは Prisma モデルで型指定されていません。これらのフィールド内で強力な型指定を実現するには、prisma-json-types-generator のような外部パッケージを使用する必要があります。
prisma-json-types-generator
の使用
まず、prisma-json-types-generator
をインストールして構成します パッケージの説明に従って。
次に、次のようなモデルがあると仮定します。
model Log {
id Int @id
meta Json
}
抽象構文木コメントを使用して更新および型指定できます。
model Log {
id Int @id
/// [LogMetaType]
meta Json
}
次に、tsconfig.json
に含まれる型宣言ファイルで上記の型を定義していることを確認してください。
declare global {
namespace PrismaJson {
type LogMetaType = { timestamp: number; host: string }
}
}
これで、Log.meta
を操作するときに、強力な型指定が行われます。
Json
に関する FAQ
JSON キー/値のサブセットを選択して返すことはできますか?
いいえ - まだ 返す JSON 要素を選択 することはできません。Prisma Client は JSON オブジェクト全体を返します。
特定のキーの存在でフィルタリングできますか?
いいえ - 特定のキーの存在でフィルタリングすることはまだできません。
大文字と小文字を区別しないフィルタリングはサポートされていますか?
いいえ - 大文字と小文字を区別しないフィルタリング はまだサポートされていません。
JSON 値内のオブジェクトプロパティをソートできますか?
いいえ、JSON 値内のオブジェクトプロパティのソート (order-by-prop) は現在サポートされていません。
JSON フィールドのデフォルト値を設定する方法は?
Json
型の @default
値を設定する場合は、@default
属性内で二重引用符で囲む必要があります (および、必要に応じて「内側」の二重引用符をバックスラッシュを使用してエスケープします)。例:
model User {
id Int @id @default(autoincrement())
json1 Json @default("[]")
json2 Json @default("{ \"hello\": \"world\" }")
}