FIrestore Query Document in Collection Using Single Field/Attribute in Flutter - flutter

I am trying to fetch the role of the currently authenticated user stored in users collection. What I am trying to achieve is at login time, query the user role by traversing fetching the user's document in the collection and sifting through the fields or checking all documents and returning the field role as a string.
Collection and document snapshot(excuse the terminology):
All documents in users collection have same fields for now.
Please how do I go about writing this type of query in flutter? I have tried using AuthResult in my service and FirebaseAuth to get current user(but no way to access the fields in the document).
Thanks.
String role;
getUserRoleWithFuture() async {
String currID = await _authService.getCurrentUID();
String mRole;
Firestore.instance.collection(USERS_REF).document(currID).get().then((doc) {
mRole = doc.data['role'];
print(mRole);
});
return mRole;
}
Future<String> getUserRoleWithStream() async {
String currID = await _authService.getCurrentUID();
String sRole;
Firestore.instance
.collection(USERS_REF)
.document(currID)
.snapshots()
.listen((DocumentSnapshot ds) {
if (ds.exists) {
sRole = ds.data['role'];
print('with stream:\t$sRole');
}
});
return sRole;
}
In the method getUserRoleWithStream() I am trying to retrieve the value printed out like role = getUserRoleWithStream() but instead get this in console a value of type Future<String> can't be assigned to a variable of type string.
How do I get this value using either the stream (cos it constantly observes the collection) or using the other method and use it in my widget?
Thanks again.

This is the working solution, in case anyone else runs into this. I appreciate the effort made into helping me understand the issue but here's the answer:
String role;
getUserRoleWithFuture() async {
String currID = await _authService.getCurrentUID();
String mRole;
Firestore.instance.collection(USERS_REF).document(currID).get().then((doc) {
mRole = doc.data['role'];
print(mRole);
});
return mRole;
}
Future<String> getUserRoleWithStream() async {
String currID = await _authService.getCurrentUID();
String sRole;
Firestore.instance
.collection(USERS_REF)
.document(currID)
.snapshots()
.listen((DocumentSnapshot ds) {
if (ds.exists) {
sRole = ds.data['role'];
print('with stream:\t$sRole');
}
});
return sRole;
}

Well first off, I assume the AuthResult.user.uid and your user's collection user's id is same. So that once you have the user from AuthResult, you can query your firestore collection to get the user's role as follows.
Future<String> getUserRole(String uid) async {
DocumentSnapshot ds = await Firestore.instance.collection('users').document(uid).get();
return ds.data['role'];
}

Related

How to fetch data and update it from firebase

I am having trouble trying to fetch data from firebase and updating the values from it.
I have a restaurant name and the number of times it has been picked (user chooses to go to that restaurant to eat). I am trying to retrieve the numPicked and update it by adding one if the user decides to go there again.
Here i am trying to fetch ONE specific document and trying to store the docID and the variables I need to update.
docID = doc.id; docID is return NULL
meaning that the foreach loop isn't even being read.
Future<bool> searchQuery(
{required String restaurantName,
required var userID,
required db}) async {
int addOne = 1; //addes one if it has been picked
//this is not working
try {
Query query2 =
db.where('userId', isEqualTo: FirebaseAuth.instance.currentUser!.uid);
Query query = query2.where('restaurantName', isEqualTo: restaurantName);
await query.get().then((querySnapshot) {
// ignore: avoid_function_literals_in_foreach_calls
querySnapshot.docs.forEach((doc) {
docID = doc.id;
numPicked = doc['numPicked'];
restaurantExist = true;
});
}).catchError((error) {
// print('error querying: #error');
});
} catch (ex) {
// ignore: avoid_print
print(ex);
}
//this is not working
int totalPicked = numPicked + addOne;
//if the restaurant exist then update the numpicked for that specific restaurant
if (restaurantExist) {
try {
var query = db
//.collection('NumRestaurantPicked')
.doc(docID);
await query.update({'numPicked': totalPicked.toString()});
} catch (ex) {}
}
return restaurantExist;
}
The docID and numPicked variables are not defined in the method signature, so they are not accessible outside of the try block. They should be defined as class variables, so they can be accessed from other methods.

How to use Array contains in the same array fields in firebase for flutter

I have a chat collection.
each document has an array with two user id's.
my goal is to get the chat that has both user sys id's
I tried running the following but I got an error because we cant use two 'arrayContains' in one query.
Is there any way to perform such query?
here is an image of the data structure
Future getChat({required List userIdsArr}) async {
var docId = '';
userIdsArr.sort((a, b) {
return a.compareTo(b);
});
var filter1 = userIdsArr[0];
var filter2 = userIdsArr[1];
await chat
.where(userIdsArrayColumn, arrayContains: userIdsArr[0])
.where(userIdsArrayColumn, arrayContains: userIdsArr[1])
.get()
.then((value) {
value.docs.forEach((element) {
docId = element.id;
});
});
return docId;
}
the goal is to get the chat that pertains to the users being passed in userIdsArr
this seems to work, is there a better way of doing this?
Future getChat({required List userIdsArr}) async {
var docId = '';
userIdsArr.sort((a, b) {
return a.compareTo(b);
});
await chat
.where(userIdsArrayColumn, arrayContains: userIdsArr[0])
// .where(userIdsArrayColumn, arrayContains: userIdsArr[1])
.get()
.then((value) {
value.docs.forEach((element) {
if (element[userIdsArrayColumn].contains(userIdsArr[1])) {
log('match found!');
docId = element.id;
}
});
});
return docId;
}
A query can only contain a single array-contains query.
To allow your query, you'll want to add an additional field to the document where you keep the pair of UIDs in a predictable (e.g. alphabetical) order. With such a field you can then use a query such as:
where("concatenated_uids", isEqualTo: userIdsArr[0]+"_"+ userIdsArr[1])

Is there a way to check the value of a specific field in a document?

I'm trying to compare the text inputted by user to my firestore collection.
But when I tried this code:
docSnapshot.data()!.containsValue(currentStudPass);
where currentStudPass is a String that contains the User ID which is 184-0055
It works but it's trying to search for all the fields value that the user has,
for example I tried "Test Section" as a password, it will return true.
I want to exclude all fields except the password: or just use containsValue for password: field. But I don't have any idea how to do that I'm new to Flutter.
I tried:
docSnapshot.data()!.containsValue('password: $currentStudPass');
var docSnapshot = await collection.doc("$currentStudId/password").get();
But it doesn't work.
Here's my firestore db.
static Future<bool> checkPass(String passArg) async {
// method to check if the users password is correct
var collection = FirebaseFirestore.instance.collection('users');
var docSnapshot = await collection.doc("$currentStudId").get();
try {
passCheck =
docSnapshot.data()!.containsValue(currentStudPass);
return passCheck;
} catch (e) {
// If any error
return false;
}
try this if this works.
static Future<bool> checkPass({String? id, String? passArg}) async {
final collection = FirebaseFirestore.instance.collection('users');
final docs = await collection.doc(id!).get();
final Map<String,dynamic> map = docs.data() as Map<String,dynamic>;
// you can direct it since the result from contains is bool
return map['password'].toString().contains(passArg!);
// Edit : you can use operator == to know exact input
// return map['password'] == passArg!;
}

Getting document from Firestore to pun inside a Custom User profile but it returns null. Dart/Flutter

Hi there I'm having some trouble to retrieve data from Firestore;
I created a class User for getting all the information for my app.
User Class:
class User {
String id;
String displayName;
String email;
String pictureURL;
String gender;
DateTime dateOfBirth;
User(fbauth.User user) {
id = user.uid;
displayName = user.displayName;
email = user.email;
pictureURL = user.photoURL;
// setting info
DocumentSnapshot resl = FirestoreUtil.read("AdditionalUserInfo", user.uid);
gender = resl.data()['gender'];
dateOfBirth = (resl.data()['date of birth'] as Timestamp).toDate();
}
FirestoreUtils class:
class FirestoreUtil {
static DocumentSnapshot read(String collection, String document) {
FirebaseFirestore.instance
.collection(collection)
.doc(document)
.get()
.then((DocumentSnapshot snapshot) {
if(snapshot.exists){
return snapshot;
}else{
print("no data found");
}
});
}
}
After I return the snapshot, it gets null. But if I do this it prints out the correct values:
class FirestoreUtil {
static DocumentSnapshot read(String collection, String document) {
FirebaseFirestore.instance
.collection(collection)
.doc(document)
.get()
.then((DocumentSnapshot snapshot) {
if(snapshot.exists){
print(snapshot.data);
}else{
print("no data found");
}
});
}
}
I tried literally everything but I couldn't figure out. Could someone help? I cannot imagine that is that hard to retrieve data inside a class.
It's probably Future(async/await) problem. You are returning value inside Future, you can get this value inside another then() etc.
class FirestoreUtil {
static DocumentSnapshot read(String collection, String document) async {
// You can directly return it too without assigning it to result variable
var result = await FirebaseFirestore.instance
.collection(collection)
.doc(document)
.get();
return result;
}
}
There is data() method for DocumentSnapshot, you can use result.data() and check if it's null, isEmpty etc.
I wrote this function with async/await but if you need Future and didn't want to wait operation to be finished, you can directly return
FirebaseFirestore.instance.collection(collection).doc(document).get();
and you can use then() after you get the Future.

How to get Firestore document ID?

In Dart, how do you retrieve the auto-generated ID within a collection's document?
I have a collection called "users" that has auto ID documents with the users details. How do you retrieve that ID?
ID eg: yyHYmbDelykMPDWXHJaV
I'm trying to get this id. When the document is first created, when user is first created, the user.uid is stored in it's collection.
I can retrieve all the data from the users database, which I don't want:
void getData() {
databaseReference
.collection('users')
.getDocuments()
.then((QuerySnapshot snapshot) {
snapshot.documents.forEach((f) => print('${f.data}'));
});
}
I can get a future reference for the current user's user.uid but that still doesn't give me the unique document ID.
Future<DocumentReference> getUserDoc() async {
final FirebaseAuth _auth = FirebaseAuth.instance;
final Firestore _firestore = Firestore.instance;
FirebaseUser user = await _auth.currentUser();
DocumentReference ref =
_firestore.collection('users').document(user.uid);
print(ref);
return ref;
}
Is it possible to search the user.uid against the database users and retrieve the ID that way?
To get the auto generated id, you can just call the document method on whatever collection you're trying to get a document from and the returned value will have a getter for documentID
final userDocument = usersCollection.document();
final documentID = userDocument.documentID;
You usually do this when you first want to create a document. To retrieve the documentID after you've created it, I'd add it to the user document itself:
userDocument.setData({
documentID: documentID,
/* ... */
});
Instead of using the auto generated documentID though, I personally use the firebase user's uid property just because it ties it back to Firebase auth
// user is a FirebaseUser
final userDocument = usersCollection.document(user.uid);
userDocument.setData({
uid: user.uid,
// ...displayName, email, etc.
});