Firebase Data Connect ช่วยให้คุณสร้างเครื่องมือเชื่อมต่อสําหรับอินสแตนซ์ PostgreSQL ที่มีการจัดการด้วย Google Cloud SQL ได้ เครื่องมือเชื่อมต่อเหล่านี้เป็นการรวมสคีมา การค้นหา และการดัดแปลงสําหรับการใช้ข้อมูล
คู่มือเริ่มต้นใช้งานได้แนะนำสคีมาแอปรีวิวภาพยนตร์สําหรับ PostgreSQL และคู่มือนี้จะเจาะลึกวิธีออกแบบสคีมาData Connectสําหรับ PostgreSQL
คู่มือนี้จะจับคู่Data Connectการค้นหาและการกลายพันธุ์กับตัวอย่างสคีมา เหตุใดจึงต้องพูดถึงการค้นหา (และการกลายพันธุ์) ในคู่มือเกี่ยวกับสคีมา Data Connect เช่นเดียวกับแพลตฟอร์มอื่นๆ ที่ใช้ GraphQL Firebase Data Connect เป็นแพลตฟอร์มการพัฒนาแบบเน้นการค้นหา ดังนั้นในฐานะนักพัฒนาซอฟต์แวร์ คุณจะต้องคำนึงถึงข้อมูลที่จำเป็นสำหรับลูกค้าในโมเดลข้อมูล ซึ่งจะส่งผลอย่างมากต่อสคีมาข้อมูลที่พัฒนาขึ้นสำหรับโปรเจ็กต์
คู่มือนี้เริ่มต้นด้วยสคีมาใหม่สําหรับรีวิวภาพยนตร์ จากนั้นจะอธิบายการค้นหาและการกลายพันธุ์ที่มาจากสคีมานั้น และสุดท้ายจะแสดงรายการ SQL ที่เทียบเท่ากับสคีมา Data Connect หลัก
สคีมาสําหรับแอปรีวิวภาพยนตร์
สมมติว่าคุณต้องการสร้างบริการที่อนุญาตให้ผู้ใช้ส่งและดูรีวิวภาพยนตร์
คุณต้องมีสคีมาเริ่มต้นสําหรับแอปดังกล่าว ซึ่งคุณจะขยายสคีมานี้ในภายหลังเพื่อสร้างการค้นหาเชิงสัมพันธ์ที่ซับซ้อน
ตารางภาพยนตร์
สคีมาสำหรับภาพยนตร์มีคำสั่งหลักๆ เช่น
@table
ซึ่งช่วยให้เราตั้งชื่อการดำเนินการได้โดยใช้อาร์กิวเมนต์singular
และplural
@col
เพื่อตั้งชื่อคอลัมน์อย่างชัดแจ้ง@default
เพื่ออนุญาตให้ตั้งค่าเริ่มต้น
# Movies
type Movie
@table(name: "Movies", singular: "movie", plural: "movies", key: ["id"]) {
id: UUID! @col(name: "movie_id") @default(expr: "uuidV4()")
title: String!
releaseYear: Int @col(name: "release_year")
genre: String
rating: Int @col(name: "rating")
description: String @col(name: "description")
}
ค่าของเซิร์ฟเวอร์และค่าสเกลาร์ของคีย์
ก่อนดูแอปรีวิวภาพยนตร์ เราขอแนะนําData Connect ค่าเซิร์ฟเวอร์และสเกลาร์คีย์
การใช้ค่าเซิร์ฟเวอร์ช่วยให้เซิร์ฟเวอร์ป้อนข้อมูลในช่องของตารางแบบไดนามิกได้อย่างมีประสิทธิภาพโดยใช้ค่าที่เก็บไว้หรือค่าที่คำนวณได้ทันทีตามนิพจน์ฝั่งเซิร์ฟเวอร์ที่เฉพาะเจาะจง เช่น คุณสามารถกําหนดช่องที่มีการใช้การประทับเวลาเมื่อมีการเข้าถึงช่องโดยใช้นิพจน์ updatedAt: Timestamp! @default(expr: "request.time")
สเกลาร์คีย์คือตัวระบุออบเจ็กต์ที่กระชับซึ่ง Data Connect จะรวบรวมจากช่องคีย์ในสคีมาโดยอัตโนมัติ สเกลาร์คีย์เกี่ยวข้องกับประสิทธิภาพ ซึ่งช่วยให้คุณค้นหาข้อมูลเกี่ยวกับตัวตนและโครงสร้างของข้อมูลได้ในคอลเดียว ซึ่งจะเป็นประโยชน์อย่างยิ่งเมื่อคุณต้องการดําเนินการตามลําดับกับระเบียนใหม่และต้องการตัวระบุที่ไม่ซ้ำกันเพื่อส่งไปยังการดำเนินการที่กําลังจะเกิดขึ้น และในกรณีที่คุณต้องการเข้าถึงคีย์เชิงสัมพันธ์เพื่อดําเนินการที่ซับซ้อนมากขึ้น
ตารางข้อมูลเมตาของภาพยนตร์
ตอนนี้มาติดตามผู้กำกับภาพยนตร์และสร้างความสัมพันธ์แบบ 1:1 กับ Movie
เพิ่มคําสั่ง @ref
เพื่อกําหนดความสัมพันธ์
# Movie Metadata
# Movie - MovieMetadata is a one-to-one relationship
type MovieMetadata
@table(
name: "MovieMetadata"
) {
# @ref creates a field in the current table (MovieMetadata) that holds the
# primary key of the referenced type
# In this case, @ref(fields: "id") is implied
movie: Movie! @ref
# movieId: UUID <- this is created by the above @ref
director: String @col(name: "director")
}
Actor และ MovieActor
ถัดไป คุณต้องการให้นักแสดงแสดงในภาพยนตร์ และเนื่องจากคุณมีความสัมพันธ์แบบหลายต่อหลายรายการระหว่างภาพยนตร์กับนักแสดง ให้สร้างตารางการเข้าร่วม
# Actors
# Suppose an actor can participate in multiple movies and movies can have multiple actors
# Movie - Actors (or vice versa) is a many to many relationship
type Actor @table(name: "Actors", singular: "actor", plural: "actors") {
id: UUID! @col(name: "actor_id") @default(expr: "uuidV4()")
name: String! @col(name: "name", dataType: "varchar(30)")
}
# Join table for many-to-many relationship for movies and actors
# The 'key' param signifies the primary key(s) of this table
# In this case, the keys are [movieId, actorId], the generated fields of the reference types [movie, actor]
type MovieActor @table(key: ["movie", "actor"]) {
# @ref creates a field in the current table (MovieActor) that holds the primary key of the referenced type
# In this case, @ref(fields: "id") is implied
movie: Movie! @ref
# movieId: UUID! <- this is created by the above @ref, see: implicit.gql
actor: Actor! @ref
# actorId: UUID! <- this is created by the above @ref, see: implicit.gql
role: String! @col(name: "role") # "main" or "supporting"
# optional other fields
}
ผู้ใช้
สุดท้ายคือผู้ใช้แอป
# Users
# Suppose a user can leave reviews for movies
# user:reviews is a one to many relationship, movie:reviews is a one to many relationship, movie:user is a many to many relationship
type User
@table(name: "Users", singular: "user", plural: "users", key: ["id"]) {
id: UUID! @col(name: "user_id") @default(expr: "uuidV4()")
auth: String @col(name: "user_auth") @default(expr: "auth.uid")
username: String! @col(name: "username", dataType: "varchar(30)")
# The following are generated from the @ref in the Review table
# reviews_on_user
# movies_via_Review
}
ประเภทข้อมูลที่รองรับ
Data Connect รองรับประเภทข้อมูลเชิงเดี่ยวต่อไปนี้ โดยมีการกำหนดค่าให้กับประเภท PostgreSQL โดยใช้ @col(dataType:)
Data Connect type | ประเภทในตัวของ GraphQL หรือ ประเภทที่กำหนดเองData Connect |
ประเภท PostgreSQL เริ่มต้น | ประเภท PostgreSQL ที่รองรับ (ชื่อแทนในวงเล็บ) |
---|---|---|---|
สตริง | GraphQL | ข้อความ | text bit(n), varbit(n) char(n), varchar(n) |
Int | GraphQL | int | Int2 (smallint, smallserial), int4 (integer, int, serial) |
ทศนิยม | GraphQL | float8 | float4 (จริง) float8 (ความแม่นยำแบบ Double) numeric (ทศนิยม) |
บูลีน | GraphQL | boolean | boolean |
UUID | กำหนดเอง | uuid | uuid |
Int64 | กำหนดเอง | bigint | int8 (bigint, bigserial) numeric (decimal) |
วันที่ | กำหนดเอง | date | วันที่ |
การประทับเวลา | กำหนดเอง | timestamptz | timestamptz หมายเหตุ: ระบบจะไม่จัดเก็บข้อมูลเขตเวลาท้องถิ่น |
เวกเตอร์ | กำหนดเอง | vector | เวกเตอร์ |
- GraphQL
List
จะแมปกับอาร์เรย์ 1 มิติ- เช่น
[Int]
แมปกับint5[]
,[Any]
แมปกับjsonb[]
- Data Connect ไม่รองรับอาร์เรย์ที่ซ้อนกัน
- เช่น
การค้นหาและการดัดแปลงแบบนัยและที่กําหนดไว้ล่วงหน้า
การค้นหาและการดัดแปลง Data Connect จะขยายชุดการค้นหาโดยนัยและการดัดแปลงโดยนัยที่ Data Connect สร้างขึ้นโดยอิงตามประเภทและความสัมพันธ์ของประเภทในสคีมา เครื่องมือในเครื่องจะสร้างการค้นหาและการดัดแปลงโดยนัยทุกครั้งที่คุณแก้ไขสคีมา
ในกระบวนการพัฒนา คุณจะใช้การค้นหาที่กําหนดไว้ล่วงหน้าและการกลายพันธุ์ที่กําหนดไว้ล่วงหน้าตามการดำเนินการโดยนัยเหล่านี้
การค้นหาและการเรียกใช้แบบไม่ระบุ
Data Connect จะอนุมานชื่อที่เหมาะสมสำหรับการค้นหาและการดัดแปลงโดยนัยจากประกาศประเภทสคีมา เช่น เมื่อทํางานกับแหล่งข้อมูล PostgreSQL หากคุณกําหนดตารางชื่อ Movie
เซิร์ฟเวอร์จะสร้างรายการต่อไปนี้โดยนัย
- คําค้นหาสําหรับกรณีการใช้งานตารางเดียวที่มีชื่อที่เข้าใจง่าย
movie
(เอกพจน์ สำหรับการดึงข้อมูลผลลัพธ์แต่ละรายการโดยส่งอาร์กิวเมนต์ เช่นeq
) และmovies
(พหูพจน์ สำหรับการดึงข้อมูลรายการผลลัพธ์โดยส่งอาร์กิวเมนต์ เช่นgt
และการดำเนินการ เช่นorderby
) นอกจากนี้ Data Connect ยังสร้างคำค้นหาสําหรับการดำเนินการแบบสัมพันธ์หลายตารางที่มีชื่อที่ชัดเจน เช่นactors_on_movies
หรือactors_via_actormovie
- การกลายพันธุ์ชื่อ
movie_insert
,movie_upsert
...
ภาษาคําจํากัดความสคีมายังช่วยให้คุณกําหนดชื่อการดำเนินการได้อย่างชัดเจนโดยใช้อาร์กิวเมนต์คําสั่ง singular
และ plural
คําสั่งสําหรับการค้นหาและการดัดแปลง
นอกเหนือจากคำสั่งที่คุณใช้ในการกำหนดประเภทและตารางแล้ว Data Connect ยังมีคำสั่ง @auth
, @check
, @redact
และ @transaction
สำหรับเสริมพฤติกรรมของคำค้นหาและการกลายพันธุ์
คำสั่ง | ใช้ได้กับ | คำอธิบาย |
---|---|---|
@auth |
การค้นหาและการดัดแปลง | กําหนดนโยบายการตรวจสอบสิทธิ์สําหรับการค้นหาหรือการดัดแปลง โปรดดูคู่มือการให้สิทธิ์และการรับรอง |
@check |
การค้นหาข้อมูลการให้สิทธิ์ | ยืนยันว่าฟิลด์ที่ระบุมีอยู่ในผลการค้นหา นิพจน์ Common Expression Language (CEL) ใช้เพื่อทดสอบค่าในช่อง โปรดดูคู่มือการให้สิทธิ์และการรับรอง |
@redact |
คำค้นหา | ปกปิดข้อมูลบางส่วนในการตอบกลับจากลูกค้า โปรดดูคู่มือการให้สิทธิ์และการรับรอง |
@transaction |
การกลายพันธุ์ | บังคับให้การเปลี่ยนแปลงทำงานในธุรกรรมฐานข้อมูลเสมอ ดูตัวอย่างการกลายพันธุ์ของแอปภาพยนตร์ |
การค้นหาฐานข้อมูลรีวิวภาพยนตร์
คุณกําหนดการค้นหา Data Connect ด้วยการประกาศประเภทการดำเนินการค้นหา ชื่อการดำเนินการ อาร์กิวเมนต์การดำเนินการตั้งแต่ 0 รายการขึ้นไป และคำสั่งที่มีอาร์กิวเมนต์ตั้งแต่ 0 รายการขึ้นไป
ในบทเริ่มต้นใช้งาน ตัวอย่างคําค้นหา listEmails
ไม่ได้ใช้พารามิเตอร์ แน่นอนว่าในหลายกรณี ข้อมูลที่ส่งไปยังช่องการค้นหาจะเป็นแบบไดนามิก คุณสามารถใช้ไวยากรณ์ $variableName
เพื่อทํางานกับตัวแปรในฐานะองค์ประกอบหนึ่งของคําจํากัดความการค้นหา
ดังนั้นการค้นหาต่อไปนี้จึงมี
- คําจํากัดความประเภท
query
- ชื่อการดำเนินการ (การค้นหา)
ListMoviesByGenre
- อาร์กิวเมนต์การดำเนินการ
$genre
ตัวแปรเดียว - คําสั่งเดียว
@auth
query ListMoviesByGenre($genre: String!) @auth(level: USER)
อาร์กิวเมนต์การค้นหาทุกรายการต้องมีการประกาศประเภท ประเภทในตัว เช่น String
หรือประเภทที่กำหนดเองซึ่งกำหนดโดยสคีมา เช่น Movie
มาดูลักษณะของการค้นหาที่ซับซ้อนมากขึ้นกัน ปิดท้ายด้วยการแนะนำนิพจน์ความสัมพันธ์ที่มีประสิทธิภาพและกระชับซึ่งมีอยู่ในคําค้นหาโดยนัยที่คุณนำไปต่อยอดในคําค้นหาที่กําหนดไว้ล่วงหน้าได้
สเกลาร์หลักในการค้นหา
แต่ก่อนอื่น โปรดดูหมายเหตุเกี่ยวกับสเกลาร์หลัก
Data Connect กำหนดประเภทพิเศษสำหรับสเกลาร์คีย์ที่ระบุโดย
_Key
เช่น ประเภทของค่าสเกลาร์คีย์สําหรับตาราง Movie
คือ
Movie_Key
คุณจะดึงข้อมูลสเกลาร์หลักเป็นการตอบกลับที่เกิดจากการกลายพันธุ์โดยนัยส่วนใหญ่ หรือจากคำค้นหาที่คุณดึงข้อมูลช่องทั้งหมดที่จำเป็นต่อการสร้างคีย์สเกลาร์
การค้นหาอัตโนมัติแบบเดี่ยว เช่น movie
ในตัวอย่างที่ใช้งานอยู่รองรับอาร์กิวเมนต์คีย์ที่ยอมรับสเกลาร์คีย์
คุณอาจส่งค่าสเกลาร์ของคีย์เป็นค่าคงที่ แต่คุณกําหนดตัวแปรเพื่อส่งค่าสเกลาร์คีย์เป็นอินพุตได้
query GetMovie($myKey: Movie_Key!) {
movie(key: $myKey) { title }
}
ข้อมูลเหล่านี้สามารถระบุไว้ใน JSON คำขอได้ดังนี้ (หรือรูปแบบการจัดรูปแบบอื่นๆ)
{
# …
"variables": {
"myKey": {"foo": "some-string-value", "bar": 42}
}
}
เนื่องจากการแยกวิเคราะห์สเกลาร์ที่กำหนดเอง คุณจึงสร้าง Movie_Key
โดยใช้ไวยากรณ์ออบเจ็กต์ซึ่งอาจมีตัวแปรได้ ซึ่งจะมีประโยชน์ส่วนใหญ่เมื่อคุณต้องการแยกคอมโพเนนต์แต่ละรายการออกเป็นตัวแปรต่างๆ ด้วยเหตุผลบางอย่าง
การใช้ชื่อแทนในการค้นหา
Data Connect รองรับการแอบอ้างเป็น GraphQL ในการค้นหา คุณใช้นามแฝงเพื่อเปลี่ยนชื่อข้อมูลที่แสดงในผลการค้นหาได้ ข้อความค้นหา Data Connect รายการเดียวสามารถใช้ตัวกรองหลายรายการหรือการดำเนินการค้นหาอื่นๆ ในคําขอที่มีประสิทธิภาพเพียงรายการเดียวไปยังเซิร์ฟเวอร์ ซึ่งจะส่ง "ข้อความค้นหาย่อย" หลายรายการพร้อมกันได้อย่างมีประสิทธิภาพ คุณใช้ชื่อแทนเพื่อแยกแยะคำค้นหาย่อยเพื่อหลีกเลี่ยงการทับซ้อนกันของชื่อในชุดข้อมูลที่แสดงผล
นี่คือการค้นหาที่นิพจน์ใช้ชื่อแทน mostPopular
query ReviewTopPopularity($genre: String) {
mostPopular: review(first: {
where: {genre: {eq: $genre}},
orderBy: {popularity: DESC}
}) { … }
}
การค้นหาแบบง่ายด้วยตัวกรอง
การค้นหา Data Connect จะจับคู่กับตัวกรอง SQL ทั่วไปและการดำเนินการเรียงลำดับทั้งหมด
โอเปอเรเตอร์ where
และ orderBy
(คำค้นหาแบบเอกพจน์และพหูพจน์)
แสดงแถวที่ตรงกันทั้งหมดจากตาราง (และการเชื่อมโยงที่ฝังอยู่) แสดงผลอาร์เรย์เปล่าหากไม่มีระเบียนที่ตรงกับตัวกรอง
query MovieByTopRating($genre: String) {
mostPopular: movies(
where: { genre: { eq: $genre } }, orderBy: { rating: DESC }
) {
# graphql: list the fields from the results to return
id
title
genre
description
}
}
query MoviesByReleaseYear($min: Int, $max: Int) {
movies(where: {releaseYear: {le: $max, ge: $min}}, orderBy: [{releaseYear: ASC}]) { … }
}
โอเปอเรเตอร์ limit
และ offset
(คำค้นหาแบบเอกพจน์และพหูพจน์)
คุณแบ่งหน้าผลการค้นหาได้ ระบบยอมรับอาร์กิวเมนต์เหล่านี้ แต่ไม่แสดงในผลลัพธ์
query MoviesTop10 {
movies(orderBy: [{ rating: DESC }], limit: 10) {
# graphql: list the fields from the results to return
title
}
}
รวมสำหรับช่องอาร์เรย์
คุณสามารถทดสอบว่าช่องอาร์เรย์มีรายการที่ระบุ
# Filter using arrays and embedded fields.
query ListMoviesByTag($tag: String!) {
movies(where: { tags: { includes: $tag }}) {
# graphql: list the fields from the results to return
id
title
}
}
การดำเนินการกับสตริงและนิพจน์ทั่วไป
การค้นหาของคุณสามารถใช้การดำเนินการค้นหาและเปรียบเทียบสตริงทั่วไป รวมถึงนิพจน์ทั่วไป โปรดทราบว่าคุณกำลังรวมการดำเนินการหลายรายการเข้าด้วยกันเพื่อเพิ่มประสิทธิภาพ และทำให้การดำเนินการเหล่านั้นชัดเจนขึ้นด้วยการใช้อีเมลแทน
query MoviesTitleSearch($prefix: String, $suffix: String, $contained: String, $regex: String) {
prefixed: movies(where: {title: {startsWith: $prefix}}) {...}
suffixed: movies(where: {title: {endsWith: $suffix}}) {...}
contained: movies(where: {title: {contains: $contained}}) {...}
matchRegex: movies(where: {title: {pattern: {regex: $regex}}}) {...}
}
or
และ and
สำหรับตัวกรองแบบคอมโพส
ใช้ or
และ and
สำหรับตรรกะที่ซับซ้อนมากขึ้น
query ListMoviesByGenreAndGenre($minRating: Int!, $genre: String) {
movies(
where: { _or: [{ rating: { ge: $minRating } }, { genre: { eq: $genre } }] }
) {
# graphql: list the fields from the results to return
title
}
}
การค้นหาที่ซับซ้อน
การค้นหา Data Connect สามารถเข้าถึงข้อมูลตามความสัมพันธ์ระหว่างตาราง คุณสามารถใช้ความสัมพันธ์แบบออบเจ็กต์ (แบบ 1:1) หรืออาร์เรย์ (แบบ 1:หลายรายการ) ที่กําหนดไว้ในสคีมาเพื่อสร้างการค้นหาที่ฝังอยู่ เช่น ดึงข้อมูลสําหรับประเภทหนึ่งพร้อมกับข้อมูลจากประเภทที่ฝังอยู่หรือที่เกี่ยวข้อง
การค้นหาดังกล่าวใช้ไวยากรณ์ Data Connect _on_
และ _via
ที่เป็นค่าเริ่มต้นในการค้นหาโดยนัยที่สร้างขึ้น
คุณจะทำการเปลี่ยนแปลงสคีมาจากเวอร์ชันเริ่มต้น
หลายรายการต่อรายการเดียว
มาเพิ่มรีวิวลงในแอปกันโดยใช้ตาราง Review
และการแก้ไข User
# Users
# Suppose a user can leave reviews for movies
# user:reviews is a one to many relationship,
# movie:reviews is a one to many relationship,
# movie:user is a many to many relationship
type User
@table(name: "Users", singular: "user", plural: "users", key: ["id"]) {
id: UUID! @col(name: "user_id") @default(expr: "uuidV4()")
auth: String @col(name: "user_auth") @default(expr: "auth.uid")
username: String! @col(name: "username", dataType: "varchar(30)")
# The following are generated from the @ref in the Review table
# reviews_on_user
# movies_via_Review
}
# Reviews
type Review @table(name: "Reviews", key: ["movie", "user"]) {
id: UUID! @col(name: "review_id") @default(expr: "uuidV4()")
user: User! @ref
movie: Movie! @ref
rating: Int
reviewText: String
reviewDate: Date! @default(expr: "request.time")
}
การค้นหาแบบหลายรายการต่อรายการ
มาดูคําค้นหาที่มีการเปลี่ยนชื่อเพื่อแสดงไวยากรณ์ _via_
query UserMoviePreferences($username: String!) @auth(level: USER) {
users(where: { username: { eq: $username } }) {
likedMovies: movies_via_review(where: { rating: { ge: 4 } }) {
title
genre
description
}
dislikedMovies: movies_via_review(where: { rating: { le: 2 } }) {
title
genre
description
}
}
}
แบบตัวต่อตัว
คุณจะเห็นรูปแบบ ด้านล่างนี้เป็นสคีมาที่มีการแก้ไขเพื่อแสดงภาพ
# Movies
type Movie
@table(name: "Movies", singular: "movie", plural: "movies", key: ["id"]) {
id: UUID! @col(name: "movie_id") @default(expr: "uuidV4()")
title: String!
releaseYear: Int @col(name: "release_year")
genre: String
rating: Int @col(name: "rating")
description: String @col(name: "description")
tags: [String] @col(name: "tags")
}
# Movie Metadata
# Movie - MovieMetadata is a one-to-one relationship
type MovieMetadata
@table(
name: "MovieMetadata"
) {
# @ref creates a field in the current table (MovieMetadata) that holds the primary key of the referenced type
# In this case, @ref(fields: "id") is implied
movie: Movie! @ref
# movieId: UUID <- this is created by the above @ref
director: String @col(name: "director")
}
extend type MovieMetadata {
movieId: UUID! # matches primary key of referenced type
...
}
extend type Movie {
movieMetadata: MovieMetadata # can only be non-nullable on ref side
# conflict-free name, always generated
movieMetadatas_on_movie: MovieMetadata
}
ข้อความค้นหาแบบตัวต่อตัว
คุณค้นหาโดยใช้ไวยากรณ์ _on_
ได้
# One to one
query GetMovieMetadata($id: UUID!) @auth(level: PUBLIC) {
movie(id: $id) {
movieMetadatas_on_movie {
director
}
}
}
หลายต่อหลาย
ภาพยนตร์ต้องมีนักแสดง และนักแสดงต้องมีภาพยนตร์ ตารางเหล่านี้มีความสัมพันธ์แบบหลายต่อหลายที่คุณจำลองได้ด้วยMovieActors
ตารางการรวม
# MovieActors Join Table Definition
type MovieActors @table(
key: ["movie", "actor"] # join key triggers many-to-many generation
) {
movie: Movie!
actor: Actor!
}
# generated extensions for the MovieActors join table
extend type MovieActors {
movieId: UUID!
actorId: UUID!
}
# Extensions for Actor and Movie to handle many-to-many relationships
extend type Movie {
movieActors: [MovieActors!]! # standard many-to-one relation to join table
actors: [Actor!]! # many-to-many via join table
movieActors_on_actor: [MovieActors!]!
# since MovieActors joins distinct types, type name alone is sufficiently precise
actors_via_MovieActors: [Actor!]!
}
extend type Actor {
movieActors: [MovieActors!]! # standard many-to-one relation to join table
movies: [Movie!]! # many-to-many via join table
movieActors_on_movie: [MovieActors!]!
movies_via_MovieActors: [Movie!]!
}
การค้นหาแบบหลายต่อหลาย
มาดูตัวอย่างคําค้นหาที่มีการเปลี่ยนชื่อเพื่อแสดงไวยากรณ์ _via_
query GetMovieCast($movieId: UUID!, $actorId: UUID!) @auth(level: PUBLIC) {
movie(id: $movieId) {
mainActors: actors_via_MovieActor(where: { role: { eq: "main" } }) {
name
}
supportingActors: actors_via_MovieActor(
where: { role: { eq: "supporting" } }
) {
name
}
}
actor(id: $actorId) {
mainRoles: movies_via_MovieActor(where: { role: { eq: "main" } }) {
title
}
supportingRoles: movies_via_MovieActor(
where: { role: { eq: "supporting" } }
) {
title
}
}
}
การกลายพันธุ์สําหรับฐานข้อมูลรีวิวภาพยนตร์
ดังที่ได้กล่าวไว้ เมื่อคุณกําหนดตารางในสคีมา Data Connect จะสร้างการกลายพันธุ์โดยนัยพื้นฐานสําหรับแต่ละตาราง
type Movie @table { ... }
extend type Mutation {
# Insert a row into the movie table.
movie_insert(...): Movie_Key!
# Upsert a row into movie."
movie_upsert(...): Movie_Key!
# Update a row in Movie. Returns null if a row with the specified id/key does not exist
movie_update(...): Movie_Key
# Update rows based on a filter in Movie.
movie_updateMany(...): Int!
# Delete a single row in Movie. Returns null if a row with the specified id/key does not exist
movie_delete(...): Movie_Key
# Delete rows based on a filter in Movie.
movie_deleteMany(...): Int!
}
ซึ่งจะช่วยให้คุณใช้เคส CRUD หลักที่ซับซ้อนมากขึ้นได้ พูดเร็วๆ 5 ครั้ง
คำสั่ง @transaction
คำสั่งนี้บังคับให้การดำเนินการเปลี่ยนแปลงทำงานในธุรกรรมฐานข้อมูลเสมอ
การกลายพันธุ์ที่มี @transaction
รับประกันว่าจะสำเร็จหรือล้มเหลวโดยสมบูรณ์ หากช่องใดในธุรกรรมดำเนินการไม่สำเร็จ ระบบจะเปลี่ยนธุรกรรมทั้งหมดกลับ จากมุมมองไคลเอ็นต์ ความล้มเหลวใดๆ จะทํางานเหมือนกับว่าคําขอทั้งหมดไม่สําเร็จเนื่องจากมีข้อผิดพลาดเกี่ยวกับคําขอและยังไม่ได้เริ่มการดําเนินการ
การดัดแปลงที่ไม่มี @transaction
จะเรียกใช้ช่องรูทแต่ละช่องตามลำดับ ระบบจะแสดงข้อผิดพลาดเป็นข้อผิดพลาดของฟิลด์บางส่วน แต่ไม่แสดงผลกระทบของการดำเนินการที่ตามมา
สร้าง
มาสร้างแบบพื้นฐานกัน
# Create a movie based on user input
mutation CreateMovie($title: String!, $releaseYear: Int!, $genre: String!, $rating: Int!) {
movie_insert(data: {
title: $title
releaseYear: $releaseYear
genre: $genre
rating: $rating
})
}
# Create a movie with default values
mutation CreateMovie2 {
movie_insert(data: {
title: "Sherlock Holmes"
releaseYear: 2009
genre: "Mystery"
rating: 5
})
}
หรือเป็นการเพิ่ม
# Movie upsert using combination of variables and literals
mutation UpsertMovie($title: String!) {
movie_upsert(data: {
title: $title
releaseYear: 2009
genre: "Mystery"
rating: 5
genre: "Mystery/Thriller"
})
}
ทำการอัปเดต
ข้อมูลอัปเดตมีดังนี้ โปรดิวเซอร์และผู้กำกับย่อมหวังว่าเรตติ้งเฉลี่ยเหล่านั้นจะเพิ่มขึ้น
mutation UpdateMovie(
$id: UUID!,
$genre: String!,
$rating: Int!,
$description: String!
) {
movie_update(id: $id, data: {
genre: $genre
rating: $rating
description: $description
})
}
# Multiple updates (increase all ratings of a genre)
mutation IncreaseRatingForGenre($genre: String!, $ratingIncrement: Int!) {
movie_updateMany(
where: { genre: { eq: $genre } },
update: { rating: { inc: $ratingIncrement } }
)
}
ดำเนินการลบ
แน่นอนว่าคุณลบข้อมูลภาพยนตร์ได้ ผู้อนุรักษ์ภาพยนตร์ย่อมต้องการคงสภาพภาพยนตร์ให้อยู่ได้นานที่สุด
# Delete by key
mutation DeleteMovie($id: UUID!) {
movie_delete(id: $id)
}
คุณสามารถใช้ _deleteMany
ได้ที่นี่
# Multiple deletes
mutation DeleteUnpopularMovies($minRating: Int!) {
movie_deleteMany(where: { rating: { le: $minRating } })
}
เขียนการกลายพันธุ์ในความสัมพันธ์
ดูวิธีใช้การดัดแปลง _upsert
ที่ไม่ชัดแจ้งในความสัมพันธ์
# Create or update a one to one relation
mutation MovieMetadataUpsert($movieId: UUID!, $director: String!) {
movieMetadata_upsert(
data: { movie: { id: $movieId }, director: $director }
)
}
การค้นหาข้อมูลการให้สิทธิ์
Data Connect คุณสามารถให้สิทธิ์การดัดแปลงได้โดยเริ่มจากการค้นหาฐานข้อมูลและยืนยันผลลัพธ์ของการค้นหาด้วยนิพจน์ CEL ซึ่งจะเป็นประโยชน์เมื่อคุณเขียนลงในตารางและต้องการตรวจสอบเนื้อหาของแถวในตารางอื่น
ฟีเจอร์นี้รองรับรายการต่อไปนี้
- คำสั่ง
@check
ซึ่งช่วยให้คุณประเมินเนื้อหาของช่อง และดำเนินการต่อไปนี้ตามผลลัพธ์ของการประเมินดังกล่าว- ดำเนินการสร้าง อัปเดต และลบตามที่การกลายพันธุ์ระบุ
- ใช้ค่าที่การค้นหาแสดงผลไปยังไคลเอ็นต์เพื่อดำเนินการตามตรรกะต่างๆ ในไคลเอ็นต์
- คำสั่ง
@redact
ซึ่งช่วยให้คุณละเว้นผลการค้นหาจากโปรโตคอลการเดินสายจากผลการค้นหาได้
ฟีเจอร์เหล่านี้มีประโยชน์สําหรับขั้นตอนการให้สิทธิ์
สคีมา SQL ที่เทียบเท่า
-- Movies Table
CREATE TABLE Movies (
movie_id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
title VARCHAR(255) NOT NULL,
release_year INT,
genre VARCHAR(30),
rating INT,
description TEXT,
tags TEXT[]
);
-- Movie Metadata Table
CREATE TABLE MovieMetadata (
movie_id UUID REFERENCES Movies(movie_id) UNIQUE,
director VARCHAR(255) NOT NULL,
PRIMARY KEY (movie_id)
);
-- Actors Table
CREATE TABLE Actors (
actor_id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
name VARCHAR(30) NOT NULL
);
-- MovieActor Join Table for Many-to-Many Relationship
CREATE TABLE MovieActor (
movie_id UUID REFERENCES Movies(movie_id),
actor_id UUID REFERENCES Actors(actor_id),
role VARCHAR(50) NOT NULL, # "main" or "supporting"
PRIMARY KEY (movie_id, actor_id),
FOREIGN KEY (movie_id) REFERENCES Movies(movie_id),
FOREIGN KEY (actor_id) REFERENCES Actors(actor_id)
);
-- Users Table
CREATE TABLE Users (
user_id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
user_auth VARCHAR(255) NOT NULL
username VARCHAR(30) NOT NULL
);
-- Reviews Table
CREATE TABLE Reviews (
review_id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
user_id UUID REFERENCES Users(user_id),
movie_id UUID REFERENCES Movies(movie_id),
rating INT,
review_text TEXT,
review_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE (movie_id, user_id)
FOREIGN KEY (user_id) REFERENCES Users(user_id),
FOREIGN KEY (movie_id) REFERENCES Movies(movie_id)
);
-- Self Join Example for Movie Sequel Relationship
ALTER TABLE Movies
ADD COLUMN sequel_to UUID REFERENCES Movies(movie_id);
ขั้นตอนถัดไปคือ
- ดูวิธีทำให้การค้นหาและการดัดแปลงข้อมูลปลอดภัยด้วยการให้สิทธิ์และการรับรอง
- ดูวิธีเรียกคําค้นหาและการดัดแปลงจาก Web SDK, Android SDK, iOS SDK และ Flutter SDK ที่สร้างขึ้นโดยอัตโนมัติ