สคีมา การค้นหา และการกลายพันธุ์ของ Data Connect

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

หมายเหตุ: ระบบจะไม่จัดเก็บข้อมูลเขตเวลาท้องถิ่น
PostgreSQL จะแปลงและจัดเก็บการประทับเวลาดังกล่าวเป็น UTC

เวกเตอร์ กำหนดเอง vector

เวกเตอร์

ดูทำการค้นหาความคล้ายคลึงของเวกเตอร์ด้วย Vertex AI

  • 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);

ขั้นตอนถัดไปคือ