Check if a field exists - search through Firestore database - android

I am doing a project for school - android app which registers users to realtime database after it checks if there's a corresponding card number and phone number in a different database in Firestore. At the moment it verifies only the first document, but it wouldn't find the fields if I search for them in other documents.
This is the method I use:
public void checkIfCardExists() {
Query query = cardInfo.whereEqualTo("CardNo", cardNumber)
.whereEqualTo("Phone", userPhone);
query.get().addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
boolean documentExists;
if (task.isSuccessful()) {
Log.d("QueryResult", "Is query result empty: " + task.getResult().isEmpty());
documentExists = !task.getResult().isEmpty();
}else {
Log.e("QueryResult", "Error getting documents.", task.getException());
documentExists = false;
}
if(documentExists) {
Log.d("QueryResult", "The document exists");
Toast.makeText(com.example.transportticket.RegistrationLeap.this, "Card number found",
Toast.LENGTH_SHORT).show();
userLeap = new UserLeap(userEmail, userPass, userName, userSurname, cardNumber, userPhone);
registerUserLeap(userEmail, userPass);
startActivity(new Intent(RegistrationLeap.this, Empty.class));
}else{
Log.d("QueryResult", "The document doesn't exist or there was an error retrieving it");
Toast.makeText(com.example.transportticket.RegistrationLeap.this, "Card number not found",
Toast.LENGTH_SHORT).show();
startActivity(new Intent(RegistrationLeap.this, Empty.class));
}
}
});
}
And this is how my Firestore database looks like
Firestore database
I added a photo to clarify about finding the first document

If you are using the following line of code:
Query query = cardInfo.whereEqualTo("CardNo", cardNumber)
.whereEqualTo("Phone", userPhone);
It means that you are telling Firestore to return all documents where the CardNo property holds the value of cardNumber AND the Phone property holds the value of userPhone. So if in your collection only one document satisfies this constraint, a single document will be returned. The other documents won't exist in the results. What you are doing now, is called filtering. However, if you want to get all documents, then you should remove both whereEqualTo() calls or directly use cardInfo which is a CollectionReference object. In this way, you aren't filtering anything. A CollectionReference object is basically a Query without any filters.
So using the last solution you can get all documents and you can also create the filtering on the client. This is not a recommended approach because getting all documents, will be very costly. For instance, if you have in your collection 1000 documents, you'll pay 1000 read operations to have them. So it's up to you to decide which one is better for you.

Related

Firestore to query Array sub collection

I have my firestore collection structure as shown in the image.
Following is the main collection name which contains currently logged userId as a document ("AuScbj..")
which contains a subcollection called uIds which contains the user ids of users he follows.
When logged in user ("AuScbj..") visits a particular user profile, how can I check whether that profile user's Id available in his following list just by querying like below
firebaseFirestore.collection("Following)
.document(FirebaseAuth.getInstance().getCurrentUser().getUid())
.collection("uIds").where(
You're looking for .where(fieldPath: "uids", opStr: "array-contains", value: followingUID). It should work with your simple "array" data.
To check if id1 is present inside the uIds array in a really simpler manner, first, you should create a class:
class Document {
List<String> uIds;
}
Then get the "AUScbTj5MpNY.." document and read the content of the uIds array using the following method:
private void checkId(String id) {
String uid = FirebaseAuth.getInstance().getCurrentUser().getUid();
FirebaseFirestore rootRef = FirebaseFirestore.getInstance();
CollectionReference followingRef = rootRef.collection("Following");
DocumentReference uidRef = followingRef.document(uid);
uidRef.get().addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
#Override
public void onComplete(#NonNull Task<DocumentSnapshot> task) {
if (task.isSuccessful()) {
DocumentSnapshot document = task.getResult();
if (document.exists()) {
List<String> uIds = document.toObject(Document.class).uIds;
if (uIds.contains(id)) {
//Update the UI
}
} else {
Log.d(TAG, "No such document");
}
} else {
Log.d(TAG, "get failed with ", task.getException());
}
}
});
}
Inside the onComplete, we get the array as a List and check if the id with which we are calling the method, exists inside the array. To make it work, kick it off with:
checkId(id1);
Please also note that:
firebaseFirestore.collection("Following)
.document(FirebaseAuth.getInstance().getCurrentUser().getUid())
.collection("uIds").where(/* ... /*);
Will never work, as uIds is an array inside a document and not a collection.
You can also find more info, in the following article:
How to map an array of objects from Cloud Firestore to a List of objects?

Firestore query returns empty JSON object

We are developing a mobile application where are using firebase as backend. We are using cloud firestore as our database. While querying data from Android it return blank JSON. Here is how our database looks
Here is our android code
db.collection("meta_data")
.get()
.addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
if (task.isSuccessful()) {
for (QueryDocumentSnapshot document : task.getResult()) {
Log.d(TAG, document.getId() + " => " + document.getData());
}
} else {
Log.d(TAG, "Error getting documents: ", task.getException());
}
}
});
It prints
college_metadata => {}
Your "college_metadata" document doesn't actually contain any fields. It's what I would call an "empty document". If you print the data from an empty document, you will get an empty map, which is what you're seeing in your output: {}.
Queries in Firestore are "shallow", which means they don't fetch nested subcollections of matched documents. If you want the data in a nested subcollection, you will have to query for it by name:
db
.collection("meta_data")
.document("college_metadata")
.collection("course")
.get()
This will give you all the documents in the "course" subcollection of the empty document named "college_metadata".
There is no way to make a Firestore query get all nested documents in all nested collections. You need to write that code, if that's what you want.

How to query by type and date?

I cant get user recipe Ids to list
I try to query by whereEqualTo and orderBy but on compile firebase suggested me to create indexing, so I did that but it dont give me any results.
for (String mealType : dishTypeList){
userCollectionReference.document(userId).collection("favourites")
.whereEqualTo("mealType", mealType)
.orderBy("dateWhenSetFavourite", Query.Direction.DESCENDING)
.get().addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
List<String> favouriteRecipeIds = new ArrayList<>();
for (QueryDocumentSnapshot document : task.getResult()){
favouriteRecipeIds.add(document.toObject(FavouriteRecipeModel.class).getRecipeId());
Log.d(TAG, "LIST LIST: " + favouriteRecipeIds);
}
myFirebaseCallBack.onSuccessCallback(favouriteRecipeIds);
}
});
}
I want to get recipeId whereEqualTo by mealType and ordered by dateWhenSetToFavourites
This is my database:
Are you sure that dishTypeList contains the same dish types that are used in the database? If yes, your code looks fine to me. If all your objects in the database contain the recipe id than the following code should work:
favouriteRecipeIds.add(document.toObject(FavouriteRecipeModel.class).getRecipeId());
Otherwise, a more simpler way of getting the document id would be:
favouriteRecipeIds.add(document.getId());
Beside that, everytime you are getting as a result a Task object, check to see if it is successful:
if (task.isSuccessful()) {
//Your logic
} else {
Log.d(TAG, task.getException().getMessage());
}
And also use the else part of the statement to check for an error message.

How to retrieve and display data from users that are logged in - Firestore

I am trying to get and display my user's information when they are logged in. (i.e: name, email, phone)
I have tried multiple snippets i have found on youtube and on stack overflow but they have failed. Most tutorials use realtime Database, which is not what i am looking for.
I have also tried making a "users" object.
private void getData(){
FirebaseFirestore db = FirebaseFirestore.getInstance();
db.collection("users")
//.document(FirebaseAuth.getInstance().getCurrentUser().getUid())
.whereEqualTo("email:", FirebaseAuth.getInstance().getCurrentUser().getUid())
.get()
.addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
if (task.isSuccessful()) {
for (DocumentSnapshot document : task.getResult()) {
//Toast.makeText(getApplicationContext(),document.getId() +"==>" + document.getData(),Toast.LENGTH_LONG).show();
//Toast.makeText(getApplicationContext(),""+ document.get("Email") ,Toast.LENGTH_LONG).show();
nameEdt.setText((CharSequence) document.get("First Name"));
emailEdt.setText((CharSequence) document.get("Email"));
phoneEdt.setText((CharSequence) document.get("Phone"));
}
} else {
Toast.makeText(getApplicationContext(),"No such document",Toast.LENGTH_LONG).show();
}
}
});
}
Database Structure:
I understand that documents in firestore are not associated with users, but i dont know how to set my code up so that it only retrieves data from the user that is signed in* It works fine for newly created accounts, but if i were to log out and sign in with a different user it will not update the "account/user information".
In short, how would I access and display my database information from signed in users?
Additional Notes: I am using Email and Password for authentication
To access your user data stored in Firestore, it shouldn't be as complicated as you thought, there's no queries needed, you just need to fetch the documents corresponding to the user's uid, and fetch the specific fields or do whatever you need with them, like this:
db.collection("users").document(FirebaseAuth.getInstance().getCurrentUser().getUid())
.get().addOnCompleteListener(task -> {
if(task.isSuccessful() && task.getResult() != null){
String firstName = task.getResult().getString("First Name");
String email = task.getResult().getString("Email");
String phone = task.getResult().getString("Phone");
//other stuff
}else{
//deal with error
}
});
Original Answer:
User information is not stored in the Firestore database, they are associated with the Firebase Authentication which you set up for the log in. To retrieve the related user information, you need to use the related FirebaseAuth APIs. Use this to retrieve the current log in user:
FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
Then you can get the name and email with something like this:
String name = user.getDisplayName();
String email = user.getEmail();
For more information, refer to the documentation.
If FirebaseAuth doesn't resolve, that probably means you didn't follow the set up guides correctly and forgot to include the dependency in your gradle file:
implementation 'com.google.firebase:firebase-auth:17.0.0'
After a couple days head butting at trying to find a solution, i have found one that is able to retrieve user information from the database. However it is important to note that because my application is not holding a lot of data so this structure works for me.
So i was essentially on the right track, but with some lack of understanding of firebase i missed a few concepts.
private void getData(){
FirebaseFirestore db = FirebaseFirestore.getInstance();
FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
final String current = user.getUid();//getting unique user id
db.collection("users")
.whereEqualTo("uId",current)//looks for the corresponding value with the field
// in the database
.get()
.addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
if (task.isSuccessful()) {
for (DocumentSnapshot document : task.getResult()) {
nameEdt.setText((CharSequence) document.get("firstName"));
emailEdt.setText((CharSequence) document.get("email"));
phoneEdt.setText((CharSequence) document.get("phone"));
// These values must exactly match the fields you have in your db
}
}
As mentioned before, documents do not associate with users, but you CAN link them together by creating a field in your db called "whatever your want" (i made mine uId). This is because firebase generates a unique id for each user when authenticated. By creating a field that holds that unique id you are able to retrieve the associated information in that collection.
How to create the field:
I created a "user" object that would grab the uid from my edit text. In my code, i passed the uid wherever i was creating/authenticating a new user/account.
FirebaseUser testUser = FirebaseAuth.getInstance().getCurrentUser(); //getting the current logged in users id
String userUid = testUser.getUid();
String uidInput = userUid;
User user = new User(firstNameInput,lastNameInput,uidInput);
db.collection("users").document(userUid)
.set(user)
.addOnSuccessListener(new OnSuccessListener<Void>() {
note: I believe you can also add it to your hash map if you have it done that way.

Giving the UID for the document's name

I would like to know what is the best practice to save users preferences in my firestore database.
I would try to explain with an example...
Case 1
I have this kind of Document in my "users" Collection (the name is random generated by Firebase) with 3 fields :
user_uid : String
nickname : String
android_lover : boolean
In my Android project, when I want to search the Document of the user "DFDDE45554SDC", I search where user_uid = "DFDDE45554SDC".
Case 2
I have this kind of Document in my "users" Collection (the name is created with the UID of the user) with 2 fields :
nickname : String
android_lover : boolean
In my Android project, when I want to search the Document of the user "DFDDE45554SDC", I just search the Document "DFDDE45554SDC".
I specify : I don't want duplicate users.
So, what is the best practice (security, optimisation,...) ? Why ?
I would suggest that Case 2 is more effective, for a few reasons:
We already know the user's ID, so don't need to use a different ID here.
Using usersCollection.document(userId) is simple to construct and is a direct DocumentReference, rather than a Query, therefore:
A DocumentReference can be stored in the Firestore database, whereas a Query cannot.
A DocumentReference would likely scale better than instructing the Firestore database to perform a filter query using whereEqualTo("user_uid", userId) (although with indexing, the performance difference is likely negligible at this point).
A Query will always return a collection of results (even if there is only 1), rather than the exact document.
There isn't currently a need for a different randomly-generated ID for each document within the users collection because the user ID is already unique.
You only need 1 document for each user, so this is a sure-fire way to ensure there won't be any duplicates.
The only real incentive I can think of to use Case 1 would be to standardise your document naming scheme with other collections in your database, but this doesn't really matter so much with Firestore.
For a quick example of the two in Android:
Case 1
db.collection("users")
.whereEqualTo("user_uid", "DFDDE45554SDC")
.limit(1)
.get().addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
if (task.isSuccessful()) {
for (DocumentSnapshot document : task.getResult()) {
// Even with limit(1), we still receive a collection
// so iterate this to obtain the desired document
}
}
}
});
Case 2
db.collection("users")
.document("DFDDE45554SDC")
.get().addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
#Override
public void onComplete(#NonNull Task<DocumentSnapshot> task) {
if (task.isSuccessful() && task.getResult() != null) {
// We have access to the single desired document directly
DocumentSnapshot document = task.getResult();
}
}
});

Categories

Resources