Firestore How to access a value from a snapshot in a Document Snapshot stream - flutter

I'm new to dart/flutter and am building an app right now that keeps track of some data on each user in a firstore database. I have a 'Users' collection which contains a document for each user, and one of the fields in the user document is the "UID" received through firebase_auth.
That being said, to make sure I have access to the latest copy of a user document, I hold a Stream. I want to somehow access the "UID" field from the latest snapshot in the stream and then do some other operations with it.
Is there anyway to do this? Am I using/understanding streams incorrectly?

If you only ever need the UID to build other widgets, you can simply use a StreamBuilder which will rebuild its children whenever a new value is emitted from the stream (which you get a copy of). However, if you need to access the latest UID at some arbitrary point of time, check out RxDart's BehaviorSubject which provides constant-time synchronous access to the latest emitted value, if one exists. It is very helpful for handling state in general.

Related

AngularFire docData read value once

What is the correct way to read the content of a document as value without subscription ?
Keep in mind that when persistence is enabled the Observer receives two values, first value is the locally persisted one and the second is the actual value that is stored in the database.
You can use get() method to retrieve the contents of a single document.
For More details you can check this StackOverflow thread

Whats the best practice for object pool pattern in flutter/dart?

Imagine a very simple application with two pages, PostList and PostDetail. On the former page, we show a list of posts, and on the latter, the details of a single post.
Now consider the following scenario. Our user clicks on the first PostItem and navigates to the PostDetail page. We fetch the full post data from the server. The likes_count of this post gets increased by one. Now if our user navigates back to the first page, the PostItem should be updated and show the new likes_count as well.
One possible solution to handle this is to create a pool of posts. Now when we fetch some new data from the server, instead of creating a new post object, we can update our corresponding pool instance object. For example, if we navigate to post with id=3, we can do something like this:
Post oldPost = PostPool.get(3)
oldPost.updateFromJson(servers_new_json_for_post_3)
Since the same object is used on the PostDetail page, our PostItem on the PostList page will be updated as well.
Other approaches that do not use a unique "single instance" of our Post objects, across the application, would not be clean to implement and requires tricks to keep the UI sync.
But the ObjectPool approach also has its own problems and leads to memory leaks since the size of the pool gets bigger and bigger over time. For this problem to get solved we need to manually count the number of references for each pool object instance and discard them when this count is equal to zero. This manual calculation is full of bugs and I was wondering if there are any better ways to achieve this.
You can also solve this by using streams and StreamBuilders. First you create the stream and populates it with the initial data fetched from the API:
I like to use BehaviorSubject from rxdart but you can use normal streams too.
final BehaviorSubject<List<Post>> postsStream = BehaviorSubject<List<Post>>()..startWith(<Post>[]);
On the constructor body or initState function you would fetch the data and add it to the stream.
PostPage() {
_fetchPosts().then((posts) => postsStream.add(posts));
}
You can now subscribe to changes on this postsStream in both pages with StreamBuilder. Any update you need to do you would emit a new (updated) List<Post> to the stream triggering a rebuild on any StreamBuilder subscribed to the stream with the new and updated values.
You can latter dispose the StreamController.

Firestore: Pagination with Cursor

I am trying to paginate data with Firestore, and would be ordering data on columns where duplicates are expected, if the pagination happens to be among those values its expected that it won't work correctly.
I can work around this issue by using StartAfter based on Document ID which will be always be unique.
One way I can accomplish this is passing id of the last document to server side rest api request. This would require two steps, i.e. to fetch the DocumentSnapshot using the DocumentId and constructing the query based on it
var lastSnapshot = fetchSnapshot(id);
citiesRef.OrderBy("Population").StartAfter(lastSnapshot);
Other approach is to persist the DocumentId in the document while creation.This would require two steps each time when the document is created, one to create and the other to update immediately with Id generated (As I don't see a way to persist DocumentId during creation itself)
citiesRef.OrderBy("Population").StartAfter(lastId);
Which one of these is a good approach to follow, either to fetch DocumentSnapshot and not to persist id into the document, or perform two operations by persisting the DocumentId in the first place and using it as key for StartAfter.
Decided to go with Option 1, instead of persisting Document ID in the Document itself as in Option 2.

How to model data in NoSQL Firebase datastore?

I want to store following data:
Users,
Events,
Attendees
(similar to Firebase's example given here:
https://www.youtube.com/watch?v=ran_Ylug7AE)
My Firebase store is like the following:
Users - Collection
{
"9582940055" :
{
"name" : "test"
}
}
Every user is a different document. Am I doing it correctly?
If yes, I have kept Mobile number of every user as Document Id instead of auto id, as the mobile number is going to be unique and it will help me in querying. Is this right?
Events - Collection
{
"MkyzuARd8Uelh0qD1WMa" : // auto id for every event
{
"name" : "test",
"attendees" : {
"user": 'Lakshay'
}
}
}
Here, I have kept attendees as a Map inside the Event document. Is it right or should I make Attendees as a collection inside Event document?
Also, "user": 'Lakshay' inside "attendees" is just a string. Is it advisable to use reference data type of Firebase?
Every user is a different document. Am I doing it correctly?
Yes, this is quite common. Initially it may seem a bit weird to have documents with so little data, but over time you'll get used to it (and likely add more data to each user).
I have kept Mobile number of every user as Document Id instead of auto id, as the mobile number is going to be unique and it will help me in querying. Is this right?
If the number is unique for each user in the context of your app, then it can be used to identify users, and thus also as the ID of the user profile documents. It is slightly more idiomatic to use the user's UID for this purpose, since that is the most common way to look up a user. But if you use phone numbers for that and they are unique for each user, you can also use that.
Here, I have kept attendees as a Map inside the Event document. Is it right or should I make Attendees as a collection inside Event document?
That depends...
Storing the events for a user in a single document means you have a limit to how many events you can store for a user, as a document can be no bigger than 1MB.
Storing the events for a user in their document means you always read the data for a user's events, even when you maybe only need to have the user's name. So you'll be reading more data than needed, wasting bandwidth for both you and your users.
Storing the events inside a subcollection allows you to query them, and read a subset of the events of a user.
On the other hand: using a subcollection means you end up reading more smaller documents. So you'd be paying for more document reads from a subcollection, while paying less for bandwidth.
Also, "user": 'Lakshay' inside "attendees" is just a string. Is it advisable to use reference data type of Firebase?
This makes fairly little difference, as there's not a lot of extra functionality that Firestore's DocumentReference field type gives.

How to update collection documents efficiently when changing a specific value in Firestore?

I have 2 collections. One of them is named "USERS", and the other one "MATCHES". USERS, can join in the MATCHES, appearing the avatar of the user who has joined in the match. The problem is that when the user changes their avatar image after joining in the match, the match avatar doesn't changed, because the match has the old avatar.
The avatar is saved as Base64 in Firestore, but I will change it to "Storage" in the near future.
I have been trying to set the reference, but that only gives me the path.
If I have to do a Database Api Call for each match which is joined the user, maybe I have to do 20 Api calls updating the matches. It can be a solution, but not the best.
Maybe the solution is in the Google Functions?
I'm out of ideas.
Maybe the solution is in the Google Functions?
Cloud Functions also access Firestore through an SDK, so they can't magically do things that the SDK doesn't allow.
If you're duplicating data and you update one of the duplicates, you'll have to consider updating the others. If they all need to be updated, that indeed requires a separate call for each duplicate.
If you don't want to have to do this, don't store duplicate data.
For more on the strategies for updating duplicated data, see How to write denormalized data in Firebase