使用 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 declare 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"]
新增至您的 schema。了解更多。 - 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,您必須傳入要比對的物件陣列,即使該陣列僅包含一個物件也是如此
[{ status: 'expired', insuranceID: 92 }]
// PostgreSQL如果您使用 MySQL,則必須傳入單一物件以進行比對
{ status: 'expired', insuranceID: 92 }
// MySQL -
如果您的篩選器陣列包含多個物件,PostgreSQL 只會在所有物件都存在時傳回結果,而不是至少有一個物件存在時。
-
您必須將
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
陣列,其中包含兩個物件
[
{
"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"
}
]
以下查詢傳回至少有一隻寵物是麋鹿的所有使用者
const getUsers = await prisma.user.findMany({
where: {
extendedPetsData: {
path: '$[*].petType',
array_contains: 'Moose',
},
},
})
$[*]
是寵物物件的根陣列petType
符合任何寵物物件中的petType
鍵
以下查詢傳回至少有一隻寵物的保險已過期的所有使用者
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
欄位可能有兩種 null
值。
- 資料庫
NULL
:資料庫中的值為NULL
。 - JSON
null
:資料庫中的值包含 JSON 值,該值為null
。
為了區分這些可能性,我們引入了三個您可以使用的空值列舉
JsonNull
:表示 JSON 中的null
值。DbNull
:表示資料庫中的NULL
值。AnyNull
:表示null
JSON 值和NULL
資料庫值。(僅在篩選時)
從 v4.0.0 開始,JsonNull
、DbNull
和 AnyNull
是物件。在 v4.0.0 之前,它們是字串。
- 當使用任何空值列舉進行篩選時,您無法使用簡寫並省略
equals
運算子。 - 這些空值列舉不適用於 MongoDB,因為 JSON
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,
},
},
})
這些空值列舉不適用於 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
常見問題
您可以選取要傳回的 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\" }")
}