المخططات وطلبات البحث وعمليات التغيير في 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 تلقائيًا من الحقول الرئيسية في المخططات. تتعلّق المقاييس الأساسية بالكفاءة، ما يتيح لك العثور في مكالمة واحدة على معلومات حول هوية بياناتك وبنيتها. وتكون هذه المعرّفات مفيدة بشكل خاص عندما تريد تنفيذ إجراءات متسلسلة على سجلات جديدة وتحتاج إلى معرّف فريد لتمريره إلى العمليات القادمة، وكذلك عندما تريد الوصول إلى مفاتيح ربط لتنفيذ عمليات إضافية أكثر تعقيدًا.

نوع المعرّف

في GraphQL، يتم تعريف النوع ID كنوع غير شفاف يتم تسلسله كسلسلة. لا يفرض GraphQL أي قيود على تنسيق المعرّف، ولكنّه سيحوّل السلاسل والأعداد الصحيحة من الإدخال.

تكون مفاتيح PostgreSQL عادةً أعدادًا صحيحة أو معرّفات فريدة عالمية (UUID)، وليست سلاسل. تنشئ Data Connect هذه المفاتيح تلقائيًا من المخطط الخاص بك. يمكنك تخصيص عملية إنشاء المفاتيح باستخدام التوجيه @default، كما هو موضّح في تعريف الحقل id في جدول Actor: id: ID! … @default(generate: "UUID").

جدول البيانات الوصفية للأفلام

لنتابع الآن المدراء التنفيذيين للأفلام، بالإضافة إلى إعداد علاقة فردية مع 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 النوع نوع 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 (عدد فاصلة عشرية مزدوجة الدقة)
‫numeric (عدد عشري)
قيمة منطقية GraphQL قيمة منطقية قيمة منطقية
معرِّف فريد عالمي (UUID) مخصص uuid uuid
Int64 مخصص bigint int8 (bigint, bigserial)
numeric (decimal)
التاريخ مخصص date التاريخ
الطابع الزمني مخصص timestamptz

timestamptz

ملاحظة: لا يتم تخزين معلومات المنطقة الزمنية المحلية.
تحوّل PostgreSQL هذه الطوابع الزمنية وتخزِّنها بالتوقيت العالمي المنسَّق.

التعداد مخصص enum

تعداد

المتّجه مخصص vector

المتّجه

راجِع مقالة إجراء بحث عن التشابهات بين المتّجهات باستخدام Vertex AI.

  • تتم مطابقة GraphQL List مع صفيف أحادي البُعد.
    • على سبيل المثال، يتم ربط [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 طلب بحث باستخدام تعريف لنوع عملية طلب البحث، واسم العملية، ووسيطة واحدة أو أكثر للعملية، وتوجيه واحد أو أكثر مع وسيطات.

في دليل التشغيل السريع، لم يتضمّن طلب البحث 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 إلى البيانات استنادًا إلى العلاقات بين الجداول. يمكنك استخدام علاقات الكائن (واحد لواحد) أو المصفوفة (واحد لمتعدد) المحددة في المخطط الخاص بك لإنشاء استعلامات متداخلة، أي جلب البيانات لنوع واحد مع بيانات من نوع متداخل أو ذي صلة.

تستخدم طلبات البحث هذه بنية 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 الأساسية المعقّدة بشكل متزايد. حاول قول ذلك خمس مرات بسرعة!

إنشاء

لننفّذ عمليات إنشاء أساسية.

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

ما هي الخطوات التالية؟