Data Connect-Schemas, -Abfragen und -Mutationen

Mit Firebase Data Connect können Sie Connectors für Ihre mit Google Cloud SQL verwalteten PostgreSQL-Instanzen erstellen. Diese Connectors sind Kombinationen ein Schema, Abfragen und Mutationen zur Verwendung Ihrer Daten.

Im Startleitfaden wurde eine Filmrezension eingeführt. Anwendungsschema für PostgreSQL. In diesem Leitfaden erfahren Sie mehr darüber, wie Sie Data Connect-Schemas für PostgreSQL.

In diesem Leitfaden werden Data Connect-Abfragen und ‑Mutationen mit Schemabeispielen kombiniert. Warum sollten Sie Abfragen (und Mutationen) in einem Leitfaden zu Data Connect Schemas? Wie andere GraphQL-basierte Plattformen ist Firebase Data Connect eine Abfrage-first-Entwicklungsplattform. Als Entwickler müssen Sie bei der Datenmodellierung also an die Daten denken, die Ihre Kunden benötigen. Das hat großen Einfluss auf das Datenschema, das Sie für Ihr Projekt entwickeln.

Dieser Leitfaden beginnt mit einem neuen Schema für Filmrezensionen. behandelt dann die Abfragen und Mutationen und liefert eine SQL-Auflistung entspricht dem Data Connect-Kernschema.

Schema für eine App mit Filmrezensionen

Angenommen, Sie möchten einen Dienst erstellen, mit dem Nutzer Filmrezensionen einreichen und ansehen können.

Für eine solche App benötigen Sie ein Anfangsschema. Dieses Schema erweitern Sie später, um komplexe relationale Abfragen zu erstellen.

Filmtabelle

Das Schema für Movies enthält Kernanweisungen wie:

  • @table, mit dem wir Vorgangsnamen mithilfe der singular festlegen können und plural Argumente
  • @col, um Spaltennamen explizit festzulegen
  • @default, damit Standardeinstellungen festgelegt werden können.
# 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")
}

Serverwerte und Schlüsselskalare

Bevor wir uns die Filmbewertungs-App ansehen, stellen wir Data ConnectServerwerte und Schlüsselskalare vor.

Mit Serverwerten können Sie Felder in Ihren Tabellen dynamisch mit gespeicherten oder leicht berechenbaren Werten gemäß bestimmten serverseitigen Ausdrücken befüllen lassen. Sie können beispielsweise ein Feld mit einem Zeitstempel definieren, der beim Zugriff auf das Feld mit dem Ausdruck updatedAt: Timestamp! @default(expr: "request.time") angewendet wird.

Schlüsselskalare sind präzise Objektkennungen, die Data Connect automatisch aus Schlüsselfeldern in Ihren Schemas zusammenstellt. Bei wichtigen Skalaren geht es um Effizienz, So finden Sie in einem einzigen Aufruf Informationen zur Identität und Struktur Ihrer Daten. Sie sind besonders nützlich, wenn Sie sequenziellen Aktionen für neue Datensätze ausgeführt werden und für die eine eindeutige ID zur Übergabe an für anstehende Vorgänge und für den Zugriff auf relationale Schlüssel und komplexere Operationen durchführen.

Tabelle mit Filmmetadaten

Sehen wir uns nun an, wie wir Filmregisseure im Blick behalten und eine persönliche Beziehung zu Movie aufbauen.

Fügen Sie die Anweisung @ref hinzu, um Beziehungen zu definieren.

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

Schauspieler und FilmActor

Als Nächstes möchten Sie, dass Schauspieler in Ihren Filmen eine Hauptrolle spielen, Beziehung zwischen Filmen und Schauspielern erklärt haben, erstellen Sie eine Join-Tabelle.

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

Nutzer

Zuletzt die Nutzer Ihrer App.

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

Unterstützte Datentypen

Data Connect unterstützt die folgenden skalaren Datentypen, wobei Zuweisungen zu PostgreSQL-Typen mit @col(dataType:).

Data Connect-Typ Integrierter GraphQL-Typ oder
Data Connect benutzerdefinierter Typ
Standard-PostgreSQL-Typ Unterstützte PostgreSQL-Typen
(Alias in Klammern)
String GraphQL Text SMS senden
Bit(n), varbit(n)
char(n), varchar(n)
Integer GraphQL int Int2 (smallint, smallserial),
int4 (Ganzzahl, Ganzzahl, seriell)
Float GraphQL float8 Fließkommazahl4 (real)
Gleitkommazahl8 (doppelte Genauigkeit)
numeric (Dezimalzahl)
Boolesch GraphQL boolean boolean
UUID Benutzerdefiniert uuid uuid
INT64 Benutzerdefiniert bigint int8 (bigint, Bigserial)
numeric (Dezimalzahl)
Datum Benutzerdefiniert Datum Datum
Zeitstempel Benutzerdefiniert Zeitstempel

timestamptz

Hinweis:Informationen zur lokalen Zeitzone werden nicht gespeichert.
PostgreSQL konvertiert und speichert Zeitstempel als UTC.

Vektor Benutzerdefiniert Vektor

Vektor

Weitere Informationen finden Sie unter Mit Vertex AI nach Vektorähnlichkeiten suchen.

  • GraphQL List wird einem eindimensionalen Array zugeordnet.
    • Beispielsweise ist [Int] der int5[] zugeordnet, [Any] der Ressource jsonb[].
    • Data Connect unterstützt keine verschachtelten Arrays.

Implizite und vordefinierte Abfragen und Mutationen

Ihre Data Connect-Abfragen und -Mutationen erweitern eine Reihe von impliziten Abfragen und impliziten Mutationen, die von Data Connect basierend auf die Typen und Typbeziehungen in Ihrem Schema. Implizite Abfragen und Mutationen werden von lokalen Tools generiert, wenn Sie Ihr Schema bearbeiten.

Im Rahmen Ihres Entwicklungsprozesses implementieren Sie vordefinierte Abfragen und vordefinierte Mutationen basierend auf diesen impliziten Vorgängen.

Namensgebung für implizite Abfragen und Mutationen

Data Connect leitet geeignete Namen für implizite Abfragen und Mutationen ab aus Ihren Schematypdeklarationen. Wenn Sie z. B. mit einer PostgreSQL-Datenbank Quelle: Wenn Sie eine Tabelle mit dem Namen Movie definieren, generiert der Server implizit:

  • Abfragen für Anwendungsfälle mit einer einzelnen Tabelle mit den Aliasnamen movie (Singular, zum Abrufen einzelner Ergebnisse mithilfe von Argumenten wie eq) und movies (Plural, zum Abrufen von Ergebnislisten mithilfe von Argumenten wie gt und Vorgängen wie orderby). Data Connect generiert auch Abfragen für relationale Vorgänge mit mehreren Tabellen mit expliziten Namen wie actors_on_movies oder actors_via_actormovie.
  • Mutationen mit den Namen movie_insert, movie_upsert...

Mit der Schemadefinition können Sie auch Namen für Vorgänge explizit mithilfe von singular- und plural-Direktivargumenten festlegen.

Abfragen für die Datenbank mit Filmrezensionen

Sie definieren eine Data Connect-Abfrage mit einem Abfragevorgangstyp. Deklaration, Vorgangsname, null oder mehr Vorgangsargumente und null oder mehr mit Argumenten.

In der Kurzanleitung wurden für die listEmails-Beispielabfrage keine Parameter verwendet. Natürlich In vielen Fällen sind die an Abfragefelder übergebenen Daten dynamisch. Sie können $variableName-Syntax verwenden, um mit Variablen als Komponenten einer Abfragedefinition.

Die folgende Abfrage hat also:

  • Eine Definition des Typs query
  • Name eines ListMoviesByGenre-Vorgangs (Abfrage)
  • Ein $genre-Vorgangsargument einer einzelnen Variablen
  • Eine einzelne Anweisung, @auth.
query ListMoviesByGenre($genre: String!) @auth(level: USER)

Für jedes Abfrageargument ist eine Typdeklaration erforderlich, z. B. ein vordefinierter Typ wie String oder ein benutzerdefinierter, schemadefinierter Typ wie Movie.

Sehen wir uns die Signatur zunehmend komplexer Abfragen an. Ihr Ziel endet am mit leistungsstarken, prägnanten Beziehungsausdrücken, die in impliziten Abfragen, die Sie in Ihren vordefinierten Abfragen nutzen können.

Schlüsselskalare in Abfragen

Aber zuerst ein Hinweis zu den Schlüsselskalaren.

Data Connect definiert einen speziellen Typ für Schlüsselskalare, der durch _Key. Der Typ eines Schlüsselskalaars für unsere Tabelle Movie ist beispielsweise Movie_Key.

Sie rufen Schlüsselskalare als Antwort ab, die von den meisten impliziten Mutationen zurückgegeben wird, oder aus Abfragen, mit denen Sie alle Felder abgerufen haben, den Skalarschlüssel.

Einzelne automatische Abfragen, wie movie in unserem laufenden Beispiel, unterstützen einen Schlüssel -Argument, das einen Schlüsselskalar akzeptiert.

Sie können einen Schlüsselskalar als Literal übergeben. Sie können jedoch Variablen definieren, Schlüsselskalaren als Eingabe verwenden.

query GetMovie($myKey: Movie_Key!) {
  movie(key: $myKey) { title }
}

Diese können in JSON-Anfragen wie diesem (oder einer anderen Serialisierungsmethode) angegeben werden. Formate):

{
  # …
  "variables": {
    "myKey": {"foo": "some-string-value", "bar": 42}
  }
}

Dank benutzerdefinierter Skalarparsierung kann ein Movie_Key auch mit der Objektsyntax erstellt werden, die Variablen enthalten kann. Das ist vor allem dann nützlich, wenn Sie einzelne Komponenten aus irgendeinem Grund in verschiedene Variablen aufteilen möchten.

Aliasse in Abfragen

Data Connect unterstützt das Aliasing von GraphQL in Abfragen. Mit Aliassen können Sie die Daten umbenennen, die in den Ergebnissen einer Abfrage zurückgegeben werden. Mit einer einzelnen Data Connect-Abfrage können mehrere Filter oder andere Abfragevorgänge in einer einzigen effizienten Anfrage an den Server angewendet werden, wodurch mehrere „Unterabfragen“ gleichzeitig ausgeführt werden. Um Namenskonflikte im zurückgegebenen Dataset zu vermeiden, Aliasse verwenden, um die Unterabfragen zu unterscheiden.

Hier sehen Sie eine Abfrage, bei der ein Ausdruck den Alias mostPopular verwendet.

query ReviewTopPopularity($genre: String) {
  mostPopular: review(first: {
    where: {genre: {eq: $genre}},
    orderBy: {popularity: DESC}
  }) { … }
}

Einfache Abfragen mit Filtern

Data Connect-Abfragen sind allen gängigen SQL-Filtern und -Reihenfolgen zugeordnet Geschäftsabläufe.

where- und orderBy-Operatoren (Singular- und Pluralabfragen)

Gibt alle übereinstimmenden Zeilen aus der Tabelle (und verschachtelten Verknüpfungen) zurück. Gibt ein leeres Array zurück, wenn keine Datensätze mit dem Filter übereinstimmen.

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- und offset-Operatoren (Singular- und Pluralabfragen)

Sie können die Ergebnisse paginieren. Diese Argumente werden akzeptiert, aber nicht in Ergebnissen zurückgegeben.

query MoviesTop10 {
  movies(orderBy: [{ rating: DESC }], limit: 10) {
    # graphql: list the fields from the results to return
    title
  }
}

„includes“ für Arrayfelder

Sie können prüfen, ob ein Arrayfeld ein bestimmtes Element enthält.

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

String-Operationen und reguläre Ausdrücke

Für Abfragen können typische Stringsuch- und Vergleichsvorgänge verwendet werden, einschließlich regulärer Ausdrücke. Hinweis: Aus Effizienzgründen bündeln Sie hier mehrere Vorgänge und trennen sie mit Aliasen.

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 und and für zusammengesetzte Filter

Verwenden Sie or und and für eine komplexere Logik.

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

Komplexe Abfragen

Data Connect-Abfragen können auf Daten zugreifen, die auf den Beziehungen zwischen Tabellen basieren. Sie können Objektbeziehungen (1:1) oder Array-Beziehungen (1:n) verwenden. die in Ihrem Schema definiert sind, um verschachtelte Abfragen zu erstellen, d.h. Daten für einen Typ abzurufen aus einem verschachtelten oder verwandten Typ.

Solche Abfragen verwenden die magische Data Connect-, _on_- und _via-Syntax in impliziten Abfragen generiert.

Sie nehmen Änderungen am Schema vor, das auf unserer ursprünglichen Version basiert.

Viele-zu-Eins

Fügen wir unserer App nun Rezensionen mit einer Review-Tabelle und Änderungen an User hinzu.

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

Abfrage für viele zu eins

Sehen wir uns nun eine Abfrage mit Aliasing an, um die _via_-Syntax zu veranschaulichen.

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

1:1

Sie erkennen das Muster. Unten ist das Schema zur Veranschaulichung geändert.

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

Abfrage für Einzelgespräch

Sie können Abfragen mit der Syntax _on_ ausführen.

# One to one
query GetMovieMetadata($id: UUID!) @auth(level: PUBLIC) {
  movie(id: $id) {
    movieMetadatas_on_movie {
      director
    }
  }
}

n:n

Filme brauchen Schauspieler und Schauspieler brauchen Filme. Sie stehen in einer m:n-Beziehung, die Sie mit einer MovieActors-Join-Tabelle modellieren können.

# 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!]!
}

Abfrage für „Viele zu viele“

Sehen wir uns eine Abfrage mit Alias an, um die _via_-Syntax zu veranschaulichen.

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

Mutationen für die Datenbank für Filmrezensionen

Wie bereits erwähnt, generiert Data Connect beim Definieren einer Tabelle in Ihrem Schema grundlegende implizite Mutationen für jede Tabelle.

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!
}

Damit können Sie immer komplexere CRUD-Grundfälle implementieren. Sag das fünfmal schnell!

Erstellen

Beginnen wir mit der grundlegenden Erstellung.

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

oder ein Upsert.

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

Aktualisierungen durchführen

Hier sind die neuesten Informationen. Produzenten und Regisseure hoffen natürlich, dass diese durchschnittlichen Bewertungen dem Trend entsprechen.

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

Löschen

Sie können Filmdaten natürlich auch löschen. Filmarchivare möchten die physischen Filme natürlich so lange wie möglich erhalten.

# Delete by key
mutation DeleteMovie($id: UUID!) {
  movie_delete(id: $id)
}

Hier können Sie _deleteMany verwenden.

# Multiple deletes
mutation DeleteUnpopularMovies($minRating: Int!) {
  movie_deleteMany(where: { rating: { le: $minRating } })
}

Mutationen in Beziehungen schreiben

Hier sehen Sie, wie die implizite _upsert-Mutation auf eine Beziehung angewendet wird.

# Create or update a one to one relation
mutation MovieMetadataUpsert($movieId: UUID!, $director: String!) {
  movieMetadata_upsert(
    data: { movie: { id: $movieId }, director: $director }
  )
}

Äquivalentes SQL-Schema

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

Nächste Schritte