How to check a certain data already exists in firestore or not
Before adding a new data into the firestore, i want to check already a data of the same kind exists in the database or not.if already a data was present means i want to prevent the user from entering duplicate data in the database. In my case it is like a appointment booking if already a booking for the same time exists,i want to prevent to users to book on the same time.i tried using query function but it is not preventing duplicate data entering.someone plz help me
private boolean alreadyBooked(final String boname, final String bodept, final String botime) {
final int[] flag = {0};
CollectionReference cref=db.collection("bookingdetails");
Query q1=cref.whereEqualTo("time",botime).whereEqualTo("dept",bodept);
q1.get().addOnSuccessListener(new OnSuccessListener<QuerySnapshot>() {
@Override
public void onSuccess(QuerySnapshot queryDocumentSnapshots) {
for (DocumentSnapshot ds : queryDocumentSnapshots) {
String rname, rdept, rtime;
rname = ds.getString("name");
rdept = ds.getString("dept");
rtime = ds.getString("time");
if (rdept.equals(botime)) {
if (rtime.equals(botime)) {
flag[0] = 1;
return;
}
}
}
}
});
if(flag[0]==1){
return true;
}
else {
return false;
}
}
Solution 1:
Loading data from Cloud Firestore happens asynchronously. By the time you return from alreadyBooked
, the data hasn't loaded yet, onSuccess
hasn't run yet, and flag
still has its default value.
The easiest way to see this is with a few log statements:
private boolean alreadyBooked(final String boname, final String bodept, final String botime) {
CollectionReference cref=db.collection("bookingdetails");
Query q1=cref.whereEqualTo("time",botime).whereEqualTo("dept",bodept);
System.out.println("Starting listener");
q1.get().addOnSuccessListener(new OnSuccessListener<QuerySnapshot>() {
@Override
public void onSuccess(QuerySnapshot queryDocumentSnapshots) {
System.out.println("Got data from Firestore");
}
});
System.out.println("Returning");
}
If you run this code it will print:
Starting listener
Returning
Got data from Firestore
That's probably not the order you expected. But it perfectly explains why you always get false
when calling alreadyBooked
: the data simply didn't come back from Firestore in time.
The solution for this is to change the way you think about the problem. Your current code has logic: "First check if it is already booked, then add a new item". We need to reframe this as: "Start checking if it is already booked. Once we know that is isn't, add a new item." In code this means that all code that needs data from Firestore must be inside the onSuccess
or must be called from there.
The simplest version is to move the code into onSuccess
:
private void alreadyBooked(final String boname, final String bodept, final String botime) {
CollectionReference cref=db.collection("bookingdetails");
Query q1=cref.whereEqualTo("time",botime).whereEqualTo("dept",bodept);
q1.get().addOnSuccessListener(new OnSuccessListener<QuerySnapshot>() {
@Override
public void onSuccess(QuerySnapshot queryDocumentSnapshots) {
boolean isExisting = false
for (DocumentSnapshot ds : queryDocumentSnapshots) {
String rname, rdept, rtime;
rname = ds.getString("name");
rdept = ds.getString("dept");
rtime = ds.getString("time");
if (rdept.equals(botime)) {
if (rtime.equals(botime)) {
isExisting = true;
}
}
}
if (!isExisting) {
// TODO: add item to Firestore
}
}
});
}
While this is simple, it makes alreadyBooked
less reusable since now it contains the code to insert the new item too. You can solve this by defining your own callback interface:
public interface AlreadyBookedCallback {
void onCallback(boolean isAlreadyBooked);
}
private void alreadyBooked(final String boname, final String bodept, final String botime, AlreadyBookedCallback callback) {
CollectionReference cref=db.collection("bookingdetails");
Query q1=cref.whereEqualTo("time",botime).whereEqualTo("dept",bodept);
q1.get().addOnSuccessListener(new OnSuccessListener<QuerySnapshot>() {
@Override
public void onSuccess(QuerySnapshot queryDocumentSnapshots) {
for (DocumentSnapshot ds : queryDocumentSnapshots) {
String rname, rdept, rtime;
rname = ds.getString("name");
rdept = ds.getString("dept");
rtime = ds.getString("time");
if (rdept.equals(botime)) {
if (rtime.equals(botime)) {
isExisting = true;
}
}
}
callback.onCallback(isExisting)
}
});
}
And then you call it as:
alreadyBooked(boname, bodept, botime, new AlreadyBookedCallback() {
@Override
public void onCallback(boolean isAlreadyBooked) {
// TODO: insert item
}
});
Also see (many of these are for the Firebase Realtime Database, where the same logic applies):
- getContactsFromFirebase() method return an empty list
- Doug's blog post on asynchronous callbacks
- Setting Singleton property value in Firebase Listener
- Android + Firebase: synchronous for into an asynchronous function
- Is it possible to synchronously load data from Firebase?
- Querying data from firebase