2023年1月11日 星期三

Flutter學習-firebase Cloud Firestore說明(2)

翻譯自:https://firebase.flutter.dev/docs/firestore/usage/ 

Writing Data#

Firebase架構 多關Firestore

Typing CollectionReference and DocumentReference#

FirestoreMap<String, dynamic>使withConverterCollectionReference.addQuery.where使 使withConverter:

class Movie {
Movie({required this.title, required this.genre});
Movie.fromJson(Map<String, Object?> json)
: this(
title: json['title']! as String,
genre: json['genre']! as String,
);
final String title;
final String genre;
Map<String, Object?> toJson() {
return {
'title': title,
'genre': genre,
};
}
}

使 withConverter 集合,就像這樣:

final moviesRef = FirebaseFirestore.instance.collection('movies').withConverter<Movie>(
fromFirestore: (snapshot, _) => Movie.fromJson(snapshot.data()!),
toFirestore: (movie, _) => movie.toJson(),
);
Future<void> main() async {
//
List<QueryDocumentSnapshot<Movie>> movies = await moviesRef
.where('genre', isEqualTo: 'Sci-fi')
.get()
.then((snapshot) => snapshot.docs);
// Add a movie
await moviesRef.add(
Movie(
title: 'Star Wars: A New Hope (Episode IV)',
genre: 'Sci-fi'
),
);
/取得id為我的資料
Movie movie42 = await moviesRef.doc('42').get().then((snapshot) => snapshot.data()!);
}

Adding Documents#

使 CollectionReference add

class AddUser extends StatelessWidget {
final String fullName;
final String company;
final int age;
AddUser(this.fullName, this.company, this.age);
Widget build(BuildContext context) {
// usersCollectionReferencefirestore
CollectionReference users = FirebaseFirestore.instance.collection('users');
Future<void> addUser() {
// 使CollectionReference使
return users
.add({
'full_name': fullName, // John Doe
'company': company, // Stokes and Sons
'age': age // 42
})
.then((value) => print("User Added"))
.catchError((error) => print("Failed to add user: $error"));
}
return FlatButton(
onPressed: addUser,
child: Text(
"Add User",
),
);
}
}
CollectionReference users = FirebaseFirestore.instance.collection('users');
Future<void> addUser() {
return users
.doc('ABC123')
.set({
'full_name': "Mary Jane",
'age': 18
})
.then((value) => print("User Added"))
.catchError((error) => print("Failed to add user: $error"));
}
CollectionReference users = FirebaseFirestore.instance.collection('users');
Future<void> addUser() {
return users
// existing document in 'users' collection: "ABC123"
.doc('ABC123')
.set({
'full_name': "Mary Jane",
'age': 18
},
SetOptions(merge: true),
)
.then(
(value) => print("'full_name' & 'age' merged with existing data!")
)
.catchError((error) => print("Failed to merge data: $error"));
}

Updating documents#(更新文件資料)

setDocumentReference使update

CollectionReference users = FirebaseFirestore.instance.collection('users');
Future<void> updateUser() {
return users
.doc('ABC123')
.update({'company': 'Stokes and Sons'})
.then((value) => print("User Updated"))
.catchError((error) => print("Failed to update user: $error"));
}

CollectionReference users = FirebaseFirestore.instance.collection('users');
Future<void> updateUser() {
return users
.doc('ABC123')
.update({'info.address.zipcode': 90210})
.then((value) => print("User Updated"))
.catchError((error) => print("Failed to update user: $error"));
}

Field values#

Cloud FirestoreBlobBinary Large Object)和

GeoPoint

CollectionReference users = FirebaseFirestore.instance.collection('users');
Future<void> updateUser() {
return users
.doc('ABC123')
.update({'info.address.location': GeoPoint(53.483959, -2.244644)})
.then((value) => print("User Updated"))
.catchError((error) => print("Failed to update user: $error"));
}


的Blob是圖片的 Blob,請提供 Uint8List。下面的例子展示了如何從你的 assets 目錄取得一張圖片,並將它嵌入 Firestore 中的 info 物件中。


CollectionReference users = FirebaseFirestore.instance.collection('users');
Future<void> updateUser() {
return rootBundle
.load('assets/images/sample.jpg')
.then((bytes) => bytes.buffer.asUint8List())
.then((avatar) {
return users
.doc('ABC123')
.update({'info.avatar': Blob(avatar)});
})
.then((value) => print("User Updated"))
.catchError((error) => print("Failed to update user: $error"));
}

Removing Data#

使Cloud Firestoredelete

CollectionReference users = FirebaseFirestore.instance.collection('users');
Future<void> deleteUser() {
return users
.doc('ABC123')
.delete()
.then((value) => print("User Deleted"))
.catchError((error) => print("Failed to delete user: $error"));
}

使FieldValuedelete

CollectionReference users = FirebaseFirestore.instance.collection('users');
Future<void> deleteField() {
return users
.doc('ABC123')
.update({'age': FieldValue.delete()})
.then((value) => print("User's Property Deleted"))
.catchError((error) => print("Failed to delete user's property: $error"));
}

Transactions#

Transactions 是確保只使用伺服器上最新的資料進行寫入操作的一種方法。Transactions 絕不會部分應用寫入,並且在成功交易結束時執行寫入。 當您想要根據其當前值或其他字段的值更新字段時,Transactions 很有用。如果您想要在不使用文檔當前狀態的情況下寫入多個文檔,應該使用批量寫入。

使 Transactions 將會失敗,它們無法使用緩存數據

transaction的例子就是在一個應用程式中,一個用戶可以訂閱一個頻道。當用戶按下訂閱按鈕時,文件中的「訂閱者」字段會增加。如果不使用交易,我們就需要先讀取現有的值,然後使用兩個單獨的操作來進行增加。 在一個高流量的應用程式中,服務器上的值可能已經改變,直到寫入操作設置新值,導致數字不一致。 transaction可以消除這個問題,通過原子性地更新服務器的值。如果價值在交易執行期間改變,它將重試,確保服務器上的值被使用,而不是客戶端的值。 要執行交易,請調用runTransaction方法:

// Create a reference to the document the transaction will use
DocumentReference documentReference = FirebaseFirestore.instance
.collection('users')
.doc(documentId);
return FirebaseFirestore.instance.runTransaction((transaction) async {
// Get the document
DocumentSnapshot snapshot = await transaction.get(documentReference);
if (!snapshot.exists) {
throw Exception("User does not exist!");
}
//根據目前的count更新follower
//注意:可以通過使用FieldValue.increment()更新人口,而不使用transaction來完成此操作
int newFollowerCount = snapshot.data()['followers'] + 1;
//對文件進行更新
transaction.update(documentReference, {'followers': newFollowerCount});
// 回傳新的數量
return newFollowerCount;
})
.then((value) => print("Follower count updated to $value"))
.catchError((error) => print("Failed to update user followers: $error"));

transaction期間更改,則最多重試五次。 您不應該在交易中直接修改應用程序狀態,因為處理程序可能會多次執行。您應該在處理程序的末尾返回一個值,在交易完成後更新應用程序狀態。 如果在處理程序中拋出異常,則整個交易將被中止。

Batch write#

Firestore 使batch

CollectionReference users = FirebaseFirestore.instance.collection('users');
Future<void> batchDelete() {
WriteBatch batch = FirebaseFirestore.instance.batch();
return users.get().then((querySnapshot) {
querySnapshot.docs.forEach((document) {
batch.delete(document.reference);
});
return batch.commit();
});
}

Data Security#

FirebaseFirebase Firestore

Access Data Offline#

Configure Offline Persistence#


Cloud
FirestoreFirestore使使Cloud Firestore Firestore

// Web.
await FirebaseFirestore.instance.enablePersistence();
// All other platforms.
FirebaseFirestore.instance.settings =
Settings(persistenceEnabled: false);

clearPersistence()

await FirebaseFirestore.instance.clearPersistence();

使 Firestore Firestore

Configure Cache Size#

Firestore會將件設定Firestore使

//預設值為40 MB,門檻必須設定為至少1 MB,並可以設定為Settings.CACHE_SIZE_UNLIMITED 以停用垃圾收集。
FirebaseFirestore.instance.settings =
Settings(cacheSizeBytes: Settings.CACHE_SIZE_UNLIMITED);

Disable and Enable Network Access#

FirestoreFirestore

await FirebaseFirestore.instance.disableNetwork()

enableNetwork

await FirebaseFirestore.instance.enableNetwork()

Emulator Usage#

使Firestore使useFirestoreEmulatorFirebase FlutterFire使

Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
// Ideal time to initialize
FirebaseFirestore.instance.useFirestoreEmulator('localhost', 8080);
//...
}

Data Bundles#

// Use a package like 'http' to retrieve bundle.
import 'package:http/http.dart' as http;
final response = await http.get(url);
// Convert the 'bundle.txt' string in the response to an Uint8List instance.
Uint8List buffer = Uint8List.fromList(response.body.codeUnits);
// Load bundle into cache.
LoadBundleTask task = FirebaseFirestore.instance.loadBundle(buffer);
// Use .stream API to expose a stream which listens for LoadBundleTaskSnapshot events.
task.stream.listen((taskStateProgress) {
if(taskStateProgress.taskState == LoadBundleTaskState.success){
//bundle is loaded into app cache!
}
});
// If you do not wish to .listen() to the stream, but simply want to know when the bundle has been loaded. Use .last API:
await task.stream.last;
// Once bundle is loaded into cache, you may query for data by using the GetOptions() to specify data retrieval from cache.
QuerySnapshot<Map<String, Object?>> snapshot = await FirebaseFirestore.instance
.collection('cached-data')
.get(const GetOptions(source: Source.cache));

Named Query#

bundle (包),您可以通過調用namedQueryGet()API來檢索查詢快照(snapshot):

// Assuming you've loaded a data bundle that includes a named query called 'sports-teams':
// Query snapshot loaded from cache.
QuerySnapshot<Map<String, Object?>> snapshot = await FirebaseFirestore.instance.namedQueryGet('sports-teams', options: const GetOptions(source: Source.cache));

Distributed Counters#

1010於傳統計數器的寫入數量。 注意 另一個解決方案是使用Firebase擴展,請參閱Ditributed Counters擴展以及如何在此處設置。 一個分散的計數器集合看起來像這樣:

// counters/${ID}
class Counter {
final int numShards;
Counter(this.numShards);
}
// counters/${ID}/shards/${NUM}
class Shard {
final int count;
Shard(this.count);
}

Future<void> createCounter(DocumentReference ref, int numShards) async {
WriteBatch batch = FirebaseFirestore.instance.batch();
// Initialize the counter document
batch.set(ref, {'numShards': numShards});
// Initialize each shard with count=0
for (var i = 0; i < numShards; i++) {
final shardRef = ref.collection('shards').doc(i.toString());
batch.set(shardRef, {'count': 0});
}
// Commit the write batch
await batch.commit();
}

Future<void> incrementCounter(DocumentReference ref, int numShards) async {
// Select a shard of the counter at random
final shardId = Random().nextInt(numShards).toString();
final shardRef = ref.collection('shards').doc(shardId);
// Update count
await shardRef.update({'count': FieldValue.increment(1)});
}



Future<int> getCount(DocumentReference ref) async {
// Sum the count of each shard in the subcollection
final shards = await ref.collection('shards').get();
int totalCount = 0;
shards.docs.forEach(
(doc) {
totalCount += doc.data()['count'] as int;
},
);
return totalCount;
}

沒有留言:

張貼留言