Firebase Data Connect cho phép bạn tạo trình kết nối cho các phiên bản PostgreSQL được quản lý bằng Google Cloud SQL. Các trình kết nối này là sự kết hợp của một giản đồ, truy vấn và đột biến để sử dụng dữ liệu của bạn.
Hướng dẫn bắt đầu đã giới thiệu giản đồ ứng dụng đánh giá phim cho PostgreSQL và hướng dẫn này sẽ xem xét kỹ hơn về cách thiết kế giản đồ Data Connect cho PostgreSQL.
Hướng dẫn này sẽ ghép nối các truy vấn và đột biến Data Connect với các ví dụ về giản đồ. Tại sao chúng ta lại thảo luận về truy vấn (và sự thay đổi) trong hướng dẫn về schema Data Connect? Giống như các nền tảng dựa trên GraphQL khác, Firebase Data Connect là nền tảng phát triển ưu tiên truy vấn. Vì vậy, là một nhà phát triển, khi lập mô hình dữ liệu, bạn sẽ suy nghĩ về dữ liệu mà khách hàng cần. Điều này sẽ ảnh hưởng lớn đến giản đồ dữ liệu bạn phát triển cho dự án của mình.
Hướng dẫn này bắt đầu bằng một giản đồ về bài đánh giá phim mới, sau đó đề cập đến các truy vấn và đột biến lấy từ giản đồ đó, cuối cùng cung cấp một trang thông tin SQL tương đương với giản đồ Data Connect chính.
Sơ đồ cho ứng dụng đánh giá phim
Hãy tưởng tượng bạn muốn xây dựng một dịch vụ cho phép người dùng gửi và xem bài đánh giá về phim.
Bạn cần có một giản đồ ban đầu cho ứng dụng như vậy. Bạn sẽ mở rộng giản đồ này sau đó để tạo các truy vấn quan hệ phức tạp.
Bảng phim
Giản đồ cho Phim chứa các lệnh cốt lõi như:
@table
, cho phép chúng ta đặt tên cho toán tử bằng cách sử dụng đối sốsingular
vàplural
@col
để đặt tên cột một cách rõ ràng@default
để cho phép đặt giá trị mặc định.
# 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")
}
Giá trị máy chủ và đại lượng vô hướng khoá
Trước khi xem ứng dụng đánh giá phim, hãy giới thiệu các giá trị máy chủ và đại lượng vô hướng khoá Data Connect.
Khi sử dụng giá trị máy chủ, bạn có thể cho phép máy chủ tự động điền các trường trong bảng bằng cách sử dụng các giá trị đã lưu trữ hoặc dễ dàng tính toán theo các biểu thức cụ thể phía máy chủ. Ví dụ: bạn có thể xác định một trường có dấu thời gian được áp dụng khi truy cập vào trường đó bằng biểu thức updatedAt: Timestamp! @default(expr: "request.time")
.
Đại lượng vô hướng chính là giá trị nhận dạng đối tượng ngắn gọn mà Data Connect tự động ghép nối từ các trường chính trong giản đồ. Các đại lượng vô hướng chính xoay quanh tính hiệu quả, cho phép bạn tìm thấy thông tin về danh tính và cấu trúc của dữ liệu trong một lệnh gọi. Các khoá này đặc biệt hữu ích khi bạn muốn thực hiện các thao tác tuần tự trên các bản ghi mới và cần một giá trị nhận dạng duy nhất để truyền đến các thao tác sắp tới, cũng như khi bạn muốn truy cập vào các khoá quan hệ để thực hiện các thao tác phức tạp hơn.
Bảng siêu dữ liệu của phim
Bây giờ, hãy theo dõi các đạo diễn phim, cũng như thiết lập mối quan hệ một với một với Movie
.
Thêm lệnh @ref
để xác định mối quan hệ.
# 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 và MovieActor
Tiếp theo, bạn muốn diễn viên đóng vai chính trong phim của mình và vì bạn có mối quan hệ nhiều với nhiều giữa phim và diễn viên, hãy tạo một bảng chung.
# 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
}
Người dùng
Cuối cùng là người dùng ứng dụng.
# 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
}
Các loại dữ liệu được hỗ trợ
Data Connect hỗ trợ các loại dữ liệu vô hướng sau đây, với các giá trị gán cho các loại PostgreSQL bằng @col(dataType:)
.
Loại Data Connect | Loại tích hợp sẵn GraphQL hoặc loại tuỳ chỉnh Data Connect |
Loại PostgreSQL mặc định | Các loại PostgreSQL được hỗ trợ (email đại diện trong dấu ngoặc đơn) |
---|---|---|---|
Chuỗi | GraphQL | văn bản | text bit(n), varbit(n) char(n), varchar(n) |
Int | GraphQL | int | Int2 (smallint, Smallserial), int4 (số nguyên, int, sê-ri) |
Nổi | GraphQL | float8 | float4 (thực) float8 (độ chính xác kép) số (thập phân) |
Boolean | GraphQL | boolean | boolean |
mã nhận dạng duy nhất (UUID) | Tuỳ chỉnh | uuid | uuid |
Int64 | Tuỳ chỉnh | bigint | int8 (số lớn, số lớn) số (thập phân) |
Ngày | Tuỳ chỉnh | ngày | ngày |
Dấu thời gian | Tuỳ chỉnh | timestamptz | timestamptz Lưu ý: Thông tin về múi giờ tại địa phương không được lưu trữ. |
Vectơ | Tuỳ chỉnh | vectơ | vectơ |
- GraphQL
List
liên kết đến một mảng một chiều.- Ví dụ:
[Int]
liên kết đếnint5[]
,[Any]
liên kết đếnjsonb[]
. - Data Connect không hỗ trợ mảng lồng nhau.
- Ví dụ:
Các truy vấn và đột biến ngầm ẩn và được xác định trước
Các truy vấn và đột biến Data Connect sẽ mở rộng một tập hợp truy vấn ngầm ẩn và đột biến ngầm ẩn do Data Connect tạo ra dựa trên các loại và mối quan hệ loại trong giản đồ. Các truy vấn ngầm ẩn và các phép biến đổi được tạo bằng công cụ cục bộ mỗi khi bạn chỉnh sửa giản đồ.
Trong quá trình phát triển, bạn sẽ triển khai các truy vấn được xác định trước và các đột biến được xác định trước dựa trên các thao tác ngầm ẩn này.
Tên truy vấn và đột biến ngầm ẩn
Data Connect suy ra tên phù hợp cho các truy vấn ngầm ẩn và các trường hợp đột biến từ nội dung khai báo kiểu giản đồ. Ví dụ: khi làm việc với nguồn PostgreSQL, nếu bạn xác định một bảng có tên là Movie
, máy chủ sẽ tạo ngầm ẩn:
- Truy vấn cho các trường hợp sử dụng bảng đơn có tên thân thiện
movie
(số ít, để truy xuất kết quả riêng lẻ truyền các đối số nhưeq
) vàmovies
(số nhiều, để truy xuất danh sách kết quả truyền các đối số nhưgt
và các thao tác nhưorderby
). Data Connect cũng tạo truy vấn cho các thao tác đa bảng, quy chiếu có tên rõ ràng nhưactors_on_movies
hoặcactors_via_actormovie
. - Các đột biến có tên là
movie_insert
,movie_upsert
...
Ngôn ngữ định nghĩa giản đồ cũng cho phép bạn đặt tên rõ ràng cho các thao tác bằng cách sử dụng đối số lệnh singular
và plural
.
Truy vấn cho cơ sở dữ liệu bài đánh giá phim
Bạn xác định truy vấn Data Connect có nội dung khai báo loại thao tác truy vấn, tên thao tác, không hoặc nhiều đối số toán tử và không có hoặc nhiều lệnh có đối số.
Trong phần hướng dẫn nhanh, truy vấn listEmails
mẫu không có tham số nào. Tất nhiên, trong nhiều trường hợp, dữ liệu được chuyển đến các trường truy vấn sẽ là dữ liệu động. Bạn có thể sử dụng cú pháp $variableName
để xử lý các biến dưới dạng một trong các thành phần của định nghĩa truy vấn.
Vì vậy, truy vấn sau đây có:
- Định nghĩa kiểu
query
- Tên toán tử (truy vấn)
ListMoviesByGenre
- Một đối số thao tác
$genre
biến - Một lệnh duy nhất,
@auth
.
query ListMoviesByGenre($genre: String!) @auth(level: USER)
Mỗi đối số truy vấn đều yêu cầu một nội dung khai báo kiểu, một nội dung tích hợp sẵn như String
hoặc một kiểu tuỳ chỉnh, do giản đồ xác định như Movie
.
Hãy xem chữ ký của những truy vấn ngày càng phức tạp. Cuối cùng, bạn sẽ giới thiệu các biểu thức mối quan hệ mạnh mẽ, súc tích có trong các truy vấn ngầm ẩn mà bạn có thể xây dựng trong các truy vấn được xác định trước.
Số thực chính trong truy vấn
Nhưng trước tiên, hãy lưu ý về đại lượng vô hướng chính.
Data Connect xác định một loại đặc biệt cho các đại lượng vô hướng khoá, được xác định bằng _Key
. Ví dụ: loại của một đại lượng vô hướng khoá cho bảng Movie
là Movie_Key
.
Bạn truy xuất các đại lượng vô hướng khoá dưới dạng phản hồi được trả về bởi hầu hết các trường hợp đột biến ngầm ẩn, hoặc tất nhiên là từ các truy vấn mà bạn đã truy xuất tất cả các trường cần thiết để tạo khoá vô hướng.
Các truy vấn tự động số ít, chẳng hạn như movie
trong ví dụ đang chạy, hỗ trợ đối số khoá chấp nhận một đại lượng vô hướng khoá.
Bạn có thể truyền một đại lượng vô hướng khoá ở dạng giá trị cố định. Tuy nhiên, bạn có thể xác định các biến để truyền các đại lượng vô hướng của khoá làm dữ liệu đầu vào.
query GetMovie($myKey: Movie_Key!) {
movie(key: $myKey) { title }
}
Bạn có thể cung cấp các thông tin này trong yêu cầu JSON như sau (hoặc các định dạng chuyển đổi tuần tự khác):
{
# …
"variables": {
"myKey": {"foo": "some-string-value", "bar": 42}
}
}
Nhờ khả năng phân tích cú pháp vô hướng tuỳ chỉnh, bạn cũng có thể tạo Movie_Key
bằng cách sử dụng cú pháp đối tượng có thể chứa các biến. Điều này chủ yếu hữu ích khi bạn muốn chia các thành phần riêng lẻ thành các biến khác nhau vì một số lý do.
Bí danh trong truy vấn
Data Connect hỗ trợ việc gán bí danh GraphQL trong các truy vấn. Với bí danh, bạn đổi tên dữ liệu được trả về trong kết quả của truy vấn. Một truy vấn Data Connect có thể áp dụng nhiều bộ lọc hoặc các thao tác truy vấn khác trong một yêu cầu hiệu quả đến máy chủ, đồng thời phát hành một số "truy vấn phụ" một cách hiệu quả. Để tránh xung đột tên trong tập dữ liệu được trả về, bạn sử dụng bí danh để phân biệt các truy vấn phụ.
Dưới đây là một truy vấn trong đó biểu thức sử dụng bí danh mostPopular
.
query ReviewTopPopularity($genre: String) {
mostPopular: review(first: {
where: {genre: {eq: $genre}},
orderBy: {popularity: DESC}
}) { … }
}
Truy vấn đơn giản có bộ lọc
Truy vấn Data Connect liên kết với tất cả bộ lọc SQL và thao tác thứ tự phổ biến.
Toán tử where
và orderBy
(truy vấn số ít, số nhiều)
Trả về tất cả các hàng đã so khớp trong bảng (và các mối liên kết lồng nhau). Trả về một mảng trống nếu không có bản ghi nào khớp với bộ lọc.
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}]) { … }
}
Toán tử limit
và offset
(truy vấn số ít, số nhiều)
Bạn có thể phân trang kết quả. Các đối số này được chấp nhận nhưng không được trả về trong kết quả.
query MoviesTop10 {
movies(orderBy: [{ rating: DESC }], limit: 10) {
# graphql: list the fields from the results to return
title
}
}
bao gồm các trường mảng
Bạn có thể kiểm tra để đảm bảo rằng một trường mảng có chứa một mục được chỉ định.
# 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
}
}
Thao tác với chuỗi và biểu thức chính quy
Truy vấn của bạn có thể sử dụng các thao tác so sánh và tìm kiếm chuỗi thông thường, bao gồm cả biểu thức chính quy. Lưu ý để đảm bảo tính hiệu quả, bạn đang gói một số hoạt động ở đây và phân biệt các hoạt động đó bằng các bí danh.
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
và and
cho bộ lọc tổng hợp
Sử dụng or
và and
cho logic phức tạp hơn.
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
}
}
Câu lệnh truy vấn phức tạp
Truy vấn Data Connect có thể truy cập dữ liệu dựa trên mối quan hệ giữa các bảng. Bạn có thể sử dụng mối quan hệ đối tượng (một với một) hoặc mảng (một với nhiều) được xác định trong giản đồ để tạo truy vấn lồng nhau, tức là tìm nạp dữ liệu cho một loại cùng với dữ liệu từ một loại lồng nhau hoặc có liên quan.
Các truy vấn như vậy sử dụng cú pháp Data Connect _on_
và _via
thần kỳ trong các truy vấn ngầm ẩn đã tạo.
Bạn sẽ sửa đổi giản đồ từ phiên bản ban đầu của chúng tôi.
Nhiều với một
Hãy thêm bài đánh giá vào ứng dụng của chúng ta, với bảng Review
và các sửa đổi đối với 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")
}
Truy vấn nhiều với một
Bây giờ, hãy xem truy vấn có bí danh để minh hoạ cú pháp _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
}
}
}
Một với một
Bạn có thể thấy mẫu này. Dưới đây là giản đồ được sửa đổi để minh hoạ.
# 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
}
Truy vấn một với một
Bạn có thể truy vấn bằng cú pháp _on_
.
# One to one
query GetMovieMetadata($id: UUID!) @auth(level: PUBLIC) {
movie(id: $id) {
movieMetadatas_on_movie {
director
}
}
}
Nhiều với nhiều
Phim cần có diễn viên và diễn viên cần phim. Các lớp này có mối quan hệ nhiều với nhiều mà bạn có thể lập mô hình bằng bảng nối 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!]!
}
Truy vấn nhiều với nhiều
Hãy xem một truy vấn có sử dụng bí danh để minh hoạ cú pháp _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
}
}
}
Các đột biến cho cơ sở dữ liệu bài đánh giá phim
Như đã đề cập, khi bạn xác định một bảng trong giản đồ, Data Connect sẽ tạo các đột biến ngầm ẩn cơ bản cho mỗi bảng.
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!
}
Với những kiến thức này, bạn có thể triển khai các trường hợp CRUD cốt lõi ngày càng phức tạp. Hãy nói nhanh năm lần!
Tạo
Hãy tạo các thành phần cơ bản.
# 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
})
}
Hoặc một thao tác chèn mới.
# 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"
})
}
Thực hiện cập nhật
Sau đây là thông tin cập nhật. Nhà sản xuất và đạo diễn chắc chắn hy vọng những điểm xếp hạng trung bình đó sẽ tăng lên.
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 } }
)
}
Thực hiện thao tác xoá
Tất nhiên, bạn có thể xoá dữ liệu phim. Những người bảo tồn phim chắc chắn sẽ muốn duy trì phim vật lý lâu nhất có thể.
# Delete by key
mutation DeleteMovie($id: UUID!) {
movie_delete(id: $id)
}
Tại đây, bạn có thể sử dụng _deleteMany
.
# Multiple deletes
mutation DeleteUnpopularMovies($minRating: Int!) {
movie_deleteMany(where: { rating: { le: $minRating } })
}
Viết đột biến về quan hệ
Quan sát cách sử dụng đột biến _upsert
ngầm ẩn trên một mối quan hệ.
# Create or update a one to one relation
mutation MovieMetadataUpsert($movieId: UUID!, $director: String!) {
movieMetadata_upsert(
data: { movie: { id: $movieId }, director: $director }
)
}
Sơ đồ SQL tương đương
-- 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);
Tiếp theo là gì?
- Tìm hiểu cách gọi truy vấn và đột biến từ một SDK web, SDK Android, SDK iOS và SDK Flutter được tạo tự động.