Can someone help me to query the list of map object in the collection? Please find sample document below
see my sample document from firestore
In above the image I want to fetch the GROUP collection based on personId which is basically List type.
Searching for solution android. Please advise
Not sure whether its an optimal way of performing a query in Firestore but i only found this way. You can query a map but you will need both the personId as well as personName
Future<void> memberList() async {
List<Map> personList= new List<Map>();
Map<String, dynamic> personData = new Map<String,dynamic>();
personData ["personId"] = pid;
personData ['personName'] = pname;
personList.add(personData);
QuerySnapshot query;
query = await FirebaseFirestore.instance.collection('Group')
.where('groupMembers',arrayContainsAny: personList).get();
}
Related
I want to calculate how many documents are in a collection, not the length of the document. I have tried it with some code but what appears is the length of the character from my document name.
this my code :
StreamSubscription<DocumentSnapshot> userpost;
final DocumentReference documentReference =
Firestore.instance.document("product/$documentPost");
userpost = documentReference.snapshots().listen((datasnapshot) {
if (datasnapshot.exists) {
for (int i = 0; i < datasnapshot.data.length; i++){
print(datasnapshot.data.length);
}
An Example Function to fetch Documents Count.
void countDocuments() async {
QuerySnapshot _myDoc = await Firestore.instance.collection('product').getDocuments();
List<DocumentSnapshot> _myDocCount = _myDoc.documents;
print(_myDocCount.length); // Count of Documents in Collection
}
You can use the count() function which was added in cloud_firestore version 4.0.0
Accepted answer might be a bad solution because you have to fetch all the documents just to count the number of documents. As per Firestore pricing, every document read is taken as 1 read count.
So a better solution is to use the count() function instead.
AggregateQuerySnapshot query = FirebaseFirestore.instance.collection('random_collection').count().get();
int numberOfDocuments = query.count;
count() is an Aggregation Query
PS: You might need to update your firebase plugins in pubspec.yaml.
With Cloud Firebase 2.0, there is a new way to count documents in a collection. According to reference notes, the count does not count as a read per document but a metaData request:
"[AggregateQuery] represents the data at a particular location for retrieving metadata without retrieving the actual documents."
Example:
final CollectionReference<Map<String, dynamic>> userList = FirebaseFirestore.instance.collection('users');
Future<int> countUsers() async {
AggregateQuerySnapshot query = await userList.count().get();
debugPrint('The number of users: ${query.count}');
return query.count;
}
I am trying to create a query which only selects documents whose reference is equal to a given reference, using Java for Android development. A document which it would match contains the reference for the path "/users/someUser". I am creating the reference like so:
DocumentReference ref = mDatabase.document("users/someUser");
I have also tried:
DocumentReference ref = mDatabase.document("/users/someUser");
Then the query:
Query query = mDatabase.collection("myCollection").whereEqualTo("refField", ref).limit(10);
However, when I run the query and check the task.isSuccessful() in the onComplete method, it's not passing, i.e. it didn't work, whereas when I remove the .whereEqualTo(), it passes and the task's result isn't empty. How can I properly use .whereEqualTo() to check for all documents containing a specific reference?
An example of a document that should match my query would be:
/myCollection/GDpojS5koac2C7YlIqxS which contains the field:
refField: /users/someUser (value of type reference)
And an example of a document that should not match my query would be:
/myCollection/J5ZcVAMYU1nI5XZmh6Bv which contains the field:
refField: /users/wrongUser (value of type reference)
I think you need to add a get() method to run the query and add an onCompletionListener.
Something like this should work:
mDatabase.collection("myCollection")
.whereEqualTo("refField", ref)
.limit(10)
.get()
.addOnCompleteListener({task ->
if(task.isSuccessful){
val result = task.result
})
The above example is in kotlin, but i guess in java it is something similar
You need not to worry about the documents, if you create a query based on your fields then all the documents will be returned in the "QuerySnapshot" object,
for eg,
CollectionReference collectionReference = db.collection(FIRESTORE_USERS);
DocumentReference documentReference = collectionReference.document(userID);
CollectionReference notificationCollection = documentReference.collection(FIRESTORE_NOTIFICATIONS);
notificationCollection.whereEqualTo(USER_TYPE, userType)
.whereGreaterThanOrEqualTo(SEND_AT, calendar.getTime())
.get().addOnSuccessListener(new OnSuccessListener<QuerySnapshot>() {
#Override
public void onSuccess(QuerySnapshot documentSnapshots) {
List<DocumentSnapshot> snapshotsList = documentSnapshots.getDocuments();
ArrayList<NotificationCollections> notificationCollectionsArrayList = new ArrayList<>();
for (DocumentSnapshot snapshot : snapshotsList) {
// each document having that particular field based on query
}
}});
in the above example I am fetching all those documents which match a particular user id and also having time greater than or equal to supplied time (time will not be used in your case)
I hope this helps...
Happy coding :)
I'm trying to list documents that matches field String value from ArrayList.
Simply:
I have ArrayList with tags stored at runtime
and documents with field tag
and I want to query documents that matches tag with one of tags stored in ArrayList. Is this possible with official query or does I have to download all documents and filter it client-side? Thanks for any answers.
Also, this is my method generating query:
public static Query getQueryForFollowed(DocumentSnapshot snapshots) {
if (snapshots == null || !snapshots.exists()) {
return FirebaseFirestore.getInstance().collection("posts").whereEqualTo("null", "null"); // return query that will get nothing
}
ArrayList<String> f = processFollowedTags(snapshots);
Query query = FirebaseFirestore.getInstance()
.collection("posts")
.whereEqualTo("tag", f.get(0));
for (int i = 1; i < f.size(); i++) {
query = query.whereEqualTo("tag", f.get(i));
}
return query;
}
I have debugged code and query has contained requested conditions, but query didn't found any document matching it.
Try This
Query query = FirebaseFirestore.getInstance()
.collection("posts")
.whereEqualTo("tag", f.get(0)).orderBy("tag", Query.Direction.ASCENDING);;
After some more search on Google I have found that querying field to multiple values is not available.
According to:
https://stackoverflow.com/a/46633294/8428193
https://github.com/firebase/firebase-js-sdk/issues/321
Below code snippet may help you.
fun arrayContainsQueries() {
// [START array_contains_filter]
val citiesRef = db.collection("cities")
citiesRef.whereArrayContains("regions", "west_coast")
// [END array_contains_filter]
}
ref : git
As of Nov 2019 this is now possible to do with the in query.
With the in query, you can query a specific field for multiple values
(up to 10) in a single query. You do this by passing a list containing
all the values you want to search for, and Cloud Firestore will match
any document whose field equals one of those values.
it would look like this:
Query query = FirebaseFirestore.getInstance()
.collection("posts")
.whereIn("tag", f);
Firestore database image
Hello, I just tried to use Firestore. I had some problem when getting document id.
The question is, I want to get a document id (red box) which has value (blue box) in it.
I use the following query:
collection("mychannel").whereEqualTo("74wRU4xHrcV9oWAXEkKeRNp41c53")
But did not give results.
Thanks!
As in the official documentation:
Although Cloud Firestore can store arrays, it does not support querying array members or updating single array elements.
So there is no way in which you can use the following query:
collection("mychannel").whereEqualTo("74wRU4xHrcV9oWAXEkKeRNp41c53")
If you only want to get the entire userId array you need to iterate over a Map like this:
collection("mychannel").document("1fReXb8pgQvJzFdzpkSy").get().addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
#Override
public void onComplete(#NonNull Task<DocumentSnapshot> task) {
if (task.isSuccessful()) {
DocumentSnapshot document = task.getResult();
if (document.exists()) {
Map<String, Object> map = document.getData();
for (Map.Entry<String, Object> entry : map.entrySet()) {
if (entry.getKey().equals("userId")) {
Log.d("TAG", entry.getValue().toString());
}
}
}
}
}
});
But note, even if userId object is stored in the database as an array, entry.getValue() returns an ArrayList, not an array.
So the output will be:
[74wRU4xHrcV9oWAXEkKeRNp41c53]
A better approach will be if you consider this alternative database structure, where each user id is the key in a map and all values are true:
userId: {
"74wRU4xHrcV9oWAXEkKeRNp41c53": true,
"AdwF...": true,
"YsHs...": true
}
This question is answered here: Firestore: Query by item in array of document
In summary, don't use arrays to store data in Firestore as the query you are trying to do is not available yet (remember it is still in beta). You should use a Map instead.
I'm developing an app and saving some strings like postedAtTime, postedBy, postedOnDate in Firebase database. I want to save the GeoFire coordinates in the same node in which all the above string are saved, so that later I can do query, easily.
Here's the path to which I'm saving all the strings:
databaseReferenceHRequests = firebaseDatabase.getReferenceFromUrl("https://appName-e1a35.firebaseio.com/requests/");
This is how I'm saving it:
// in onButtonClicked method:
postNewRequest(null, imageUID, MainActivity.userName.getText().toString(), time, date, utcFormatDateTime, MainActivity.userEmail.getText().toString(), geoFire);
// the method:
public void postNewRequest(Bitmap bitmap, String imageUIDh, String postedBy, String postedAtTime, String postedOnDate, String utcFormatDateTime, String userEmail, GeoFire geoFire) {
HRequest hRequest = new HelpRequest(null, imageUIDh, postedBy, postedAtTime, postedOnDate, utcFormatDateTime, userEmail, geoFire);
databaseReferenceHRequests.push().setValue(hRequest);
}
Here's how it is getting saved in the database:
What I want is to save the GeoFire coordinates in the same node, which is -KLIoLUsI0SpQZGpV1h4 here. This is just a push ID and it gets generated randomly.
I tried it by giving this reference:
geoFire = new GeoFire(firebaseDatabase.getReferenceFromUrl("https://appName-e1a35.firebaseio.com/requests/"));
And then pushing it with other items as shown above. But, this saved only GeoFire coordinates and not the other items under the node requests.
So, what should be my GeoFire reference so that it gets saved along with all the data in the same node?
What is going wrong here? Please let me know.
Frank's answer is correct, but I want to give an example.
Your database structure should be like this.
{
"items" : {
<itemId> : {
"someData" : "someData",
...
}
},
"items_location" : {
<itemId> : {
<geofireData> ...
}
}
}
To get the data, first you need to do GeoQuery at items_location node and then get the data on the onKeyEntered method. The parameter key is itemId from my example.
geoFire = new GeoFire(FirebaseDatabase.getInstance().getReference().child("items_location");
geoQuery = geoFire.queryAtLocation(geoLocation), radius);
geoQuery.addGeoQueryEventListener(new GeoQueryEventListener() {
#Override
public void onKeyEntered(String key, GeoLocation location) {
//retrieve data
}
};
Hope this helps.
EDIT
How to push the item and set the geofire data.
String itemId = ref.child("items").push().getKey();
ref.child("items").child(itemId).setValue(item);
geoFire = new GeoFire(ref.child("items_location"));
geoFire.setLocation(itemId, new GeoLocation(lattitude, longitude));
EDIT Save the item data and geofire data in one API call
GeoHash geoHash = new GeoHash(new GeoLocation(latitude, longitude));
Map<String, Object> updates = new HashMap<>();
updates.put("items/" + itemId, item);
updates.put("items_location/" + itemId + "/g", geoHash.getGeoHashString());
updates.put("items_location/" + itemId + "/l", Arrays.asList(latitude, longitude));
ref.updateChildren(updates);
When you use Geofire, you have two lists of data:
a list of items with their regular properties
a list of geohash indexes and their associated keys, which you query through Geofire
You use the keys to get from the Geoquery results to the regular items. That's why the events for Geofire are called "Key Entered", "Key Exited", etc.
Trying to store them in one node is a bad idea, since you're mixing mostly static data (the properties of your items) with highly volatile data (the geo-location information). Separating the two out leads to better performance, which is why Geofire enforces it.
While there may be use-cases where the properties and geo-data are equally dynamic/static, GeoFire does not support keeping the geo-data and other properties in a single location.
You can use Firebase functions to enter it for you on every new entry
let functions = require('firebase-functions');
let GeoFire = require('geofire');
exports.testLocation = functions.database.ref('/items/{item}').onWrite(event => {
let data = event.data.val();
console.log(data);
console.log(event.params.item);
if (data.location && data.location.coords) {
console.log('Update GeoFire');
let ref = event.data.adminRef.parent.parent.child('/items_locations'));
let key = event.params.test;
let location = [data.location.coords.latitude, data.location.coords.longitude]);
let geoFire = new GeoFire(ref);
geoFire.set(key, location).then(() => {
console.log('Update succesfull');
}).catch(error => {
console.log(error);
});
}
}
For those more recently coming to this post with the same question, this is possible with the Geofirestore library, which supports Geofire for apps built on top of the Firebase Firestore database.