據(jù)格式(NDF)完全指南:服務(wù)間數(shù)據(jù)導(dǎo)入導(dǎo)出的中間 JSON 格式)
后端數(shù)據(jù)庫(kù)GraphQL【免費(fèi)下載鏈接】prisma1 Database Tools incl. ORM, Migrations and Admin UI (Postgres, MySQL MongoDB) [deprecated]項(xiàng)目地址https://gitcode.com/gh_mirrors/pr/prisma1點(diǎn)擊查看免費(fèi)下載本篇技術(shù)指南圍繞 Prisma 服務(wù)中數(shù)據(jù)導(dǎo)入導(dǎo)出的核心中間格式——?dú)w一化數(shù)據(jù)格式Normalized Data FormatNDF展開系統(tǒng)講解其 JSON 結(jié)構(gòu)、三種 value typenodes/lists/relations的字段約定與完整示例并結(jié)合當(dāng)前倉(cāng)庫(kù)中prisma import/prisma export命令的源碼實(shí)現(xiàn)深入剖析 NDF 在導(dǎo)入校驗(yàn)、導(dǎo)出分頁(yè)與壓縮打包等環(huán)節(jié)中的實(shí)際運(yùn)作方式。讀完本文你將能夠手寫合法的 NDF 數(shù)據(jù)文件理解 Prisma CLI 導(dǎo)入導(dǎo)出的底層調(diào)用鏈并能夠依據(jù)校驗(yàn)規(guī)則定位并修復(fù)數(shù)據(jù)格式錯(cuò)誤。NDF 是什么導(dǎo)入導(dǎo)出之間的中間格式The Normalized Data Format (NDF) is used as anintermediatedata format for import and export in Prisma services. NDF 為 JSON 定義了一種特定的結(jié)構(gòu)規(guī)范它既不直接對(duì)應(yīng)數(shù)據(jù)庫(kù)的行也不直接對(duì)應(yīng) GraphQL 查詢結(jié)果而是把服務(wù)中的數(shù)據(jù)拆解為三類互補(bǔ)的值類型value type從而讓數(shù)據(jù)可以在不同 Prisma 服務(wù)、不同環(huán)境本地 / 云端 / 自建集群之間無損遷移。在 Prisma 的數(shù)據(jù)遷移實(shí)踐中prisma export產(chǎn)出的.zip內(nèi)部正是 NDF 文件按nodes/、lists/、relations/三個(gè)目錄組織而prisma import消費(fèi)的也正是這種格式。相關(guān)的完整命令參考見 01-Data-Import.md 與 02-Data-Export.md。NDF 的三種值類型當(dāng)使用 NDF 時(shí)數(shù)據(jù)被拆分到三種不同的值類型中Nodes節(jié)點(diǎn)包含節(jié)點(diǎn)的標(biāo)量字段scalar fields數(shù)據(jù)Lists列表包含節(jié)點(diǎn)的列表字段list fields數(shù)據(jù)Relations關(guān)系包含通過關(guān)系字段relation fields連接兩個(gè)節(jié)點(diǎn)的數(shù)據(jù)。這種拆分是 NDF 的核心設(shè)計(jì)把一個(gè)對(duì)象拆成它的標(biāo)量部分 它的列表部分 它與其他對(duì)象的關(guān)系三者分別獨(dú)立成文檔最終由id字段與_typeName字段互相錨定、重新組裝。頂層結(jié)構(gòu)valueType valuesNDF 文檔是一個(gè) JSON 對(duì)象只包含兩個(gè)頂層鍵valueType表示文檔中數(shù)據(jù)的值類型取值只能是nodes、lists或relationsvalues實(shí)際數(shù)據(jù)是一個(gè)數(shù)組其元素結(jié)構(gòu)必須符合對(duì)應(yīng)的值類型約定。該結(jié)構(gòu)在倉(cāng)庫(kù)源碼中有精確的類型定義types.tsexport type ValueType nodes | relations | lists export type ScalarType number | string | boolean | null export interface Node { _typeName: string id: string [fieldName: string]: ScalarType | ScalarType[] } export interface RelationNode { _typeName: string id: string fieldName: string } export type RelationTuple [RelationNode, RelationNode] export interface ImportData { valueType: ValueType values: Node[] | RelationTuple[] }下面所有示例均基于如下數(shù)據(jù)模型與 Normalized-Data-Format.md 中的示例一致type User { id: String! unique firstName: String! lastName: String! hobbies: [String!]! partner: User }valueType 為 nodes 時(shí)的結(jié)構(gòu)當(dāng)valueType為nodes時(shí)values數(shù)組內(nèi)每個(gè)對(duì)象的結(jié)構(gòu)如下{ valueType: nodes, values: [ { _typeName: STRING, id: STRING, scalarField1: ANY, scalarField2: ANY, ..., scalarFieldN: ANY }, ... ] }其中_typeName與id均為字符串_typeName指數(shù)據(jù)模型中 SDL 類型的名稱scalarFieldX占位符是該 SDL 類型上各標(biāo)量字段的名稱。例如下面的 JSON 文檔可用于導(dǎo)入兩個(gè)User節(jié)點(diǎn)的標(biāo)量值{ valueType: nodes, values: [ {_typeName: User, id: johndoe, firstName: John, lastName: Doe}, {_typeName: User, id: sarahdoe, firstName: Sarah, lastName: Doe} ] }從源碼校驗(yàn)邏輯Validator.ts可以進(jìn)一步確認(rèn)nodes的約束_typeName必須存在且必須是數(shù)據(jù)模型由 SDL 解析出的類型集合中真實(shí)存在的類型id必須存在且為字符串?dāng)?shù)據(jù)模型中標(biāo)為必填NonNull的標(biāo)量字段必須全部出現(xiàn)在對(duì)象中否則拋出 lacks the following properties 錯(cuò)誤出現(xiàn)數(shù)據(jù)模型中不存在的字段會(huì)被判為未知字段而報(bào)錯(cuò)createdAt、updatedAt、id除外字段值必須匹配其聲明的標(biāo)量類型見下文類型校驗(yàn)器小節(jié)。valueType 為 lists 時(shí)的結(jié)構(gòu)當(dāng)valueType為lists時(shí)values數(shù)組內(nèi)每個(gè)對(duì)象的結(jié)構(gòu)如下{ valueType: lists, values: [ { _typeName: STRING, id: STRING, scalarListField: [ANY] }, ... ] }_typeName與id同樣為字符串scalarListField占位符是該 SDL 類型上列表字段的名稱。需要注意與標(biāo)量字段不同每個(gè)對(duì)象只能為一個(gè)列表字段提供值——即一個(gè)lists文檔中的對(duì)象只能攜帶一個(gè)列表鍵。例如下面的 JSON 文檔可用于導(dǎo)入兩個(gè)User節(jié)點(diǎn)的hobbies列表字段值{ valueType: lists, values: [ {_typeName: User, id: johndoe, hobbies: [Fishing, Cooking]}, {_typeName: User, id: sarahdoe, hobbies: [Biking, Coding]} ] }源碼中對(duì)應(yīng)的校驗(yàn)Validator.ts 與 Validator.ts保證了這種單向約定在nodes文檔中提供列表值會(huì)報(bào)錯(cuò)List value ... mustnt be provided in a nodes definition在lists文檔中提供標(biāo)量值也會(huì)報(bào)錯(cuò)lists文檔中的值必須是數(shù)組數(shù)組內(nèi)每個(gè)元素再按元素標(biāo)量類型逐一校驗(yàn)。valueType 為 relations 時(shí)的結(jié)構(gòu)當(dāng)valueType為relations時(shí)values數(shù)組內(nèi)每個(gè)元素的結(jié)構(gòu)如下{ valueType: relations, values: [ [ { _typeName: STRING, id: STRING, fieldName: STRING }, { _typeName: STRING, id: STRING, fieldName: STRING } ], ... ] }_typeName、id、fieldName均為字符串。_typeName指向數(shù)據(jù)模型中的一個(gè) SDL 類型fieldName是該類型上的關(guān)系字段名。由于關(guān)系數(shù)據(jù)的目的是通過一條關(guān)系連接兩個(gè)節(jié)點(diǎn)values中每個(gè)元素本身是一對(duì)pair——寫作一個(gè)恰好包含兩個(gè)元素的數(shù)組這與nodes、lists中單個(gè)對(duì)象的形式不同。例如下面的 JSON 文檔通過partner關(guān)系字段在兩個(gè)User節(jié)點(diǎn)之間建立一條關(guān)系{ valueType: relations, values: [ [ { _typeName: User, id: johndoe, fieldName: partner }, { _typeName: User, id: sarahdoe, fieldName: partner } ] ] }源碼中的關(guān)系元組校驗(yàn)Validator.ts非常嚴(yán)格每個(gè) tuple 必須是數(shù)組且長(zhǎng)度必須恰好為 2Relation tuple must have 2 nodestuple 內(nèi)兩個(gè)節(jié)點(diǎn)都必須滿足_typeNameid約束至少其中一個(gè)節(jié)點(diǎn)必須帶fieldName且該fieldName必須真實(shí)存在于對(duì)應(yīng)類型的字段定義中否則報(bào) points to a non-existing fieldName 錯(cuò)誤。導(dǎo)入導(dǎo)出流程中的 NDF源碼視角NDF 不只是規(guī)范文檔它貫穿了 Prisma CLI 導(dǎo)入導(dǎo)出的完整執(zhí)行鏈路。本倉(cāng)庫(kù)中相關(guān)實(shí)現(xiàn)位于 cli/packages/prisma-cli-core/src/commands/import 與 cli/packages/prisma-cli-core/src/commands/export 目錄下。導(dǎo)出分頁(yè)拉取并按 valueType 落盤prisma export的核心是 Exporter.ts。其關(guān)鍵流程download 方法為在工作目錄下創(chuàng)建.export/臨時(shí)目錄并預(yù)建nodes/、lists/、relations/三個(gè)子目錄makeDirs依次以nodes→lists→relations的順序調(diào)用導(dǎo)出服務(wù)接口每次請(qǐng)求攜帶{ fileType, cursor }其中cursor是{ table, row, field, array }四元組用于分頁(yè)游標(biāo)ExportCursor每次返回的數(shù)據(jù)被包裝成標(biāo)準(zhǔn) NDF 文檔寫入編號(hào)文件JSON.stringify({ valueType: fileType, values: data.out.jsonElements })文件名按六位前導(dǎo)零編號(hào)000001.json、000002.json……寫入對(duì)應(yīng)類型目錄downloadFiles當(dāng)游標(biāo)之和小于 0 時(shí)停止拉取隨后將.export/目錄壓縮為最終交付的.zip文件zipIt并清理臨時(shí)目錄。因此一份prisma export產(chǎn)物的內(nèi)部結(jié)構(gòu)天然就是 NDF 的組織形態(tài)export.zip ├── nodes/000001.json # valueType: nodes ├── lists/000001.json # valueType: lists └── relations/000001.json # valueType: relations導(dǎo)入解壓、逐類型校驗(yàn)與按序上傳prisma import的核心是 Importer.ts。其執(zhí)行流程upload 方法為若傳入的是.zip路徑則先解壓到.import/目錄unzip若傳入的是目錄則直接使用用 glob 收集nodes/、lists/、relations/三個(gè)目錄下的全部.json文件getFiles本地校驗(yàn)對(duì)每個(gè)文件逐一JSON.parse后交給Validator校驗(yàn)validateFiles。如果三個(gè)目錄一個(gè)文件都沒有直接報(bào)錯(cuò)提示目錄結(jié)構(gòu)不合法錯(cuò)誤信息中同樣指向 NDF 文檔按序上傳嚴(yán)格按nodes→lists→relations的順序逐文件上傳到服務(wù)端。這個(gè)順序是硬性要求——必須先有節(jié)點(diǎn)數(shù)據(jù)才能掛接列表字段與關(guān)系斷點(diǎn)續(xù)傳上傳進(jìn)度通過state.json記錄{ nodes, lists, relations }三個(gè)數(shù)字文件名中的數(shù)字編號(hào)如000001.json→ 1用于比對(duì)已導(dǎo)入過的文件會(huì)跳過Skipping file ... (already imported)從而支持中斷后繼續(xù)導(dǎo)入saveState / getState / getNumber。導(dǎo)入前的 NDF 校驗(yàn)器Validator.ts 是 NDF 數(shù)據(jù)質(zhì)量的守門員。它以數(shù)據(jù)模型的 SDL 字符串為輸入用 GraphQL 解析器將其轉(zhuǎn)為 AST再據(jù)此構(gòu)建每個(gè)類型的字段映射、必填字段清單、枚舉取值表astToTypes / astToEnums隨后對(duì)三種 value type 分別執(zhí)行校驗(yàn)。其中內(nèi)置的標(biāo)量類型校驗(yàn)器validators值得注意SDL 類型校驗(yàn)規(guī)則源碼實(shí)現(xiàn)ID/String必須是字符串isStringInt可解析為數(shù)字且(x | 0) x即為整數(shù)Float必須是數(shù)字isNumberDateTime必須是字符串且匹配 ISO-8601 風(fēng)格正則\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d([-][0-2]\d:[0-5]\d\|Z)Boolean必須是布爾值isBooleanJson必須是對(duì)象typeof value object自定義枚舉值必須包含在 SDL 枚舉定義中makeEnumValidators這些規(guī)則在測(cè)試中有充分的覆蓋例如 Validator.test.ts 驗(yàn)證了缺失_typeName、未知_typeName都會(huì)拋錯(cuò)Validator.test.ts 驗(yàn)證了id為數(shù)字 25 時(shí)拋錯(cuò)、為字符串25時(shí)通過。倉(cāng)庫(kù)內(nèi)置的真實(shí) NDF 示例倉(cāng)庫(kù)的導(dǎo)入測(cè)試 fixturecli/packages/prisma-cli-core/src/commands/import/fixtures/basic提供了一套可直接對(duì)照學(xué)習(xí)的完整 NDF 數(shù)據(jù)數(shù)據(jù)模型 types.graphqlPost含title、description、tags: [String!]!、state: PostState!枚舉、comments關(guān)系與Comment含text、post關(guān)系節(jié)點(diǎn)數(shù)據(jù) nodes/000001.jsonvalueType: nodesPost攜帶標(biāo)量字段與枚舉字段stateComment只攜帶text列表數(shù)據(jù) lists/000001.jsonvalueType: lists每個(gè)Post對(duì)象僅攜帶一個(gè)列表鍵tags關(guān)系數(shù)據(jù) relations/000001.jsonvalueType: relations每個(gè) tuple 由Post.comments與Comment.post這一對(duì)關(guān)系字段構(gòu)成進(jìn)度狀態(tài) state.json初始狀態(tài)為三個(gè)計(jì)數(shù)器均為 0。生成該 fixture 的腳本見 makeData.ts其數(shù)據(jù)生成邏輯與 NDF 的標(biāo)量 / 列表 / 關(guān)系三分離模型一一對(duì)應(yīng)適合作為手寫 NDF 數(shù)據(jù)的參考模板。手寫 NDF 的實(shí)踐要點(diǎn)綜合規(guī)范文檔與源碼實(shí)現(xiàn)在手動(dòng)構(gòu)造 NDF 文件時(shí)應(yīng)當(dāng)遵循以下要點(diǎn)頂層結(jié)構(gòu)固定每個(gè) JSON 文件都必須且只能包含valueType與values兩個(gè)鍵valueType只能取nodes、lists、relations三者之一三件套齊備一次完整的導(dǎo)入需要同時(shí)提供nodes/、lists/、relations/三個(gè)目錄內(nèi)容可以為空文件集但目錄結(jié)構(gòu)必須存在否則 CLI 會(huì)報(bào)目錄結(jié)構(gòu)錯(cuò)誤id是錨點(diǎn)三種 value type 都通過_typeNameid定位到具體節(jié)點(diǎn)id必須為字符串且與數(shù)據(jù)庫(kù)中該節(jié)點(diǎn)的唯一標(biāo)識(shí)一致類型正確性標(biāo)量字段值必須與 SDL 聲明的類型匹配如DateTime需符合 ISO-8601 格式、枚舉值必須合法否則會(huì)在本地校驗(yàn)階段即被攔截列表與標(biāo)量互斥列表字段只能出現(xiàn)在lists文檔中且每個(gè)對(duì)象只放一個(gè)列表字段標(biāo)量字段只能出現(xiàn)在nodes文檔中關(guān)系成對(duì)出現(xiàn)relations的values中每個(gè)元素必須是恰好兩個(gè)節(jié)點(diǎn)的數(shù)組兩個(gè)節(jié)點(diǎn)分別從關(guān)系兩端如Post.comments與Comment.post描述同一條關(guān)系上傳順序不可變即便自行編寫導(dǎo)入腳本也應(yīng)遵循服務(wù)端 nodes → lists → relations 的依賴順序先有節(jié)點(diǎn)再掛列表和關(guān)系。小結(jié)歸一化數(shù)據(jù)格式NDF是 Prisma 數(shù)據(jù)導(dǎo)入導(dǎo)出體系的基石它以nodes/lists/relations三種值類型把數(shù)據(jù)庫(kù)內(nèi)容正交切分用統(tǒng)一的_typeNameid作為跨文檔的關(guān)聯(lián)錨點(diǎn)配合 CLI 的本地校驗(yàn)、按序上傳與斷點(diǎn)續(xù)傳機(jī)制實(shí)現(xiàn)了數(shù)據(jù)在不同 Prisma 服務(wù)之間的可靠遷移。無論是閱讀prisma export產(chǎn)物、手工構(gòu)造prisma import輸入還是排查導(dǎo)入報(bào)錯(cuò)理解本文所講的 NDF 結(jié)構(gòu)、字段約束與校驗(yàn)規(guī)則都能直接派上用場(chǎng)。進(jìn)一步的實(shí)踐入口完整的導(dǎo)入導(dǎo)出命令用法見 01-Data-Import.md 與 02-Data-Export.mdCLI 命令參考見 prisma-import.md數(shù)據(jù)遷移的端到端工作流可參考 01-Data-Export--Import.md。贊分享后端數(shù)據(jù)庫(kù)GraphQL【免費(fèi)下載鏈接】prisma1 Database Tools incl. ORM, Migrations and Admin UI (Postgres, MySQL MongoDB) [deprecated]項(xiàng)目地址https://gitcode.com/gh_mirrors/pr/prisma1點(diǎn)擊查看免費(fèi)下載相關(guān)推薦Prisma Normalized Data Format (NDF) 完全指南結(jié)構(gòu)化 JSON 實(shí)現(xiàn)數(shù)據(jù)導(dǎo)入導(dǎo)出的中間格式Prisma Normalized Data Format NDF 完全指南結(jié)構(gòu)化 JSON 實(shí)現(xiàn)數(shù)據(jù)導(dǎo)入導(dǎo)出的中間格式 Normalized Data Fo后端數(shù)據(jù)庫(kù)GraphQLPrisma 數(shù)據(jù)導(dǎo)入導(dǎo)出核心Normalized Data Format (NDF) 中間格式完全解析Prisma 數(shù)據(jù)導(dǎo)入導(dǎo)出核心Normalized Data Format NDF 中間格式完全解析 Normalized Data FormatNDF是后端數(shù)據(jù)庫(kù)GraphQLPrisma 數(shù)據(jù)導(dǎo)出與導(dǎo)入實(shí)戰(zhàn)使用 NDF 格式在服務(wù)與 stage 之間遷移數(shù)據(jù)Prisma 數(shù)據(jù)導(dǎo)出與導(dǎo)入實(shí)戰(zhàn)使用 NDF 格式在服務(wù)與 stage 之間遷移數(shù)據(jù) 本教程基于 Prisma 開源倉(cāng)庫(kù) prisma1 講解數(shù)據(jù)導(dǎo)出與導(dǎo)后端數(shù)據(jù)庫(kù)GraphQL上一篇Mac Mouse Fix終極指南3步將普通鼠標(biāo)升級(jí)為macOS生產(chǎn)力神器下一篇Slang 編譯目標(biāo)Targets、能力Capabilities與配置文件Profiles完整指南創(chuàng)作聲明:本文部分內(nèi)容由AI輔助生成(AIGC),僅供參考