How to use composite query in firestore? - firebase

I have a question about firestore query.
I'm working react native project with firestore
I made index to retrieve data which has been voted in a week.
So, my code is like below.
db.getData
.where('updatedAt' '<=' now - week)
.orderBy('count', 'desc')
I know it's not allowed in firestore.
Any idea of this?
If I need to restructure DB, then please show me how it looks.

It depends on the type of updatedAt data. Is is a date object or a number or string timestamp? I prefer to store updates with plain Date.now() timestamps (as number type). So, if updatedAt is a number it's a simple comparison. Tip, ...just name it updated rather than updatedAt, so you have created and updated fields.

Related

Query only specific field with firestore

I use this code to get a collection snapshot from Firestore.
firestore().collection('project').where('userID', '==', authStore.uid).onSnapshot(onResult, onError);
This returns a huge amount of data, but I only need a few fields. Is it possible to query only a specific field? For example, if I only need the projectName and the creationDate fields.
Is it possible to query only a specific field?
No, that is not possbile. A Firestore listener fires on the document level. This means that you'll always get the entire document.
For example if I only need the projectName and the creationDate fields.
You cannot only get the value of a specific set of fields. It's the entire document or nothing. If you, however, only need to read those values and nothing more, then you should consider storing them in a separate document. This practice is called denormalization, and it's a common practice when it comes to NoSQL databases.
You might also take into consideration using the Firebase Realtime Database, for the duplicated data.

Firebase Firestore Query Date and Status not equals. Cannot have inequality filters on multiple properties

I want to query by date and status. I created an index for both the fields, but when querying it throws an error:
Cannot have inequality filters on multiple properties: [created_at, status]
const docQuery = db.collection('documents')
.where('created_at', '<=', new Date()) // ex. created_at later than 2 weeks ago
.where('status', '!=', 'processed') // works when == is used
.limit(10);
'status' is a string and can be multiple things.
I read about query limitations, https://firebase.google.com/docs/firestore/query-data/queries but that is such a simple thing for any database to do, what is a good solution for such a problem? Loading entire records is not an option.
The issue isn't that it's a "simple" thing to do. The issue is that it's an unscalable thing to do. Firestore is fast and cheap because of the limitations it places on queries. Limiting queries to a single inequality/range filter allows it to scale massively without requiring arbitrarily large amounts of memory to perform lots of data transformations. It can simply stream results directly to the client without loading them all into enough memory to hold all of the results. While it's not necessary to understand how this works, it's necessary to accept the limitations if you want to use Firestore effectively.
For your particular case, you could make your data more scalable to query by changing to your "status" field from a single string field to a set of boolean fields. For each one of the possible status values, you could instead have a boolean field that indicates the current status. So, for the query you show here, if you had a field called "isProcessed" with a boolean value true/false, you could find the first 10 unprocessed documents created before the current date like this:
const docQuery = db.collection('documents')
.where('created_at', '<=', new Date())
.where('processed', '==', false)
.limit(10);
Yes, this means you have to keep each field up to date when there is a change in status, but you now have a schema that's possible to query at massive scale.

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 correctly define ID and DateTime fields in Firebase Firestore database?

I am using Firebase FireStore database for the first time and I have the following question.
I have created a calendar collection. This collection will contains document representing events that have to be shown into a Calendar implemented by an Angular application.
So I am defining the following fields for these documents:
id: int. It is a unique identifier of the specific document\event.
title: string. It is the event title.
start_date_time: string. It specifies the date and the time at which the event starts.
end_date_time: string. It specifies the date and the time at which the event ends.
And here I have some doubts:
Is the id field required? From what I know I will have the document UID that will ensure the uniqueness of the document. If not strongly required adopt an id field can be convenient have something like an auto increment field? (I know that I have to handle in some other way the auto increment because FireStore is not a relational DB and doesn't automatically handle it). For example I was thinking that can be useful to order my document from the first inserted one to the last inserted one.
It seems to me that FireStore doesn't handle DateTime field (as done for example by a traditional relational database). Is this assumption correct? How can I correctly handle my start_date_time and end_date_time fields? These field have to contains the date time used by my Angular application? So for example I was thinking that I can define it as string field and put into these fields values as 2020-07-20T07:00:00 representing a specific date and a specific time. It could be considered a valid approach to the problem or not?
Is the id field required?
No fields are required. Firestore is schema-less. The only thing a document requires is a string ID that is unique to the collection where it lives.
There is no autoincrement of IDs. That doesn't scale massively the way Firestore requires. If you need ordering, you will have to define that for yourself according to your needs.
In general, you are supposed to accept the randomly generated IDs that the Firebase client APIs will generate for you. Ordering is typically defined using a field in the document.
It seems to me that FireStore doesn't handle DateTime field
Firestore has a timestamp field type that stores moments in time to nanosecond precision. There is no need to store a formatted string, unless that's something you require for other reasons.

How to query one field then order by another one in Firebase cloud Firestore?

I'm struggling to make a (not so) complex query in firebase cloud firestore.
I simply need to get all docs where the id field == a given id.
Then order the results by their date and limit to 10 results.
So this is my current situation
db.firestore().collection('comments')
.where("postId",'==',idOfThePost)
.orderBy('date','asc').limit(10).get().then( snapshot => {
//Nothing happens and the request wasn't even been executed
})
I can get the result only if i don't use the orderBy query but i have to process this sorting for the needs of my application.
Someone has an idea to help me to fix this ?
thanks
You can do this by creating an Index in the firestore.
The first field of the index should be the equality field and the second field of the index should be the order by field.
Given your problem, you would need the following index:
first field: postId, 'asc'
second field: date, 'asc'
Please check the doc. It says
However, if you have a filter with a range comparison (<, <=, >, >=), your first ordering must be on the same field
you can try this code
db.firestore().collection('comments')
.where("postId",'==',idOfThePost)
.orderBy('postId')
.orderBy('date','asc').limit(10).get().then( snapshot => {
.....
})
My Workaround
If you're googling this you've probably realized it can't be done traditionally. Depending on your problem though there may be some viable workarounds, I just finished creating this one.
Scenario
We have an app that has posts that appear in a feed (kind of like Reddit), each post has an algorithmic score 'score' and we needed a way to get the 'top posts' from 12-24 hours ago. Trying to query sorted by 'score' where timestamp uses > and < to build the 12-24 hour ago range fails since Firebase doesn't allow multiple conditional querying or single conditional querying with an descending sort on another field.
Solution
What we ended up doing is using a second field that was an array since you can compound queries for array-contains and descending. At the time a post was made we knew the current hour, suppose it was hour 10000 since the server epoch (i.e. floor(serverTime/60.0/60.0)). We would create an array called arrayOfHoursWhenPostIsTwelveToTwentyFourHoursOld and in that array we would populate the following values:
int hourOffset = 12;
while (hourOffset <= 24) {
[arrayOfHoursWhenPostIsTwelveToTwentyFourHoursOld addObject:#(currentHour+hourOffset)];
hourOffset++;
}
Then, when making the post we would store that array under the field hoursWhenPostIsTwelveToTwentyFourHoursOld
THEN, if it had been, say, 13 hours since the post was made (the post was made at hour 10000) then the current hour would be 10013, so we could use the array-contains query to see if our array contained the value 10013 while also sorting by algorithm score at the same time
Like so:
FIRFirestore *firestore = [Server sharedFirestore];
FIRCollectionReference *collection = [firestore collectionWithPath:#"Posts"];
FIRQuery *query = [collection queryOrderedByField:#"postsAlgorithmScore" descending:YES];
query = [query queryWhereField:#"hoursWhenPostIsTwelveToTwentyFourHoursOld" arrayContains:#(currentHour)];
query = [query queryLimitedTo:numberToLoad];
Almost Done
The above code will not run properly at first since it is using a compound index query, so we had to create a compound index query in firebase, the easiest way to do this is just run the query then look at the error in the logs and firebase SDK will generate a link for you that you can navigate to and it will auto-generate the compound index for your database for you, otherwise you can navigate to firebase>database>index>compound>new and build it yourself using hoursWhenTwelveToTwentyFourHoursOld: Arrays, score: Descending
Enjoy!
same here, it is weird why can't. below is another sample. can't get the result. Hoping firebase can reply about this and update the document.
dbFireStore.collection('room').where('user_id.'+global.obj_user.user_id,'==',true).orderBy('last_update').get().then((qs)=>{
console.log(qs);
});
using other work-around solution is javascript array and array.sort()
I ran into the same issue yesterday on Android. The Callback was just not called. Today I suddenly got an error message. FAILED_PRECONDITION: The query requires an index. It even contains a URL in the error message to generate that index with one click.
It seems that if you want to use orderBy on your data, you need to create an index for that field. The index also needs to be in the correct order (DESC, ASC).
As per firestore document,
If you attempt a compound query with a range clause that doesn't map to an existing index, you receive an error. The error message includes a direct link to create the missing index in the Firebase console.
So just click that link you get in Logcat, it will be redirected to create index page, just create index. It will take some time. after enabling composite index, you will get the result as your requested query.
Stumbled across this looking for help when i found that using the orderBy function didnt work and the documentation still says it does not support it. A bit weird and unclear to be honest, because it does support it so long as you index your Firestore database. For example, this query now works fine for me having set up indexing:
const q = query(docRef, where("category", "==", 'Main'), orderBy('title', 'asc')
Indexing in Firestore
Console Log that even gives you the url to automatically create the index if you try and run with the above command.
Maybe I am missing something, or a later version of Firebase (I am using v9) simply does support it.

Resources