I want to CRUD the value of the FireStore in the method - firebase

・ What I want to do.
I have stored the document ID of the currently accessed room in user collection>document>field>documentID.
I want to retrieve it and rewrite the document in the room collection.
But I can't rewrite it.
I want to know how to get the data and rewrite it.
[The document ID in the user collection is the user ID.]
I want to retrieve the field in the following method
void _onConferenceTerminated(message) async {
//[Image 1] I get the documentID being accessed from the documentID in the field of the user collection
final user = FirebaseFirestore.instance.collection('user').doc(uid()).get();
final getDocId = user.data['documentID']
//[*image2]Use it to access the room document and reduce the roomCount
final setRoomCount = await FirebaseFirestore.instance.
.collection('room')
.doc(getDocId)
.set({'roomCount': roomCount - 1});
}
//Get the user ID
String uid() {
final User user = FirebaseAuth.instance.currentUser!
final String uid = user.uid.toString();
return uid
[image1]
[image2]

You could use .update() instead of .set() if you want to change only one field. I also recommend you to use FieldValue.increment(value) which is built-in function in firestore.
Value could be both int and double.
FieldValue.increment(1)
FieldValue.increment(2.0)
Also, you can use negative numbers to decrease the value. In your case, you can try the below code.
await FirebaseFirestore.instance
.collection("room")
.doc(getDocId)
.update({"roomCount": FieldValue.increment(-1)}
Documentation: https://firebase.google.com/docs/firestore/manage-data/add-data#increment_a_numeric_value

Related

Unique field in Firestore database + Flutter

I'm trying to implement a normal authentication system in my app, but I'd like to create a new field for each user that is the "uniqueName" so users can search and add each other in their friends list. I was thinking of adding a textField in the signup form for the uniqueName and updating my User class adding a new String in this way:
class User {
String email;
String name;
String uniqueName;
String userID;
String profilePictureURL;
String appIdentifier;
...
}
Now, since I have this method for the email&password signup:
static firebaseSignUpWithEmailAndPassword(String emailAddress,String password,File? image,String name,) async {
try {
auth.UserCredential result = await auth.FirebaseAuth.instance
.createUserWithEmailAndPassword(
email: emailAddress, password: password);
String profilePicUrl = '';
if (image != null) {
await updateProgress('Uploading image, Please wait...');
profilePicUrl =
await uploadUserImageToFireStorage(image, result.user?.uid ?? '');
}
User user = User(
email: emailAddress,
name: name,
userID: result.user?.uid ?? '',
profilePictureURL: profilePicUrl);
String? errorMessage = await firebaseCreateNewUser(user);
if (errorMessage == null) {
return user;
} else {
return 'Couldn\'t sign up for firebase, Please try again.';
}
}
how do I have to modify it in order to add this new field in the registration? Since I have to check that the uniqueName insert by the user is effectively unique before creating a new user in the database, what can I do?
Furthermore, I think that it would be cool if this check is made concurrently to the filling of the form, how can I do it? (this is not necessary)
Thanks everyone for the answers
You have to save your users in a collection, then check if uniqueName already exists in the collection. If it exists, return error.
Then when a new user account is created, save the uniqueName.
// this function checks if uniqueName already exists
Future<bool> isDuplicateUniqueName(String uniqueName) async {
QuerySnapshot query = await FirebaseFirestore.instance
.collection('PATH_TO_USERS_COLLECTION')
.where('uniqueName', isEqualTo: uniqueName)
.get();
return query.docs.isNotEmpty;
}
// call the above function inside here.
static firebaseSignUpWithEmailAndPassword(String emailAddress, String password, File? image, String name,) async {
if (await isDuplicateUniqueName(name)) {
// UniqueName is duplicate
// return 'Unique name already exists';
}
// ... the rest of your code. Go ahead and create an account.
// remember to save the uniqueName to users collection.
I suggest doing the following steps:
Create your own users collection (for example users) in Firestore, which you might have done already. (I don't think that User is a good class name, since Firebase Authentication is using the same name. Try MyUser or something.)
Add authentication triggers that will ensure that whenever a Firebase user is added or deleted, it will also be added to or deleted from users collection, use Firebase uid as identifier.
Create a solution to check whether a uniqueName already exists in users collection. You can use a Firestore query, but in this case you have to allow unauthenticated access to read users, at least uniqueName field. (Since the user is not authenticated yet at this point.) A Firebase Cloud Function is another option.
When users enter their desired uniqueName, run the check before creating Firebase user. You can do it when user enters this or when you start the signup process.
If uniqueName is unique, you can try to create Firebase user. Be aware, this step can also fail (for example e-mail name taken etc.). Your users document will be created by the authentication trigger you set up in step 2.
Finally, you have to store this uniqueName in users collection. At this point you will have uid of the newly created Firebase user, so you can use Firestore set command with merge option set to true, so you don't overwrite other data.
It is important to note that you can't guarantee that the Firebase trigger already created the new document in users by the time you arrive to point 6, it is very likely that the trigger is still working or not even started yet. That's why you have to use set both in the authentication trigger and in your own code that sets uniqueName: which "arrives" first, will create the document, and the second will update it.
Also, for the same reason, you have to allow inserts and updates into users collection with Firestore rules. This might sound a little scary, but keep in mind that this is only your own user list to keep track of uniqueName, and authentication is based not on this, but on Firebase Authentication's user management which is well protected.
Last comment: this is not a 100% solution. It is quite unlikely, but theoretically can happen, that some else reserves a uniqueName between you check whether it's unique and the user is actually created. To mitigate this, it is a good idead to make the check just before Firebase user is created. Even in this case a slight chance remains for duplicates.

Retrieve field information form Firestore database

So I want to retrieve name of a user which is inside a field in firestore.
The whole sequence in given in image below.
I want to get the string value 'a' which is inside (chatroom->a_qaz->users->'a').
I am trying to get it with this code but its not working. How to get the field information.
getOtherUserByUsername() async {
return await FirebaseFirestore.instance
.collection("chatroom")
.doc("chatRoomId")
.get();
First of all, let's get the document from your collection.
collection.doc(), as per reference, gets the actual ID as parameter. In your case, you need to specify "a_qaz". After that, you get the document and then you can read the fields. Your code should look like this:
let chatRoom = await FirebaseFirestore.instance
.collection("chatroom")
.doc("a_qaz")
.get();
let users = chatRoom.get("users");
users will store, then, the list of users that's in that field.

Flutter/Dart/Firebase - Updated nested map values

I am trying to record which on my users has purchased which ticket in my app. I am using firebase to store my data about my users and giveaways. When a purchase is complete, I am trying to update the relevant giveaway and assign each ticket to a user using their id.
Firstly, I am not sure if my data schema is the most appropriate for what I'm trying to achieve so open to suggestions for editing as I'm still quite new to the flutter world.
Second, here is how my data is currently structured:
Here is how I have structured my code. Here is my SingleBasketItem model:
class SingleBasketItem {
final String id;
final String uid;
final OurGiveAways giveaway;
final int ticketNumber;
SingleBasketItem(this.id, this.uid, this.giveaway, this.ticketNumber);
}
Here is my Shopping Basket model, I have added an Elevated Button to my shopping basket page which, when tapped, will execute the placeOrder() function:
class ShoppingBasket extends ChangeNotifier {
Map<String, SingleBasketItem> _items = {};
Map<String, SingleBasketItem> get items {
return {..._items};
}
void addItem(String id, String uid, OurGiveAways giveaway, int ticketNumber) {
_items.putIfAbsent(
id,
() => SingleBasketItem(id, uid, giveaway, ticketNumber),
);
notifyListeners();
}
void placeOrder(BuildContext context, OurUser user) {
for (var i = 0; i < _items.length; i++) {
FirebaseFirestore.instance
.collection('giveaways')
.doc(_items.values.toList()[i].giveaway.giveawayId)
.update(
{
'individual_ticket_sales'[_items.values.toList()[i].ticketNumber]:
user.uid,
},
);
}
}
}
Below is an image of the results:
By analysing the results it looks like my code is creating a new field with a title of the 1st index character of individual_ticket_sales ("n" because ive bought ticket 1), how can I set the nested "1" (or whatever ticket I choose) to my user id rather than creating a new field? Thanks.
I would recommend to refactor your database structure because the first problem you will hit with that one is that firestore for now does not support updating a specific index for an array field value. You can get more info about that here.
You could get the whole value individual_ticket_sales update it and save it again as whole but it would be just a matter of time when you would hit the problem that multiple users want to update the same value on almost the same time and one of the changes get's lost. Even the usage of transaction would not be 100% safe because of the size of the object and potential multiple changes.
Is it possible for you to store each ticketId as a firestore document in a firestore collection like this:
FirebaseFirestore.instance
.collection('giveaways')
.doc(_items.values.toList()[i].giveaway.giveawayId)
.collection('individual_ticket_sales')
.doc(i)
.update(user.uid);

how to get data from cloud firestore where user.uid equal to document id in flutter?

I am Having this profile screen which shows users info.
after user authenticated I am storing data in cloud firestore with document id is equal to user-id.
Now, I want to retrieve data from cloud firestore with having current userId is equal to document id.
For now i have this :
class UserManagement {
getData() async{
String userId = (await FirebaseAuth.instance.currentUser()).uid;
print(userId);
return Firestore.instance.collection('users').document(userId);
}
but this is not working properlywhen i log out and after re-login with different user it showing me same data.
UserManagement().getData().then((results) {
setState(() {
userFlag = true;
users = results;
});
});
Now, how get other fields like name,email,course,phonenumber..etc
and all values all storing into user.right?
If the document id in your firestore is equal to the userid in the Firebase authentication console, then you need to retrieve the uid first and pass it as an argument to the method document():
getData() async{
String userId = (await FirebaseAuth.instance.currentUser()).uid;
return Firestore.instance.collection('users').document(userId);
}
Your query is fetching all of the documents in the "userData" collection, then picking out the first document from that entire set. This will be the same set of documents for all users that have read access to that collection. I don't see why you would expect a different result for different users. Perhaps you meant to access a single document for a user given their user ID, instead of all of the documents. If that's the case, you should request that document by its ID with Firestore.instance.collection('userData').document(uid)' whereuid` is the ID of the currently signed in user.
Also, your code is querying a collection called "userData", but your screenshot shows a collection called "users", so that is confusing.

Fetch collection startAfter documentID

Is there a way to fetch document after documentID like
private fun fetchCollectoionnAfterDocumentID(limit :Long){
val db = FirebaseFirestore.getInstance()
var query:Query = db.collection("questionCollection")
.startAfter("cDxXGLHlP56xnAp4RmE5") //
.orderBy("questionID", Query.Direction.DESCENDING)
.limit(limit)
query.get().addOnSuccessListener {
var questions = it.toObjects(QuestionBO::class.java)
questions.size
}
}
I want to fetch sorted questions after a given Document ID. I know I can do it using DocumentSnapShot. In order to fetch the second time or after the app is resume I have to save this DocumentSnapshot in Preference.
Can It be possible to fetch after document ID?
startAfter - > cDxXGLHlP56xnAp4RmE5
Edit
I know I can do it using lastVisible DocumentSnapshot . But I have to save lastVisible DocumentSnapshot in sharedPreference.
When app launch first time 10 question are fetched from questionCollection. Next time 10 more question have to be fetched after those lastVisible. So for fetching next 10 I have to save DocumentSnapshot object in sharedPreference. Suggest me a better approach after seeing my database structure.
And one more thing questionID is same as Document reference ID.
There is no way you can pass only the document id to the startAfter() method and simply start from that particular id, you should pass a DocumentSnapshots object, as explained in the official documentation regarding Firestore pagination:
Use the last document in a batch as the start of a cursor for the next batch.
first.get().addOnSuccessListener(new OnSuccessListener<QuerySnapshot>() {
#Override
public void onSuccess(QuerySnapshot documentSnapshots) {
=// Get the last visible document
DocumentSnapshot lastVisible = documentSnapshots.getDocuments()
.get(documentSnapshots.size() -1);
// Construct a new query starting at this document,
Query next = db.collection("cities")
.orderBy("population")
.startAfter(lastVisible) //Pass the DocumentSnapshot object
.limit(25);
// Use the query for pagination
}
});
See, here the lastVisible is a DocumentSnapshot object which represents the last visible object. You cannot pass only a document id. For more information, you can check my answer from the following post:
How to paginate Firestore with Android?
It's in Java but I'm confident you can understand it and write it in Kotlin.
Edit:
Please consider defining an order of your results so that all your pages of data can exist in a predictable way. So you need to either specify a startAt()/startAfter() value to indicate where in the ordering to begin receiving ordered documents or use a DocumentSnapshot to indicate the next document to receive, as explained above.
Another solution might be to put the document id into the document itself (as a value of a property) and order on it, or you can use FieldPath.documentId() to order by the id without having to add one.
You can also check this and this out.
There is one way to let startAfter(documentID) works.
Making one more document "get", then using the result as startAfter input.
val db = FirebaseFirestore.getInstance()
// I use javascript await / async here
val afterDoc = await db.collection("questionCollection").doc("cDxXGLHlP56xnAp4RmE5").get();
var query:Query = db.collection("questionCollection")
.startAfter(afterDoc)
.orderBy("questionID", Query.Direction.DESCENDING)
.limit(limit)
A simple way to think of this: if you order on questionID you'll need to know at least the value of questionID of the document to start after. You'll often also want to know the key, to disambiguate between documents with the same values. But since it sounds like your questionID values are unique within this collection, that might not be needed here.
But just knowing the key isn't enough, as that would require Firestore to scan its entire index to find that document. Such an index scan would break the performance guarantees of Firestore, which is why it requires you to give you the information it needs to perform a direct lookup in the index.

Resources