Firestore: How to query Documents where DocumentID begins with a substring? - firebase

I am trying to query all Documents where DocumentID begins with my string. My research tells me it's possible for field values by using '<=' in where(), but I have not found anything online that tells how to do this on Document ID fields.
I am doing this because my table contains Geohashes as DocumentIDs (high precision) with data, and I would like to get those documents on bases of low precision Geohash.
I also found this as a potential solution:
docs = mycolRef.where(firebase.firestore.FieldPath.documentId(), '>', somestring).stream();
However, this gives the error: "NameError: name 'firebase' is not defined"
I am importing firestore through google.cloud as mentioned in this guide: (https://cloud.google.com/firestore/docs/quickstart-servers)
from firebase import firestore, or import firebase resulted in errors.

How to query Documents where DocumentID begins with a substring?
There is currently no way you can achieve that.
I have not found anything online that tells how to do this on Document ID fields.
You haven't found anything because such a query does not exist.
My research tells me it's possible for field values by using '<=' in where()
Yes, that's the only option you have now. So add all those Geohashes which are now documents ids as values of a new (geohash) property within your document.
I also found this as a potential solution:
docs = mycolRef.where(firebase.firestore.FieldPath.documentId(), '>', somestring).stream();
Unfortunately, you cannot do that, hence that error.
One more thing to note is that in Firestore, it is not possible to change the document ids once it exists. All that data is immutable. What you can do instead is to read all the documents, change the document id and then put it all back.

Related

How to use a calculated derived field in the where clause or orderby clause in firestore

I am using a calculated field, say distance which is not in my schema, firestore. It is calculated by calling a function. This works fine. However I need to use this field (distance) to limit or order by the data example:
.where ('distance', isLessThan :1000)
or say :
.orderBy ('distance')
.on firestore data. I tried to use this way however as this 'distance' field does not exist in forestore, it is giving this error:
[FirebaseAuth:] Preparing to create service connection to fallback implementation
W/System (15007): Ignoring header X-Firebase-Locale because its value was null.
D/FirebaseAuth(15007): Notifying id token listeners about user ( juo4UbaWhdVfmfkr8vyyiY5FNNc2 ).
Thanks!
Firestore has no way to perform (custom or otherwise) calculations inside a query. It can only order/filter on values that are physically present in the documents, and in an index.
If you have a use-case where documents are associated with a location, and you want to find documents within a certain area, the common approach is to store a geohash in each document, and then query on that as explained in the documentation on implement geoqueries on Firestore. I gave a pretty detailed explanation of this approach in my talk on geoquerying on Firebase and Firestore.

How can I limit and sort on document ID in firestore?

I have a collection where the documents are uniquely identified by a date, and I want to get the n most recent documents. My first thought was to use the date as a document ID, and then my query would sort by ID in descending order. Something like .orderBy(FieldPath.documentId, descending: true).limit(n). This does not work, because it requires an index, which can't be created because __name__ only indexes are not supported.
My next attempt was to use .limitToLast(n) with the default sort, which is documented here.
By default, Cloud Firestore retrieves all documents that satisfy the query in ascending order by document ID
According to that snippet from the docs, .limitToLast(n) should work. However, because I didn't specify a sort, it says I can't limit the results. To fix this, I tried .orderBy(FieldPath.documentId).limitToLast(n), which should be equivalent. This, for some reason, gives me an error saying I need an index. I can't create it for the same reason I couldn't create the previous one, but I don't think I should need to because they must already have an index like that in order to implement the default ordering.
Should I just give up and copy the document ID into the document as a field, so I can sort that way? I know it should be easy from an algorithms perspective to do what I'm trying to do, but I haven't been able to figure out how to do it using the API. Am I missing something?
Edit: I didn't realize this was important, but I'm using the flutterfire firestore library.
A few points. It is ALWAYS a good practice to use random, well distributed documentId's in firestore for scale and efficiency. Related to that, there is effectively NO WAY to query by documentId - and in the few circumstances you can use it (especially for a range, which is possible but VERY tricky, as it requires inequalities, and you can only do inequalities on one field). IF there's a reason to search on an ID, yes it is PERFECTLY appropriate to store in the document as well - in fact, my wrapper library always does this.
the correct notation, btw, would be FieldPath.documentId() (method, not constant) - alternatively, __name__ - but I believe this only works in Queries. The reason it requested a new index is without the () it assumed you had a field named FieldPath with a subfield named documentid.
Further: FieldPath.documentId() does NOT generate the documentId at the server - it generates the FULL PATH to the document - see Firestore collection group query on documentId for a more complete explanation.
So net:
=> documentId's should be as random as possible within a collection; it's generally best to let Firestore generate them for you.
=> a valid exception is when you have ONE AND ONLY ONE sub-document under another - for example, every "user" document might have one and only one "forms of Id" document as a subcollection. It is valid to use the SAME ID as the parent document in this exceptional case.
=> anything you want to query should be a FIELD in a document,and generally simple fields.
=> WORD TO THE WISE: Firestore "arrays" are ABSOLUTELY NOT ARRAYS. They are ORDERED LISTS, generally in the order they were added to the array. The SDK presents them to the CLIENT as arrays, but Firestore it self does not STORE them as ACTUAL ARRAYS - THE NUMBER YOU SEE IN THE CONSOLE is the order, not an index. matching elements in an array (arrayContains, e.g.) requires matching the WHOLE element - if you store an ordered list of objects, you CANNOT query the "array" on sub-elements.
From what I've found:
FieldPath.documentId does not match on the documentId, but on the refPath (which it gets automatically if passed a document reference).
As such, since the documents are to be sorted by timestamp, it would be more ideal to create a timestamp fieldvalue for createdAt rather than a human-readable string which is prone to string length sorting over the value of the string.
From there, you can simply sort by date and limit to last. You can keep the document ID's as you intend.

increment document id by timestamp in firestore

My cloud firestore database has an "orders" collection and in HTML I have a 'save' button to add document(s) into that "orders" collection upon clicking. Now, using add will assign auto-generated ID for each document.
What if I want to customise such ID by timestamp? So that the document created yesterday will be assigned an index as '1', and the following document created will be '2', etc...
What you're trying to do is not compatible with the way Cloud Firestore was designed. Firestore will not assign monotonically increasing numbers for document IDs. This just doesn't scale massively as required by Firestore and would introduce performance bottlenecks.
If you want to be able to sort documents by timestamp, the best strategy is to add a timestamp field to each document, then use that field in an ordered query.
Note that you could try to write a lot of code to get this done the way you want, but you are MUCH better off accepting the random IDs and using fields to filter and order data.
in some case, when you need to save several docs in different collection due to an event occurs, it's better to same all docs with same id in different collections with single firestore server's timestamp. you get the timestamp like below:
const admin = require('firebase-admin')
const ts = admin.firestore.Timestamp.now().toMillis().toString()
by doing this, when you need to read all those docs, you only need to query once to get timestamp, then read all other doc by timestamp directly.
it should be faster than query the timestamp inside document fields for each collections

How to search document(s) from a collection based on a Field in Firestore database?

I'm working on a React Native application and I'm fetching profiles from a firebase collection.
And I want to add a search functionality where when I enter even the first 1 or 2 (or more) alphabets of a username and press the search button.
I should be able to fetch usernames starting with those 1 or 2 alphabets.
I did check Cloud Firestore queries but couldn't find one for my problem.
UPDATED QUESTION:
In the above code, I'm adding the below code as answered by Renaud Tarnec.
let queries = hashes.map(hash => rangeQueryParams(hash))
.map(range => profiles.where('hash', '>=', range.start).where('hash', '<', range.end)
.orderBy('displayName') // displayName is the name of Field here
.startAt(searchString)
.endAt(searchString + '\uf8ff')
.get());
But this doesn't seems to work. I guess it's because range filter and orderBy are on different fields here.
You should use a combination of orderBy(), startAt() and endAt(), see the documentation here: https://firebase.google.com/docs/firestore/query-data/order-limit-data?authuser=0
var searchString = 'Sh' //Example of value
firebase
.firestore()
.collection('yourCollectioName')
.orderBy('username')
.startAt(searchString)
.endAt(searchString + '\uf8ff')
.get()
.then(...)
The character \uf8ff used in the query is after most regular characters in Unicode, therefore the query matches all values that start with searchString.
An approach for full text search recommended by Firebase is to use Algolia https://firebase.google.com/docs/firestore/solutions/search
Using Firestore’s queries with start at and end at with an ordered collection certainly work. If you have a need for full text search within your app elsewhere that requires spelling tolerance etc for example then moving to Algolia is worth considering instead of using compound firestore queries

Resolve Firestore References

Firebase Firestore has a reference type while defining fields of a document which allows us to add a reference to another document via its "Document path".
For example, I have the document animals/3OYc0QTbGOTRkhXeiW0t, with a field name having value Zebra. I reference it in the array animals, of document zoo/xmo5wX0MLUEbfFJHvKq6. I am basically storing a list of animals in a zoo, by referring the animals to the corresponding animal document in the animals collections.
Now if I query a specific document from the zoo collection, will references to the animals be automatically resolved? Will I the get the animal names in the query result? If not, how can I achieve this?
All document queries in Firestore are shallow, meaning that you only get one document in return for each document requested.
References in a document are not automatically fetched - you will have to make subsequent queries using the references in the document to get those other documents on your own.
Same thing with documents in subcollections - they require separate queries.

Resources