How to check a certain data already exists in firestore or not
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
How can I tell if I value already exists in Firebase Firestore?
There isn't any function to check if a field exists in a document. You'll have to fetch the document and check for it's existence:
let docRef = Firestore.firestore().collection(user!.uid).document(docID)
docRef.getDocument { (document, error) in
if let document = document, document.exists {
let data = document.data()
// Check if the field exists in data
} else {
print("Document does not exist")
}
}
javascript - Firestore checking if username already exists
To be straight forward you can just try this code:
const username = ""
const userNameDoc = await firebase.firestore().collection("users").where("username", "==", username).get()
if(!userNameDoc.empty) {
console.log("This username is already taken!");
}
But as this is all frontend so this can be bypassed if anyone wants to. All this requires you to give all users access to the whole users collection which won't be ideal. So you should ideally use a cloud function or you own server environment for better security.
For example, you can block all direct requests to Firestore collection using security rules and create users using the Admin SDK in cloud functions.
You can use the code below in your cloud function to create a new user by checking if the username is still valid.
exports.createNewUser = functions.https.onCall(async (data, context) => {
const userEmail = data.email
const userName = data.name
const userPass = data.password
const userNameDoc = await admin.firestore().collection("users").where("username", "==", username).get()
if(!userNameDoc.empty) {
console.log("This username is already taken!");
return {result: "Username is already taken!"}
}
return admin
.auth()
.createUser({
email: 'user@example.com',
password: userPassword,
displayName: userName,
})
.then((userRecord) => {
// See the UserRecord reference doc for the contents of userRecord.
console.log('Successfully created new user:', userRecord.uid);
return {result: "User Created!"}
})
.catch((error) => {
console.log('Error creating new user:', error);
return {result: error}
});
});
The 2nd method is way more secure than first one for your Firestore DB.
How to check if a document exists in a firestore collection?
final snapShot = await Firestore.instance.collection('users').document("docID").get();
if (snapShot.exists){
}
You can check here
Flutter firestore check if document with specific id exists
The following line from your code returns a QuerySnapshot
which is not nullable:
FirebaseFirestore.instance.collection('doc_folder').where("userId", isEqualTo: currentUser!.uid).get()
and you assign the returned value of QuerySnapshot
to snap
and perform the following:
snap != null ? ... : ...
However, this condition will be true regardless of whether a document exists.
You should instead check docs
property from QuerySnapshot
, which is a list of the document snapshots returned for your query.
I'd prefer the widget of FutureBuilder
over calling getData()
in initState
for this example to make it clear:
@override
Widget build(BuildContext context) {
return FutureBuilder<QuerySnapshot>(
future: crud.getData(),
builder: (context, snapshot) {
if (snapshot.hasData) {
if (snapshot.data.docs.isNotEmpty) {
// Document exists
} else {
// Document does not exist
}
} else {
// Show a loading widget
// ...
}
},
);
}
Full Example
class Crud {
Future<QuerySnapshot> getData() async {
return await FirebaseFirestore.instance
.collection('doc_folder')
.where("userId", isEqualTo: currentUser!.uid)
.get();
}
}
class MyPage extends StatefulWidget {
@override
_MyPageState createState() => _MyPageState();
}
class _MyPageState extends State<MyPage> {
final crud = Crud();
@override
Widget build(BuildContext context) {
return FutureBuilder<QuerySnapshot>(
future: crud.getData(),
builder: (context, snapshot) {
if (snapshot.hasData) {
final querySnaphost = snapshot.data; // Get query snapshot
if (querySnaphost.docs.isNotEmpty) {
// Document exists
final documentSnapshot =
querySnaphost.docs.first; // Get document snapshot
return Text('${documentSnapshot.data()}'); // Get the data
} else {
// Document does not exist
return Text('Document does not exist.');
}
} else {
// Show a loading widget
return CircularProgressIndicator();
}
},
);
}
}
Firestore query - checking if username already exists
To solve this, please use the following lines of code:
FirebaseFirestore rootRef = FirebaseFirestore.getInstance();
CollectionReference allUsersRef = rootRef.collection("all_users");
Query userNameQuery = allUsersRef.whereEqualTo("username", "userNameToCompare");
userNameQuery.get().addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
@Override
public void onComplete(@NonNull Task<QuerySnapshot> task) {
if (task.isSuccessful()) {
for (DocumentSnapshot document : task.getResult()) {
if (document.exists()) {
String userName = document.getString("username");
Log.d(TAG, "username already exists");
} else {
Log.d(TAG, "username does not exists");
}
}
} else {
Log.d("TAG", "Error getting documents: ", task.getException());
}
}
});
In which userNameToCompare
is of type String and is the user name of the user with which you want to make the comparison.
Firebase Firestore: How to check if the document exists before writing data?
The set()
method with the merge
option is what you are looking for:
If the document does not exist, "it will set a new document to the path", if it exists "it will just update the data".
You have to do as follows:
const ref = firebase.firestore().collection('posts').doc('post_id');
ref.set(
{foo: 'bar', bar: 'foo'},
{merge: true}
);
Check if Field Already exists in Flutter Firestore
I have solved this issue with the follwoing code, thanks for helping me!
IN THE FOLLOWING CODE I USED TO FIND
1)A DOCUMENT IS EXISTING OR NOT?
2)A KEY IS EXISTING OR NOT?
3)A VALUE IS EXISTING OR NOT?
SIMPLE METHOD
//////////////////////////////////////////////////////////////////////
import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'dart:async';
String myText1 = 'temp1';
String myText2 = 'temp2';
String myText3 = 'temp3';
String myText4 = 'temp4';
String myText5 = 'temp5';
String myText6 = 'temp6';
StreamSubscription<DocumentSnapshot> subscription;
final DocumentReference documentReference =
Firestore.instance.document("company/Nova");
class Clean extends StatefulWidget {
@override
_CleanState createState() => _CleanState();
}
class _CleanState extends State<Clean> {
@override
void initState() {
super.initState();
subscription = documentReference.snapshots().listen((datasnapshot) {
//FINDING A SPECIFICDOCUMENT IS EXISTING INSIDE A COLLECTION
if (datasnapshot.exists) {
setState(() {
myText1 = "Document exist";
});
} else if (!datasnapshot.exists) {
setState(() {
myText2 = "Document not exist";
});
}
//FINDING A SPECIFIC KEY IS EXISTING INSIDE A DOCUMENT
if (datasnapshot.data.containsKey("name")) {
setState(() {
myText3 = "key exists";
});
} else if (!datasnapshot.data.containsKey("name")) {
setState(() {
myText4 = "key not exists";
});
}
//FINDING A SPECIFIC VALUE IS EXISTING INSIDE A DOCUMENT
if (datasnapshot.data.containsValue("nova")) {
setState(() {
myText5 = "value exists";
});
} else if (!datasnapshot.data.containsValue("nova")) {
setState(() {
myText6 = "value not exists";
});
}
});
}
@override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
new Text(myText1),
new Text(myText2),
new Text(myText3),
new Text(myText4),
new Text(myText5),
new Text(myText6),
],
);
}
}
MY OLD COMPLEX METHOD BASED ON MY EXISTING CODE
////////////////////////////////////////////////////////
Concept
it has a search bar,when you type it will show the company name ie existing or not in
A Card
and a RaisedButton
. I am using lower case in Firestore in order to avoid the search error. I have forced the TextFormField
output to be lower case with toLowercase()
. You can change it to your own text format.
Code
//if the name is not existing it will show a raised button so u can clcik on that to
//go to a COMPANY ADDING PAGE,otherwise it will only show a **CARD** so that you
//can't go to the next page to add your company
//code:
import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'dart:async';
import './fullscreen.dart';
const blue = 0xFF3b78e7;
String filter = '';
StreamSubscription<DocumentSnapshot> subscription;
final TextEditingController _usercontroller = new TextEditingController();
class CheckAvail extends StatefulWidget {
@override
HomeState createState() => HomeState();
}
class HomeState extends State<CheckAvail> {
@override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomPadding: false,
body: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
// CHILD1
new Flexible(
child: StreamBuilder(
stream: Firestore.instance
.collection('company')
.where('name', isGreaterThanOrEqualTo: filter.toLowerCase())
.limit(1)
.snapshots(),
builder: (BuildContext context,
AsyncSnapshot<QuerySnapshot> snapshot) {
if (!snapshot.hasData) {
return new Column(
children: <Widget>[
new Card(
elevation: 5.0,
child: new Image.asset('assets/progress.gif'),
)
],
);
} else {
return FirestoreListView1(documents: snapshot.data.documents);
}
},
),
),
new Card(
elevation: 0.0,
color: Colors.white,
shape: new RoundedRectangleBorder(
borderRadius: BorderRadius.circular(60.0)),
child: Container(
padding: new EdgeInsets.only(left: 8.0),
child: new TextField(
controller: _usercontroller,
onChanged: (String z) {
setState(() {
filter = z;
});
},
decoration: const InputDecoration(
hintText: "Search...",
hintStyle: TextStyle(
fontFamily: 'roboto',
color: Colors.black38,
fontSize: 16.0,
letterSpacing: -0.500),
fillColor: Colors.white,
border: InputBorder.none,
),
),
),
),
],
),
backgroundColor: Color(blue),
);
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
class FirestoreListView1 extends StatelessWidget {
final List<DocumentSnapshot> documents;
FirestoreListView1({this.documents});
@override
Widget build(BuildContext context1) {
return ListView.builder(
itemCount: documents.length,
padding: new EdgeInsets.all(1.0),
itemBuilder: (BuildContext context1, int index) {
String name = documents[index].data['name'];
if (name.contains(filter.toLowerCase()) &&
name.length == filter.length) {
return new Container(
padding: new EdgeInsets.only(top: 45.0),
child: new Card(
child: new Text(
"Error:Already a Company Exists with this name\nTry another name")),
);
} else {
return (filter.length >= 1)
? new Container(
padding: new EdgeInsets.only(top: 15.0),
child: new RaisedButton(
onPressed: () => Navigator.push(
context1,
new MaterialPageRoute(
builder: (context1) => new NextPage(
value1: name,
))),
disabledColor: Colors.white,
child: new Text(
"Good!You can use this company name",
),
),
)
: new Container(padding: new EdgeInsets.only(top: 250.0),
child: new Card(child: new Text("CHECK IF YOUR COMPANY NAME \n AVAILABLE OR NOT",style: new TextStyle(fontSize: 20.0),)),
);
}
});
}
}
Related Topics
Auto Scale Textview Text to Fit Within Bounds
All Com.Android.Support Libraries Must Use the Exact Same Version Specification
How to Make an Android Spinner With Initial Text "Select One"
How to Start New Activity on Button Click
Activity Has Leaked Window That Was Originally Added
The Apk Must Be Signed With the Same Certificates as the Previous Version
How to Get the External Sd Card Path For Android 4.0+
Onsaveinstancestate () and Onrestoreinstancestate ()
How to Support Different Screen Size in Android
Instant Run in Android Studio 2.0 (How to Turn Off)
Passing a Bundle on Startactivity()
Android 11 Scoped Storage Permissions
What Is Android:Weightsum in Android, and How Does It Work
How to Add Dividers and Spaces Between Items in Recyclerview
Android Toolbar Center Title and Custom Font
How to Use the New Sd Card Access API Presented For Android 5.0 (Lollipop)
Android Studio Installation on Windows 7 Fails, No Jdk Found