Firestore compound query with <= & >= - firebase

I need to query a firestore collection for entries where a start time (which is a number) is >= slot.start and and <= slot.end.
like this:
.collection('Entries', ref => ref
.where('timeStart', '>=', slot.timeStart)
.where('timeEnd', '<=', slot.timeEnd))
but I get this error:
Invalid query. All where filters with an inequality (<, <=, >, or >=) must be on the same field.
I thought this query would work with composite index but as the error says that does not work.
When I change the query to this:
.collection('Entries', ref => ref
.where('timeStart', '==', slot.timeStart)
.where('timeEnd', '<=', slot.timeEnd))
I can create an index for it. Furthermore when I change the 2nd '<=' to an '==' aswell I don't even need an index for it.
I didn't think my query is that advanced and I'm a little bit disappointed that firestore can't do this query. So what is the best solution for my problem? Is the next best thing to omit the 2nd query statement like this
.collection('Entries', ref => ref.where('timeStart', '>=', slot.timeStart))
And then iterate through the results and check if timeEnd <= entry.timeEnd "manually"? That seems like a really bad solution for me because I may need to load a lot of data then.

You're going to have to pick one field and query that one then filter out the other on the client side. Compound Queries documentation is firm on this one.
You can only perform range comparisons (<, <=, >, >=) on a single field, and you can include at most one array_contains clause in a
compound query:

Related

Streambuilder from firebase firestore with advanced filtering

Based on new version of firebase dependencies in flutter, there is new filters such like isNotEqualTo, arrayContains, arrayContainsAny, whereIn and so on.
I try to create streambuilder with some filters, I want to get documents that matches these conditions.
The "uid" field value Does not equal current uid. (isNotEqualTo)
The "school" field value isEqualTo current user school.(isEqualTo)
The "random" field value isGreaterThanOrEqualTo random number that the app generated it (isGreaterThanOrEqualTo)
stream: fireStore.collection(USERS_COLLECTION).where(UID_FIELD,
isNotEqualTo: me.uid).where(SCHOOL_FIELD, isEqualTo:
me.school).where(RANDOM_FIELD, isGreaterThanOrEqualTo:
random).limit(10).snapshots(),
Condition 1 (isNotEqualTo) does not work with me, but 2 and 3 it's working fine, when I add the filter 1 there is error showing to me.
All where filters with an inequality (<, <=, >, or >=) must be on the
same field. But you have inequality filters on 'FieldPath([uid])' and
'FieldPath([random])'. 'package:cloud_firestore/src/query.dart':
Failed assertion: line 486 pos 18: 'hasInequality == field'
That is a Firestore-Limitation
https://firebase.google.com/docs/firestore/query-data/queries
Note the following limitations for != queries:
Only documents where the given field exists can match the query.
You can't combine not-in and != in a compound query.
In a compound query, range (<, <=, >, >=) and != comparisons must all filter on the same field.
So you can either filter using != on the uid OR you can filter using >= on Random.
You cannot do both in the same query.
I'd recommend filtering without the UID and let your repository filter out the UID later, once you've received the snapshots.
actually you can filter on multiple fields by creating a composite index.
Cloud Firestore uses composite indexes to support queries not already supported by single-field indexes.
Cloud Firestore does not automatically create composite indexes like it does for single-field indexes because of the large number of possible field combinations. Instead, Cloud Firestore helps you identify and create required composite indexes as you build your app.
If you attempt the query above without first creating the required index, Cloud Firestore returns an error message containing a link you can follow to create the missing index. This happens any time you attempt a query not supported by an index. You can also define and manage composite indexes manually by using the console or by using the Firebase CLI. For more on creating and managing composite indexes, see Managing Indexes.
https://firebase.google.com/docs/firestore/query-data/index-overview#composite_indexes
Also if in your code you call requireData from your snapshot you will see in your logs a direct link to create that index on Firebase.

How to negate a filter with JSONAPI requests in Drupal?

I'm trying to build a query that returns all items whose path do NOT contain a string.
I can easily build one that returns items whose path does contain a string:
GET /url?filter[path][CONTAINS]=string
But how can I negate that filter?
JSON:API specification itself is agnostic about the filtering strategy used. All it does is recommending that the query parameter filter is used for filtering:
Filtering
The filter query parameter is reserved for filtering data. Servers and clients SHOULD use this key for filtering operations.
Note: JSON:API is agnostic about the strategies supported by a server. The filter query parameter can be used as the basis for any number of filtering strategies.
https://jsonapi.org/format/#fetching-filtering
But you mentioned in a comment that you are using Drupal's JSON:API. That one implements a well specified filtering strategy.
The filtering strategy used by Drupal is build around conditions, which could be grouped and combined either with OR or AND.
A condition is the combination of a path of fields, an operator and a value. It's documentation lists the following operators as supported:
'=' (equal)
'<>' (not equal),
'>' (greater than)
'>=' (greater or equal than)
'<' (less than)
'<=' (less or equal than)
'STARTS_WITH'
'CONTAINS'
'ENDS_WITH'
'IN'
'NOT IN'
'BETWEEN'
'NOT BETWEEN'
'IS NULL'
'IS NOT NULL'
For most operators it supports their negated counter part - like 'in' and 'not in', 'IS NULL' and 'IS NOT NULL', '<' and '>='. But a few operators do not have a negated counterpart: 'CONTAINS', 'STARTS_WITH' and ''ENDS_WITH'. I guess that's cause it may be very expensive to do a search with a 'NOT CONTAINS' operator on the supported databases.
As the documentation also doesn't mention a possibility to negate an condition or a group of conditions I think your specific use case is not support by Drupal's filtering strategy.

orderBy date but have nulls last

I'm querying firestore with:
this.todosCollection = this.afs.collection('todos', ref => ref.where('owner', '==', this.userId). orderBy('due', 'asc'));
The todo items is ordered in ascending order as I want, the problem is that todos without a due date (null) comes first. I want them to come last. Is this possible within the orderBy or other technique?
That's the way the ordering of nulls is intended to work. The documentation makes this clear, and it can't be changed in the query itself.
If you want to change the ordering, then you will have to manually re-sort the documents on the client to support your preferred ordering. Or, you will have to make two queries, one that includes null and another that does not, then merge the results yourself.

How to use not/ inequality operator in cloud firestore?

I have list of strings, and using not operator I wanted to select all values except one. But I couldn't find anything about not opertor in the firebase website. Please help me with this issue.
P.S: This Link explains how to do the inequality operation with numbers, and not strings
It's a limitation with Cloud Firestore queries. But I found a workaround.
Cloud Firestore Documentation states,
"Queries with a != clause. In this case, you should split the query into a greater-than query and a less-than query. For example, although the query clause where("age", "!=", "30") is not supported, you can get the same result set by combining two queries, one with the clause where("age", "<", "30") and one with the clause where("age", ">", 30)."
New update on Firestore now supports not equal and not in queries. As mentioned here:
const capitalNotFalseRes = await citiesRef.where('capital', '!=', false).get();
const notUsaOrJapan = await citiesRef.where('country', 'not-in', ['USA', 'Japan']).get();

Azure CosmosDB IS_DEFINED vs NOT IS_DEFINED

I was trying to query a collection, which had few documents. Some of the collections had "Exception" property, where some don't have.
My end query looks some thing like:
Records that do not contain Exception:
**select COUNT(1) from doc c WHERE NOT IS_DEFINED(c.Exception)**
Records that contain Exception:
**select COUNT(1) from doc c WHERE IS_DEFINED(c.Exception)**
But this seems not be working. When NOT IS_DEFINED is returning some count, IS_DEFINED is returning 0 records, where it actually had data.
My data looks something like (some documents can contain Exception property & others don't):
[{
'Name': 'Sagar',
'Age': 26,
'Exception: 'Object reference not set to an instance of the object', ...
},
{
'Name': 'Sagar',
'Age': 26, ...
}]
Update
As Dax Fohl said in an answer NOT IS_DEFINED is implemented now. See the the cosmos dev blob April updates for more details.
To use it properly the queried property should be added to the index of the collection.
Excerpt from the blog post:
Queries with inequality filters or filters on undefined values can now
be run more efficiently. Previously, these filters did not utilize the
index. When executing a query, Azure Cosmos DB would first evaluate
other less expensive filters (such as =, >, or <) in the query. If
there were inequality filters or filters on undefined values
remaining, the query engine would be required to load each of these
documents. Since inequality filters and filters on undefined values
now utilize the index, we can avoid loading these documents and see a
significant improvement in RU charge.
Here’s a full list of query filters with improvements:
Inequality comparison expression (e.g. c.age != 4)
NOT IN expression (e.g. c.name NOT IN (‘Luis’, ‘Andrew’, ‘Deborah’))
NOT IsDefined
Is expressions (e.g. NOT IsDefined(c.age), NOT IsString(c.name))
Coalesce operator expression (e.g. (c.name ?? ‘N/A’) = ‘Thomas’)
Ternary operator expression (e.g. c.name = null ? ‘N/A’ : c.name)
If you have queries with these filters, you should add an index for
the relevant properties.
The main difference between IS_DEFINED and NOT IS_DEFINED is the former utilizes the index while the later does not (same w/ = vs. !=). It's most likely the case here is IS_DEFINED query finishes in a single continuation and thus you get the full COUNT result. On the other hand, it seems that NOT IS_DEFINED query did not finish in a single continuation and thus you got partial COUNT result. You should get the full result by following the query continuation.

Resources