Firestore: Wherein custom object array? - firebase

im trying to make a sort of chat app with the following architecture:
Its currently working as follows: in a root doc, there is an array of chat objects. Each chat object spawns a message doc, which contains an array of message objects. Same logic for comments.
Im thinking about a function to update all posts relative to the associated user, IE if a user changes their name all associated comments will be updated. Is this possible with the WhereIn() function? Or should i edit the architecture to something more like each document being its own message/comment? Thanks!

An in clause checks if a specific field is equal to one of a set of values, which doesn't apply here.
You might be thinking of array-contains, but that wouldn't work in your current structure either. The array-contains only matches exact an item in the array if it completely/exactly matches the value in the query.
The common way to allow such a query is to add a participants array field to each document where you store the UIDs of all participants in that doc. Then you can do an array-contains query against that.

Related

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.

Find the last element present in the array via Firestore rules

I am trying to work on restricting people who are able to like the posts on an application built on firebase.
I need to verify if the last element of the array exists.
However, I am unable to find the last element present inside array.
I have an array 'Likes' in cloud firestore which has 2 parts - userName and userId
How will I be able to find the last userId present in the array via firestore rules
I have tried below as can be done in python but doesnt work -
request.resource.data.likes[-1].userId
What you call an array, is actually known as a List object in Firestore security rules. This type indeed does not recognize negative offsets (honestly: most systems I know of don't), but you can get the same by using size.
So something like:
request.resource.data.likes[request.resource.data.likes.size()-1].userId

How to get documents from Firestore without a single document id

So i have a collection named products in my Firestore and it contains all my products.
Since i am using Tag widget it gives me an error when there is more than one item sharing the same tag. So for my Products Builder i want to give a list that contains all my products without a specific product catched by his id, so stream property of my Stream Builder can be like this.
StreamBuilder(stream: Firestore.instance.collection('products').WITHOUT SPECIFIC PRODUCT ID .snapshot()
builder:...)
How can i do that?
It's not possible to construct a query for all documents except one. There are no inequality filters in Firestore.
Since it's just one document, it makes sense to simply do the query for all documents, the have the client code remove the one you don't want from the result set.

Select documents where array field contains one of value from given array (array matching)

Consider the following data in Firestore:
I also have a set of selected by the user he's interested in: ['python', 'sql', 'swift']
How to get all the documents from my collection where tags field contains at least one of tags selected by the user? I'm aware that Firestore doesn't have such operator out of the box. Probably I need to rethink the way I store data somehow? Please point me to the right direction.
With the way you have your data structured, you will need to make one array-contains type query for each of the tags the user is interested in, then merge the results of each of those queries in your app code.

Way to store Tags for good Query options?

I have a Collection of Posts and a Collection of Users. Posts have certain attributes which are irrelevant for this, but they also have one attribute called tags right now its an array with certain words.
A User can follow certain Tags so he has a attribute followedTags which is also an Array right now that contains the Tags he follows.
Now one of the use cases for this is a User Feed to Show only Posts with Tags he follows, the Problem is that I only found methods to query this for ONE Attribute at a time(in Arrays). Since I dont wanna run 20 Querys for 1 Feed (For example if the user follows 20 Tags) I thought maybe I could make a smart change in the data modell itself, any suggestions?
Problem is that I only found methods to query this for ONE Attribute at a time(in Arrays)
If you try to chain multiple whereArrayContains() methods, you're most likely getting the following error:
Caused by: java.lang.IllegalArgumentException: Invalid Query. Queries only support having a single array-contains filter.
So unfortunately Firestore can only allow a single call to whereArrayContains() method in query.
Since I dont wanna run 20 Querys for 1 Feed (For example if the user follows 20 Tags)
If you have a reasonable number tags, you can create each tag as a separate property. In this case, it is allowed to call Firestore Query's whereEqualTo() multiple times.
If this is not the case, then you should consider augmenting your database structure to allow a reverse lookup by creating each tag as a seprate object (document) in a tagCollection. Under each document you can create a new collection named tagPost in which you should add all the posts that are labeled with a specific tag.

Resources