I want to perform orderyBy "commentedBy" field and "geoHash" field together.
Since firestore doesn't allow multiple orderBy on not equals operator.
Is there any way to perform this query?
I'm trying to show all nearby posts which is not already commented by current user.
My code is below:
query
.collection("posts")
.orderBy("commentedBy")
.whereNotIn("commentedBy",listOf(FirebaseAuth.getInstance().currentUser!!.uid))
.whereEqualTo("disabled", false)
.whereEqualTo("expired", false)
.orderBy("geoHash")
.whereGreaterThanOrEqualTo("geoHash", boundingBox.minimumMatch)
.whereLessThanOrEqualTo("geoHash",boundingBox.maximumMatch)distanceForRadius)
Yes it is possible. After making your query, run your code and you will encounter an error, if you want with log or if you want, find that link in the build section and click it. Confirm it. After the build process is finished, run the application again.
The problem is not that multiple orderBy sortings are applied on a 'not equals' query. For example, this does work as a query:
db.collection("users")
.whereNotIn("firstName", Arrays.asList("Foo", "Bar"))
.orderBy("firstName")
.orderBy("lastName")
The problem with your query is that it's combining range clauses (whereGreaterThanOrEqualTo, whereLessThanOrEqualTo) with an inequality clause (whereNotIn) on different fields (geohash, uID). This is not supported as shown in the query limitations:
In a compound query, range (<, <=, >, >=) and not equals (!=, not-in) comparisons must all filter on the same field.
So your query cannot be done as is. Something that you can try is shifting the filtering for "not the current user" from the query into a stream (the getDocuments() method supports Java streams):
//Query
ApiFuture<QuerySnapshot> query = db.collection("posts")
.whereEqualTo("disabled", false)
.whereEqualTo("expired", false)
.whereLessThanOrEqualTo("geohash", <geohash value>)
.whereGreaterThanOrEqualTo("geohash", <geohash value>)
.orderBy("geohash")
.get();
//Stream that filters fetched documents to exclude current users
List<QueryDocumentSnapshot> documents = querySnapshot.getDocuments().stream()
.filter(doc -> !Arrays.asList(<uid values>).contains(doc.getString("commentedBy")))
.collect(Collectors.toList());
This worked on my tests with simple placeholder data for user IDs and geohashes (and after creating the relevant index as directed by the terminal output).
Related
While I'm using the below query in Firestore, an error is coming:
Code:
query = database.collection("CustomerViews/Data/Post")
.whereGreaterThanOrEqualTo("postDate", startDate)
.whereLessThanOrEqualTo("postDate", endDate)
.orderBy("postViews", Query.Direction.DESCENDING)
.orderBy("postDate", Query.Direction.DESCENDING)
Error:
Invalid query. You have an inequality where filter (whereLessThan(), whereGreaterThan(), etc.) on field 'postDate'
and so you must also have 'postDate' as your first orderBy() field, but your first orderBy() is currently on field
'postViews' instead.
The error message is quite explicit about the error. You are using both "whereGreaterThan()" and "whereLessThan()" on the "postDate" field, but you aren't first ordering the results based on that field, hence that error. Please remember, that the order of the method calls in Firestore is very important. To solve this, please change your query to:
query = database.collection("CustomerViews/Data/Post")
.orderBy("postDate", Query.Direction.DESCENDING)
.whereGreaterThanOrEqualTo("postDate", startDate)
.whereLessThanOrEqualTo("postDate", endDate)
.orderBy("postViews", Query.Direction.DESCENDING)
And right after that, simply create the corresponding index. That's it.
Edit:
According to your first comment:
By using your code data is coming but "postViews" descending is not happening. Only postDate descending is coming.
Yes, the results will be returned descending according to "postDate". And if two or more elements will have the same "postDate", then only those results will be after that ordered descending according to "postViews". This is how Firestore works.
According to your second comment:
I want the "postViews" in descending order in given date ranges.
Cloud Firestore queries can only sort or filter range on a single field. What you are trying to achieve it's not possible, since you are trying to filter on "postDate" and then order on "postViews".
In the official documentation, there is an example on how not to do it:
Range filter and first orderBy on different fields
citiesRef.whereGreaterThan("population", 100000).orderBy("country"); //Invalid
According to your last comment:
I got the solution for my problem. I have added the below line before attaching the ArrayList to an adapter.
postArrayList.sortByDescending { it.PostViews }
Indeed it will work if you download all documents on the client and to the filtering there, but it will cost you one document read for each document you download.
Based on your sample code, you have an inequality wherein postDate should be initialized first before postViews as the error suggested. This is currently a restriction of orderBy() clause wherein your first ordering must be on the same field.
In addition to #Alex's answer, You may check the links below for similar scenarios
Firestore query order on field with filter on a different field
Firestore "Invalid query" - Am I using Indexing wrong?
Finally, here's a documentation for the limitations of order and limit data with Cloud Firestore.
I got the solution for my problem. I have added below line before attaching the arraylist to an adapter.
postArrayList.sortByDescending { it.PostViews }
I'm in a bit of a bind creating a query in Firestore's Android SDK. I want to limit documents to only those from the past week to reduce the number of docs, and also filter results using user-supplied text. I'd like to use a whereGreaterThan clause with the created field for the date, and startAt and endAt clauses for the filtering the id field.
However, there is a problem. The SDK requires that the first orderBy clause use the same field as the whereGreaterThan clause. On the other hand, the startAt and endAt also seem to be bound to the first orderBy clause.
Calendar oneWeekAgo= Calendar.getInstance();
oneWeekAgo.add(Calendar.DATE, -7);
Query query = firestore.collection(BuildConfig.FULL_PATH)
.whereGreaterThan("created",oneWeekAgo.getTime())
.orderBy("created", Query.Direction.ASCENDING)
.orderBy("id", Query.Direction.ASCENDING)
.startAt(filter)
.endAt(filter + "z");
Is there a way to bind the .startAt and .endAt to the second orderBy? Or otherwise accomplish the goal of getting filtered docs from firestore created after a certain date?
If I delete the whereGreaterThan and first orderBy clauses, the query gets filtered results from all documents, not just those created in the past week.
This a limitation of the API because Cloud Firestore doesn't support ordering by a different field than the supplied inequality. In this case you should order the data at client side, in this case your Android app. This is explained here too.
I've been trying to find a query for almost 2 days now
I want to search id (current user id) from the document 4 fields (customer1,customer2,customer3,customer4)
Here is the firestore document picture
tried this query
final Query userQuery = collectionReference
.whereEqualTo("customer1",firebaseAuth.getInstance().getCurrentUser().getUid())
.whereEqualTo("customer2",firebaseAuth.getInstance().getCurrentUser().getUid())
.whereEqualTo("customer3",firebaseAuth.getInstance().getCurrentUser().getUid())
.whereEqualTo("customer4",firebaseAuth.getInstance().getCurrentUser().getUid());
but this only shows up if the current ID is present in all 4. Is there any easier way to do this.
You can do that by using a field that is an array containing the uids you want to test, and then applying array-contains on it. In your case:
In your case:
customer: [customer1, customer2, customer3, customer4]
collectionReference
.where("customer ", "array-contains", firebaseAuth.getInstance().getCurrentUser().getUid())
Firestore does not support logical OR queries among mulitple fields. So, what you're trying to do is not possible with a single query using the database structure you have now. You would have to perform multiple queries and merge the results in the client.
If you want to be able to use a single query, you will have to change your database. One option is to put all the customers into a single array field and use an array-contains query to find a customer in that field.
I want to query in firestore where I want to get the products where the stocks are greater than zero then order it by index. So I tried like this:
Query query = db.collection("Products").whereGreaterThan("productStock", 0)
.orderBy("productStock").orderBy("index", Query.Direction.ASCENDING);
In the documentation of Firesbase Firestore, they said
However, if you have a filter with a range comparison (<, <=, >, >=), your first ordering must be on the same field:
But it won't work. The only working was the orderby("productStock") and
the .orderBy("index", Query.Direction.ASCENDING); isn't working.
I just want to know if this kind of method is possible? if not,
is there any way to achieve what I wanted?
There is no way to get a range of matching documents that are primarily filtered on another field. If you need that functionality, use the query to limit the number of documents, and re-order them client-side.
This code:
var query: Query = store.collection(COLLECTION_USERS)
query = query.whereGreaterThanOrEqualTo("age", filter.startAge).whereLessThanOrEqualTo("age", filter.endAge)
query = query.whereGreaterThanOrEqualTo("start_date", filter.startDate).whereLessThanOrEqualTo("end_date", filter.endDate)
query = query.whereEqualTo("sex", filter.sex)
return query.get()
throw exception: java.lang.IllegalArgumentException: All where filters other than whereEqualTo() must be on the same field. But you have filters on 'age' and 'start_date' how to fix?
Official documentation of Firestore says that range filters on different fields are forbidden. So Firestore allows to chain multiple where() methods to create more specific queries but only on the same field.
To achieve what you want, you need to query your database twice, once for each fiter because you cannot use both methods in the same query.
Another way to make it happen would be to store a special flag that might fit the query, although in real world applications it will almost impossible to store every single way a user might query the data.