Firebase query doesn't get documents with a specific field - firebase

I'm running a query to get all documents from firebase containing a field of location contains, say 'Nairobi'. The structure of my database is /posts/[userId]/userPosts/[postId].
Now, when I specify the userId in my code, I get the documents.
QuerySnapshot snapshot = await FirebaseFirestore.instance.collection('posts')
.doc("1092987983578079255")
.collection('userPosts')
.where('location', isGreaterThanOrEqualTo: '${placemark.locality}')
.get();
setState(() {
posts = snapshot.docs.map((doc) => Post.fromDocument(doc)).toList();
isLoading = false;
});
However, when I try to get all the documents within the entire posts collection, I get null results.
QuerySnapshot snapshot = await FirebaseFirestore.instance.collection('posts')
.where('location', isGreaterThanOrEqualTo: '${placemark.locality}')
.get();
setState(() {
posts = snapshot.docs.map((doc) => Post.fromDocument(doc)).toList();
});
I have tried using the code
QuerySnapshot snapshot = await FirebaseFirestore.instance
.collectionGroup("userPosts")
.where('location', isGreaterThanOrEqualTo: '${placemark.locality}')
.get();
setState(() {
posts = snapshot.docs.map((doc) => Post.fromDocument(doc)).toList();
isLoading = false;
});
but I get the error Operation was rejected because the system is not in a state required for the operation's execution. If performing a query, ensure it has been indexed via the Firebase console.
given that, it is only querying through one criterion, I don't believe that a composite index is necessary but I, still, have created one.
adb logcat doesn't give me the link to create an index even if I wrap the code in try to catch the error e.
What am I missing?

Related

Flutter Firestore Update Where

I'm trying to run a query that retrieves a single row given a where clause and updates it. I understand that Firebase doesn't support an UpdateWhere operations so I'm trying to use a Transaction instead.
I'm having difficulty making it work, maybe I'm too used to sql dbs... Here's my broken code
try {
final whereQuery = _db
.doc(userPath(user))
.collection("someInnerCollection")
.where("active", isEqualTo: true)
.limit(1);
await _db.runTransaction((transaction) async {
final entry = await transaction.get(whereQuery); // This doesn't compile as .get doesn't take in a query
await transaction.update(entry, {
"someValue": "newValue",
});
});
} catch (e) {
...
}
From the test I’ve made, I would suggest the following to achieve what you mention:
Based on the following answer:
As you can see from the API documentation, where() returns a Query object. It's not a DocumentReference.
Even if you think that a query will only return one document, you still have to write code to deal with the fact that it could return zero or more documents in a QuerySnapshot object. I suggest reviewing the documentation on queries to see examples.
After doing the query consult, you have to get the DocumentReference for that given result.
Then, you can use that reference to update the field inside a Batched writes
try {
final post = await firestore
.collection('someInnerCollection')
.where('active', isEqualTo: true)
.limit(1)
.get()
.then((QuerySnapshot snapshot) {
//Here we get the document reference and return to the post variable.
return snapshot.docs[0].reference;
});
var batch = firestore.batch();
//Updates the field value, using post as document reference
batch.update(post, { 'someValue': 'newValue' });
batch.commit();
} catch (e) {
print(e);
}
You are passing the DocumentSnapshot back in the update() operation instead of DocumentReference itself. Try refactoring the like this:
final docRefToUpdate = _db.collection("colName").doc("docId");
await _db.runTransaction((transaction) async {
final entry = await transaction.get() // <-- DocRef of document to update in get() here
await transaction.update(docRefToUpdate, {
// Pass the DocumentReference here ^^
"someValue": "newValue",
});
});
You can use a collection reference and then update single fields using .update().
final CollectionReference collectionReference = FirebaseFirestore.instance.collection('users');
await collectionReference.doc(user.uid).collection('yourNewCollection').doc('yourDocumentInsideNestedCollection').update({
'singleField': 'whatever you want,
});
Same code using "where"
collectionReference.doc(user.uid).collection('yourNewCollection').doc().where('singleField', isEqualTo: yourValue).update({
'singleField': 'whatever you want,
});

search firebase document for a specific field in flutter dart and store it

im trying to restore a document that its field "email" equal to certain value my code didnt get me anything dont know what is the problem
void EditDisplayedName(String email, String name) async {
CollectionReference s = FirebaseFirestore.instance
.collection("Users")
.doc("list_instructors")
.collection("Instructor")
..where("Email", isEqualTo: email);
s.doc(email).update({'Full Name': name});
} //end method
Your code doesn't yet find/read the document for the email address.
The correct process is:
// 1. Create a reference to the collection
CollectionReference s = FirebaseFirestore.instance
.collection("Users")
.doc("list_instructors")
.collection("Instructor")
// 2. Create a query for the user with the given email address
Query query = s.where("Email", isEqualTo: email);
// 3. Execute the query to get the documents
QuerySnapshot querySnapshot = await query.get();
// 4. Loop over the resulting document(s), since there may be multiple
querySnapshot.docs.forEach((doc) {
// 5. Update the 'Full Name' field in this document
doc.reference.update({'Full Name': name});
});

How do I fetch data from multiple documents in a Firebase collection

I'm getting the user's location then using that location to fetch documents containing that location then displaying the data within the documents on the page.
The structure of my Firebase Database is /posts/{userId}/userPosts/{postId}. I want it to search through all the posts (meaning through all the different postIds).
Future<void> initState() {
super.initState();
getListings();
}
getListings() async {
placemark = await getUserLocation();
getLocationListings(placemark);
}
getLocationListings(placemark) async {
setState(() {
isLoading = true;
});
QuerySnapshot snapshot = await FirebaseFirestore.instance.collection('posts')
// .doc(userId)
// .collection('userPosts')
.where('location', isGreaterThanOrEqualTo: placemark.locality)
.get();
setState(() {
posts = snapshot.docs.map((doc) => Post.fromDocument(doc)).toList();
isLoading = false;
});
// print(placemark.locality);
}
When I run the code as it is (.doc(userId) and .collection('userPosts') commented out), the list List<Post> posts = []; remains empty. However, when I uncomment them and provide a userId, I get all the documents containing placemark.locality. How can I go round this?

How do i create a timeline feed using firebase in flutter?

- posts
- userID
- userID
- randomID
- randomID
- followers
- personB userid
- personA userid
- following
- personA userid
- personB userid
This is how my firebase collection is organized.
how do i get create a timeline feed for current logged-in user where the timline posts should be of only from the logged-in user's following list?
Note:I dont want to use firebase functions.Is it possible with CRUD?
What ive tried so far.
getFollowing() async {
QuerySnapshot snapshot = await followersRef
.document(currentUser.id)
.collection('userFollowing')
.getDocuments();
setState(() {
followingList = snapshot.documents.map((doc) => doc.documentID).toList();
print(followingList);
});
}
getTimeline()async{
QuerySnapshot snapshot = await postsRef
.document(followingList)//getting an error here saying list<string> csn't be assigned to string.
.collection('userPosts')
.orderBy('timestamp', descending: true)
.getDocuments();
List<Post> posts =
snapshot.documents.map((doc) => Post.fromDocument(doc)).toList();
setState(() {
this.posts = posts;
});
}
The approach which I think is efficient is using A collection 'Posts' and document for each post, with an array inside the post document as a field.
This means when a user adds post you have to include the user's followers as a field(List<dynamic) inside the post.
- posts(Collection)
- post(Document)
- followers(List<dynamic>)
you can query post for users like
Firestore.instance.collection('posts').where('followers', arrayContains: currentUserId).snapshots();
//this brings all queries with the tagged users as followers aka the users that follow the poster will be able to query this post.
if you want to update the array you can use
FieldValue.arrayRemove or FieldValue.arrayUnion,
Edit: As a final warning I must mention that there is a limit to how much data there could be for one document, based on the scale of your app you have to decide whether to put a growing list (such as followers) inside one document or to create another collection for the growing list and give one document for each element since documents can't be limited (this approach could cost more).
List timelinePosts =[];
getFollowing() async {
QuerySnapshot snapshot = await followersRef
.document(currentUser.id)
.collection('userFollowing')
.getDocuments();
setState(() {
followingList = snapshot.documents.map((doc) => doc.documentID).toList();
print(followingList);
});
}
getTimeline()async{
List posts = [];
for( int i=0; i< followingList.length; i++)
{
QuerySnapshot snapshot = await postsRef
.collections('posts/${followingList[i]}/userPosts')
.orderBy('timestamp', descending: true)
.getDocuments();
posts+= snapshot.data.documents.map((doc) => {'id':doc.documentID,...doc.data}).toList();
}
setState(() {
timelinePosts = timeLinePosts + posts;
});
}
then u can use Listview.builder() on timelinePosts,
However this is not the most efficient method, since you are fetching all the posts at once. You can also implement fetch post as you scroll through the list, using Listview controller attribute

How to make a one-time simple query with Firebase Firestore?

In Dart/Flutter and learning Firebase Firestore... I'm using the following method to test before creating UI:
_testFireStore() async {
var result = Firestore.instance
.collection('users')
.where('uid', isEqualTo: 'IvBEiD990Vh0D9t24l2GCCdsrAf1')
.snapshots();
await for (var snapshot in result) {
for (var user in snapshot.documents) {
print('main.DEBUG: ' + user.data.toString());
}
}
}
It works as expected -- the print statement is executed initially, but also subsequently in real-time every time any field is updated in the document in the Firestore database.
How can this code be changed such that the snapshot is only retrieved once -- not "subscribed/listened" to... and thus we don't waste bandwidth on unwanted/unneeded data and the print statement is only executed once?
Firestore.instance.collection(...).where(...) returns a Query object. It has a method called getDocuments() that executes the query and gives you a Future with a single set of results.
var query = Firestore.instance
.collection('users')
.where('uid', isEqualTo: 'IvBEiD990Vh0D9t24l2GCCdsrAf1');
query.getDocuments().then((QuerySnapshot snapshot) {
// handle the results here
})
Or use await to get the QuerySnapshot, since getDocumets() returns a Future.
Use getDocuments(), to retrieve all the documents once:
_testFireStore() async {
var result = await Firestore.instance
.collection('users')
.where('uid', isEqualTo: 'IvBEiD990Vh0D9t24l2GCCdsrAf1')
.getDocuments();
print(result.documents.toString());
}

Resources