โค้ดไคลเอ็นต์สามารถสมัครรับข้อมูลการค้นหาเพื่อรับข้อมูลอัปเดตแบบเรียลไทม์เมื่อผลการค้นหาเปลี่ยนแปลง
ก่อนเริ่มต้น
ตั้งค่าการสร้าง SDK สำหรับโปรเจ็กต์ตามที่อธิบายไว้ในเอกสารประกอบสำหรับ เว็บ, แพลตฟอร์ม Apple และ Flutter
- คุณต้องเปิดใช้การแคชฝั่งไคลเอ็นต์สำหรับ SDK ทั้งหมดที่สร้างขึ้น โดยเฉพาะอย่างยิ่ง การกำหนดค่า SDK ทุกรายการต้องมีการประกาศลักษณะต่อไปนี้
clientCache: maxAge: 5s storage: ... # Optional.ไคลเอ็นต์แอปต้องใช้ SDK หลักของ SQL Connect เวอร์ชันล่าสุด
- Apple: Firebase SQL Connect SDK สำหรับ Swift เวอร์ชัน 11.12.0 ขึ้นไป
- เว็บ: JavaScript SDK เวอร์ชัน 12.12.0 ขึ้นไป
- Flutter:
firebase_data_connectเวอร์ชัน 0.3.0 ขึ้นไป
สร้าง SDK ไคลเอ็นต์ใหม่โดยใช้ Firebase CLI เวอร์ชัน 15.14.0 ขึ้นไป
การสมัครรับข้อมูลผลการค้นหา
คุณสามารถสมัครรับข้อมูลการค้นหาเพื่อตอบสนองต่อการเปลี่ยนแปลงในผลการค้นหา ตัวอย่างเช่น สมมติว่าคุณมีสคีมาและการดำเนินการต่อไปนี้ที่กำหนดไว้ในโปรเจ็กต์
# dataconnect/schema/schema.gql
type Movie @table(key: "id") {
id: UUID! @default(expr: "uuidV4()")
title: String!
releaseYear: Int
genre: String
description: String
averageRating: Int
}
# dataconnect/connector/operations.gql
query GetMovieById($id: UUID!) @auth(level: PUBLIC) {
movie(id: $id) {
id
title
releaseYear
genre
description
}
}
mutation UpdateMovie(
$id: UUID!,
$genre: String!,
$description: String!
) {
movie_update(id: $id,
data: {
genre: $genre
description: $description
})
}
วิธีสมัครรับข้อมูลการเปลี่ยนแปลงในผลการเรียกใช้ GetMovieById
เว็บ
import { subscribe, DataConnectError, QueryResult } from 'firebase/data-connect';
import { getMovieByIdRef, GetMovieByIdData, GetMovieByIdVariables } from '@dataconnect/generated';
const queryRef = getMovieByIdRef({ id: "<MOVIE_ID>" });
// Called when receiving an update.
const onNext = (result: QueryResult<GetMovieByIdData, GetMovieByIdVariables>) => {
console.log("Movie <MOVIE_ID> updated", result);
}
const onError = (err?: DataConnectError) => {
console.error("received error", err);
}
// Called when unsubscribing or when the subscription is automatically released.
const onComplete = () => {
console.log("subscription complete!");
}
const unsubscribe = subscribe(queryRef, onNext, onError, onComplete);
เว็บ (React)
import { subscribe, QueryResult } from 'firebase/data-connect';
import { getMovieByIdRef, GetMovieByIdData, GetMovieByIdVariables } from '@dataconnect/generated';
import { useState, useEffect } from "react";
export const MovieInfo = ({ id: movieId }: { id: string }) => {
const [movieInfo, setMovieInfo] = useState<GetMovieByIdData>();
const [loading, setLoading] = useState(true);
const [error, setError] = useState<Error | null>(null);
useEffect(() => {
const queryRef = getMovieByIdRef({ id: movieId });
function updateUi(result: QueryResult<GetMovieByIdData, GetMovieByIdVariables>): void {
setMovieInfo(result.data);
setLoading(false);
}
const unsubscribe = subscribe(
queryRef,
updateUi,
(err) => {
setError(err ?? new Error("Unknown error occurred"));
setLoading(false);
}
);
return () => unsubscribe();
}, [movieId]);
if (loading)
return <div>Loading movie details...</div>;
if (error || !movieInfo || !movieInfo.movie)
return <div>Error loading movie details: {error?.message}</div>;
return (
<div>
<h2>{movieInfo.movie.title} ({movieInfo.movie.releaseYear})</h2>
<ul>
<li>Genre: {movieInfo.movie.genre}</li>
<li>Description: {movieInfo.movie.description}</li>
</ul>
</div>
);
};
SQL Connect ยังรองรับการแคชและการสมัครรับข้อมูลแบบเรียลไทม์โดยใช้
TanStack เมื่อคุณระบุ react: true หรือ angular: true ในไฟล์
connector.yaml ของคุณ SQL Connect จะสร้างการเชื่อมโยงสำหรับ React หรือ Angular โดยใช้ TanStack
การเชื่อมโยงเหล่านี้สามารถทำงานควบคู่ไปกับการรองรับแบบเรียลไทม์ในตัวของ SQL Connect ได้ แต่จะทำได้ค่อนข้าง ยาก เราขอแนะนำให้คุณใช้การเชื่อมโยงที่อิงตาม TanStack หรือ SQL Connectการรองรับแบบเรียลไทม์ในตัวของ's แต่ อย่างใดอย่างหนึ่ง
โปรดทราบว่าการใช้งานแบบเรียลไทม์ของ SQL Connect's เองมีข้อดีบางประการเหนือการเชื่อมโยง TanStack ดังนี้
- การแคชแบบปกติ: SQL Connect ใช้ การแคชแบบปกติ, ซึ่งช่วยปรับปรุงความสอดคล้องของข้อมูล รวมถึงประสิทธิภาพของหน่วยความจำและเครือข่าย เมื่อเทียบกับการแคชระดับการค้นหา การแคชแบบปกติจะทำให้เอนทิตีอัปเดตในส่วนหนึ่งของแอปและอัปเดตในส่วนอื่นๆ ที่ใช้เอนทิตีนั้นด้วย
- การทำให้เป็นโมฆะจากระยะไกล: SQL Connect สามารถทำให้เอนทิตีที่แคชไว้ เป็นโมฆะจากระยะไกลในอุปกรณ์ทั้งหมดที่สมัครรับข้อมูล
หากเลือกที่จะไม่ใช้ TanStack คุณควรนำการตั้งค่า react: true และ angular: true ออกจากไฟล์ connector.yaml
iOS
struct MovieDetailsView: View {
// QueryRef has the @Observable annotation, so its properties will
// automatically trigger updates on changes.
// Realtime subscriptions will keep the query results updated with changes.
// Define the ref variable.
// If parameters are known before hand, refs can be initialized here directly
// else they can be initialized in the init for the view, like here
@State private var queryRef: GetMovieByIdQuery.Ref
// Store the handle to unsubscribe from query updates.
// QueryRef can be used in multiple views.
// Each view can separately subscribe / unsubscribe to updates
// When there are no more subscribers to a QueryRef,
// it will cancel automatic updates for that QueryRef.
@State private var querySub: AnyCancellable?
init(movieId: String) {
// initialize the ref with the movieId
queryRef = DataConnect.moviesConnector.getMovieByIdQuery.ref(movieId: movieId)
}
var body: some View {
VStack {
// Use the query results in a View.
if let movie = queryRef.data?.movie {
Text(movie.title)
Text(mpvie.description)
// other details
} else {
// if last fetch/update resulted in an error
if error = queryRef.lastError {
Text("Error loading movie")
} else {
Text("Loading movie ...")
}
}
}
.onAppear {
// Subscribe to the query for updates using the Observable macro.
Task {
do {
querySub = try await queryRef.subscribe().sink { _ in }
} catch {
print("Error subscribing to query: \(error)")
}
}
}
.onDisappear {
// Calling cancel will unsubscribe from receiving updates.
querySub?.cancel()
}
}
}
Flutter
นำเข้า SDK ที่สร้างขึ้นของโปรเจ็กต์
import 'package:flutter_app/dataconnect_generated/generated.dart';
จากนั้นเรียกใช้เมธอด subscribe() ในข้อมูลอ้างอิงการค้นหา
final queryRef = MovieConnector.instance.getMovieById(id: "<MOVIE_ID>").ref();
final subscription = queryRef.subscribe().listen((result) {
final movie = result.data.movie;
if (movie != null) {
// Execute your logic to update the UI with the refreshed movie information.
updateUi(movie.title);
}
});
หากต้องการหยุดข้อมูลอัปเดต คุณสามารถเรียกใช้ subscription.cancel()
เมื่อสมัครรับข้อมูลการค้นหาตามตัวอย่างก่อนหน้าแล้ว คุณจะได้รับข้อมูลอัปเดตเมื่อใดก็ตามที่ผลการค้นหาเฉพาะเปลี่ยนแปลง ตัวอย่างเช่น หากไคลเอ็นต์อื่นดำเนินการกลายพันธุ์ UpdateMovie ในรหัสเดียวกันกับที่คุณสมัครรับข้อมูล คุณจะได้รับข้อมูลอัปเดต
สัญญาณรีเฟรชการค้นหาโดยนัย
ในตัวอย่างก่อนหน้า คุณสามารถสมัครรับข้อมูลการค้นหาและรับข้อมูลอัปเดตแบบเรียลไทม์ได้โดยไม่ต้องแก้ไขการดำเนินการเพิ่มเติม โดยเฉพาะอย่างยิ่ง คุณไม่จำเป็นต้องระบุว่าการกลายพันธุ์ UpdateMovie อาจส่งผลต่อผลการค้นหา GetMovieById
ซึ่งเป็นไปได้เนื่องจากการค้นหา GetMovieById จะได้รับสัญญาณรีเฟรชโดยนัยจากการกลายพันธุ์ UpdateMovie ระบบจะส่งสัญญาณรีเฟรชโดยนัยระหว่างการค้นหาและการกลายพันธุ์บางส่วนที่คุณอาจเขียน
หากการค้นหา ทำการค้นหาเอนทิตีเดียวด้วยคีย์หลัก แล้ว การกลายพันธุ์ ใดก็ตามที่เขียนไปยังเอนทิตีเดียวกัน ซึ่งระบุด้วยคีย์หลัก ก็จะทริกเกอร์สัญญาณรีเฟรชโดยนัย
_insertและ_insertMany_upsertและ_upsertMany_update_delete
_deleteMany และ _updateMany จะไม่ส่งสัญญาณรีเฟรช
ในตัวอย่างก่อนหน้า การค้นหา GetMovieById จะค้นหาภาพยนตร์เรื่องเดียวตามรหัส (movie(id: $id)) และการกลายพันธุ์ UpdateMovie จะอัปเดตภาพยนตร์เรื่องเดียวที่ระบุตามรหัส (movie_update(id: $id, ...) ดังนั้นการค้นหาจึงใช้ประโยชน์จากการรีเฟรชโดยนัยได้
การดำเนินการแทรกและอัปเดตสามารถทริกเกอร์สัญญาณรีเฟรชโดยนัยได้เมื่อคุณ ใช้ค่าที่ทราบ เช่น UID ของผู้ใช้ Firebase Authentication
ตัวอย่างเช่น พิจารณาการค้นหาต่อไปนี้
query GetExtendedProfileByUser @auth(level: USER) {
profile(key: { id_expr: "auth.uid" }) {
id
status
photoUrl
socialLink
}
}
การค้นหาจะได้รับสัญญาณรีเฟรชโดยนัยจากการกลายพันธุ์ต่อไปนี้
mutation UpsertExtendedProfile($status: String, $photoUrl: String, $socialLink: String) @auth(level: USER) {
profile_upsert(
data: {
id_expr: "auth.uid"
status: $status
photoUrl: $photoUrl
socialLink: $socialLink
}
) {
id
status
photoUrl
socialLink
}
}
เมื่อการค้นหาหรือการกลายพันธุ์มีความซับซ้อนมากขึ้น คุณจะต้องระบุเงื่อนไขที่ต้องมีการรีเฟรชการค้นหา โปรดดูวิธีดำเนินการในส่วนถัดไป
สัญญาณรีเฟรชการค้นหาอย่างชัดแจ้ง
นอกเหนือจากสัญญาณรีเฟรชที่การกลายพันธุ์ส่งไปยังการค้นหาโดยนัยแล้ว คุณยังระบุอย่างชัดแจ้งได้ด้วยว่าการค้นหาควรได้รับสัญญาณรีเฟรชเมื่อใด โดยใส่คำอธิบายประกอบในการค้นหาด้วย Directive @refresh
คุณต้องใช้ Directive @refresh ทุกครั้งที่การค้นหาไม่เป็นไปตามเกณฑ์เฉพาะ (ดูด้านบน) สำหรับการรีเฟรชอัตโนมัติ ตัวอย่างการค้นหาที่ต้องมี Directive นี้ ได้แก่
- การค้นหาที่ดึงข้อมูลรายการเอนทิตี
- การค้นหาที่ทำการรวมตารางอื่นๆ
- การค้นหาการรวบรวม
- การค้นหาที่ใช้ SQL ดั้งเดิม
- การค้นหาที่ใช้ ตัวแก้ปัญหาที่กำหนดเอง
คุณระบุนโยบายรีเฟรชได้ 2 วิธีดังนี้
ช่วงเวลาตามเวลา
รีเฟรชการค้นหาตามช่วงเวลาที่กำหนด
ตัวอย่างเช่น ฐานผู้ใช้ที่ใช้งานอยู่มากอาจทำให้การให้คะแนนสะสมของภาพยนตร์อัปเดตหลายครั้งทุกนาที โดยเฉพาะอย่างยิ่งหลังจากที่ภาพยนตร์ออกฉาย แทนที่จะรีเฟรชการค้นหาทุกครั้งที่การให้คะแนนเปลี่ยนแปลง คุณอาจรีเฟรชการค้นหาทุกๆ 2-3 วินาทีเพื่อรับข้อมูลอัปเดตที่แสดงผลสะสมของการกลายพันธุ์หลายรายการ
# dataconnect/connector/operations.gql
query GetMovieRating($id: UUID!) @auth(level: PUBLIC) @refresh(every: {seconds: 30}) {
movie(id: $id) {
id
averageRating
}
}
การดำเนินการกลายพันธุ์
รีเฟรชการค้นหาเมื่อมีการดำเนินการกลายพันธุ์ที่เฉพาะเจาะจง วิธีนี้จะระบุอย่างชัดแจ้งว่าการกลายพันธุ์ใดบ้างที่อาจเปลี่ยนผลการค้นหา
ตัวอย่างเช่น สมมติว่าคุณมีการค้นหาที่ดึงข้อมูลเกี่ยวกับภาพยนตร์หลายเรื่องแทนที่จะเป็นภาพยนตร์เรื่องใดเรื่องหนึ่ง การค้นหานี้ควรรีเฟรชเมื่อใดก็ตามที่การกลายพันธุ์อัปเดตระเบียนภาพยนตร์รายการใดรายการหนึ่ง
query ListMovies($offset: Int)
@auth(level: PUBLIC, insecureReason: "Anyone can list all movies.")
@refresh(onMutationExecuted: { operation: "UpdateMovie" }) {
movies(limit: 10, offset: $offset) {
id
title
releaseYear
genre
description
}
}
นอกจากนี้ คุณยังระบุเงื่อนไขนิพจน์ CEL ที่ต้องเป็นจริงเพื่อให้การกลายพันธุ์ทริกเกอร์การรีเฟรชการค้นหาได้ด้วย
เราขอแนะนำให้ดำเนินการดังกล่าว ยิ่งคุณระบุเงื่อนไขได้อย่างแม่นยำมากเท่าใด ระบบก็จะใช้ทรัพยากรฐานข้อมูลที่ไม่จำเป็นน้อยลง และแอปของคุณก็จะตอบสนองได้ดียิ่งขึ้น
ตัวอย่างเช่น สมมติว่าคุณมีการค้นหาที่แสดงภาพยนตร์เฉพาะในประเภทที่ระบุ การค้นหานี้ควรรีเฟรชเฉพาะเมื่อการกลายพันธุ์อัปเดตภาพยนตร์ในประเภทเดียวกัน
query ListMoviesByGenre($genre: String, $offset: Int)
@auth(level: PUBLIC, insecureReason: "Anyone can list movies.")
@refresh(onMutationExecuted: {
operation: "UpdateMovie",
condition: "request.variables.genre == mutation.variables.genre"
}) {
movies(
where: { genre: { eq: $genre } },
limit: 10,
offset: $offset) {
id
title
releaseYear
genre
description
}
}
การเชื่อมโยง CEL ในเงื่อนไข @refresh
นิพจน์ condition ใน onMutationExecuted มีสิทธิ์เข้าถึง 2 บริบท ได้แก่
request
สถานะของการค้นหาที่สมัครรับข้อมูล
| การเชื่อมโยง | คำอธิบาย |
|---|---|
request.variables |
ตัวแปรที่ส่งไปยังการค้นหา (เช่น request.variables.id) |
request.auth.uid |
Firebase Authentication UID ของผู้ใช้ที่ดำเนินการค้นหา |
request.auth.token |
พจนานุกรมการอ้างสิทธิ์โทเค็น Firebase Authentication สำหรับผู้ใช้ที่ดำเนินการค้นหา |
mutation
สถานะของการกลายพันธุ์ที่ดำเนินการ
| การเชื่อมโยง | คำอธิบาย |
|---|---|
mutation.variables |
ตัวแปรที่ส่งไปยังการกลายพันธุ์ (เช่น mutation.variables.movieId) |
mutation.auth.uid |
Firebase Authentication UID ของผู้ใช้ที่ดำเนินการกลายพันธุ์ |
mutation.auth.token |
พจนานุกรมการอ้างสิทธิ์โทเค็น Firebase Authentication สำหรับผู้ใช้ที่ดำเนินการกลายพันธุ์ |
รูปแบบทั่วไป
# Refresh only when the mutation targets the same entity
"request.variables.id == mutation.variables.id"
# Refresh only when the same user who subscribed makes a change
"request.auth.uid == mutation.auth.uid"
# Refresh when a specific field value matches a condition
"request.auth.uid == mutation.auth.uid && mutation.variables.status == 'PUBLISHED'"
# Refresh when a specific flag is set in the mutation
"mutation.variables.isPublic == true"
Directive @refresh หลายรายการ
คุณสามารถระบุ Directive @refresh หลายครั้งในการค้นหาเพื่อทริกเกอร์การรีเฟรชเมื่อใดก็ตามที่มีเกณฑ์ใดเกณฑ์หนึ่งที่ระบุโดย Directive @refresh รายการใดรายการหนึ่งเป็นจริง
ตัวอย่างเช่น การค้นหาต่อไปนี้จะรีเฟรชทุกๆ 30 วินาที รวมถึงเมื่อใดก็ตามที่มีการดำเนินการกลายพันธุ์รายการใดรายการหนึ่งที่ระบุ
query ListMovies($offset: Int)
@auth(level: PUBLIC, insecureReason: "Anyone can list all movies.")
@refresh(every: {seconds: 30})
@refresh(onMutationExecuted: { operation: "UpdateMovie" })
@refresh(onMutationExecuted: { operation: "BulkUpdateMovies" }) {
movies(limit: 10, offset: $offset) {
id
title
releaseYear
genre
description
}
}
ข้อมูลอ้างอิง
ดูตัวอย่างเพิ่มเติมได้ที่ข้อมูลอ้างอิง Directive @refresh