สคีมา การค้นหา และการกลายพันธุ์ของ 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 ข้อความ ข้อความ
บิต(n), varbit(n)
char(n), varchar(n)
Int GraphQL int Int2 (เล็ก, ซีเรียลเล็ก),
int4 (จำนวนเต็ม, int, อนุกรม)
ทศนิยม กราฟ QL Float 8 Float4 (จริง)
Float8 (ความแม่นยำ 2 เท่า)
ตัวเลข (ทศนิยม)
บูลีน กราฟ QL boolean boolean
UUID กำหนดเอง uuid uuid
Int64 กำหนดเอง Bigint int8 (bigint, bigserial)
numeric (decimal)
วันที่ กำหนดเอง วันที่ วันที่
การประทับเวลา กำหนดเอง timestamptz

timestamptz

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

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

เวกเตอร์

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

  • GraphQL List จะแมปกับอาร์เรย์ 1 มิติ
    • เช่น [Int] แมปกับ int5[], [Any] แมปกับ jsonb[]
    • Data Connect ไม่รองรับอาร์เรย์ที่ซ้อนกัน

การค้นหาและการดัดแปลงแบบนัยและที่กําหนดไว้ล่วงหน้า

ข้อความค้นหาและการกลายพันธุ์ Data Connect ของคุณจะขยายชุดของ implicit การค้นหาและการเปลี่ยนแปลงโดยนัยที่ Data Connect สร้างขึ้นโดยอิงจาก ประเภทและความสัมพันธ์ของประเภทในสคีมา ข้อความค้นหาที่ไม่เจาะจงและการเปลี่ยนแปลง สร้างขึ้นโดยเครื่องมือภายในเมื่อใดก็ตามที่คุณแก้ไขสคีมา

ในกระบวนการพัฒนา คุณจะใช้การค้นหาที่กำหนดไว้ล่วงหน้าและ การกลายพันธุ์ที่กำหนดไว้ล่วงหน้าจากการดำเนินการโดยนัยเหล่านี้

การค้นหาโดยนัยและการตั้งชื่อการเปลี่ยนแปลง

Data Connect จะอนุมานชื่อที่เหมาะสมสำหรับการค้นหาและการดัดแปลงโดยนัยจากประกาศประเภทสคีมา เช่น การทำงานกับ PostgreSQL ถ้าคุณกำหนดตารางชื่อ Movie เซิร์ฟเวอร์จะสร้าง implicit:

  • คำค้นหาสำหรับ Use Case ตารางเดียวที่มีชื่อเรียกได้ง่าย movie (คำเอกพจน์ สำหรับการเรียกข้อมูลผลลัพธ์แต่ละรายการที่ส่งผ่านอาร์กิวเมนต์ เช่น eq) และ movies (คำพหูพจน์ ในการเรียกข้อมูลรายการผลลัพธ์ ที่ส่งอาร์กิวเมนต์ เช่น gt และการดำเนินการเช่น orderby) Data Connect ยังสร้างการค้นหาสำหรับหลายตาราง การดำเนินการเชิงสัมพันธ์ที่มีชื่ออย่างชัดแจ้ง เช่น actors_on_movies หรือ actors_via_actormovie
  • การกลายพันธุ์ชื่อ movie_insert, movie_upsert...

ภาษาคําจํากัดความสคีมายังช่วยให้คุณกําหนดชื่อการดำเนินการได้อย่างชัดเจนโดยใช้อาร์กิวเมนต์คําสั่ง singular และ plural

การค้นหาฐานข้อมูลรีวิวภาพยนตร์

คุณกำหนดการค้นหา 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
  }
}

include สำหรับฟิลด์อาร์เรย์

คุณสามารถทดสอบว่าช่องอาร์เรย์มีรายการที่ระบุ

# 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 ที่เป็นค่าเริ่มต้นในการค้นหาโดยนัยที่สร้างขึ้น

คุณจะทำการเปลี่ยนแปลงสคีมาจากเวอร์ชันเริ่มต้น

หลายต่อ 1

มาเพิ่มรีวิวในแอปของเราด้วยตาราง 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")
}

ค้นหาหลายรายการต่อ 1 รายการ

ตอนนี้เราจะมาดูคำค้นหาพร้อมชื่อแทน เพื่อแสดงไวยากรณ์ _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
}

การค้นหาสำหรับ 1 ต่อ 1

คุณค้นหาได้โดยใช้ไวยากรณ์ _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 ครั้ง

สร้าง

มาเริ่มสร้างผลงานแบบพื้นฐานกัน

# 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 }
  )
}

สคีมา 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);

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

  • ดูวิธีเรียกใช้คำค้นหาและการกลายพันธุ์จากที่ระบบสร้างขึ้นโดยอัตโนมัติ SDK ของเว็บ, Android SDK, iOS SDK และ Flutter SDK