Find the last element present in the array via Firestore rules - firebase

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

Related

Firestore: Wherein custom object array?

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.

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.

Can we trigger firebase cloud functions when we create a collection (not "document")?

I am trying to achieve a scenario where firebase triggers only once when a collection is created and not every time that a document is added to the collection. Let's say I want it to trigger only for the first document added to the collection and not for every other document added to the same collection. How can that be done? Please help !!!
There is no such trigger for Cloud Functions. Your trigger path must specify exactly one document, or use wildcards to specify a path that could possibly match many documents.
If you want to know what a collection contains its first document, you will have to either:
Maintain a count of documents in that collection (perhaps in yet another document in another collection), and trigger of the change of that value when it goes from 0 to 1.
Query all of the documents in the collection where a document was just created in order to figure out if it was the first one.
Both of these requires a fair amount of extra code - very much not trivial to implement correctly for arbitrary collections. They could also run into problems under heavy load. If these options won't work for you, I suggest figuring out another way to get you function to trigger at the right time.
While It's not possible to do directly. You can check if collection contains any elements, and if it does it means that it exists.
const result =await firestore.collection("collection").limit(1).get()
if(result.size){
// Collection exists
}

How do I create a Firebase collection?

So I am writing a chat application that I want to have multiple rooms, however, I can't find a button on the Firebase console that I can add child collections.
I've tried exporting, editing, then importing but that doesn't seem to do much. I have looked at some Firebase tutorial's but I can't find one that explains this.
Anything you enter in the console has to have a value itself, or at least one child (with a value). This is because Firebase does not explicitly store "null" or empty values in the database. You can enter the name of the collection and then rather than a value use the + button at the right to start adding children to it and so on until you reach a node with a value:
You cannot however simply create a placeholder for a collection that has no values. If you need a collection but can't initialize any of its data, just use your security rules to define what's allowed and write your client code knowing it may or may not exist. Firebase allows you to attach listeners to nodes that don't exist yet.

How do I stop Firebase from creating an additional nested object or how can I access the newly generated string?

Problem: Whenever I add an order to the orders array, an additional nested array element(-KOPWA...) gets added. I wouldn't mind except, I don't know how to access that nested string to access it's child nodes.
Example of database node for users below:
firebase.database().ref('users/'+userIdState+'/orders/'+<<unique numbervariable>>).push({
"order":{"test":"product","quantity":2}
});
I'm using the above code to push new json objects with a unique number to the firebase array. Still the nested array with the weird strings gets generated.
Can anyone help me understand how to either: create my own nested array with my own unique string or how to access the nested string that gets generated from firebase so I can access it's children nodes.
Multiple instances of nest arrays will be generated by users.
Any help is much appreciated.
Thanks,
Moe
You're experiencing this behaviour because Firebase's push is not the same as an array push. I recommend reading this article to understand how it works.
As for a solution, you can simply change push to set in your code. This will create the structure you were (presumably) expecting, that is
1:
order:
...
This is however potentially unsafe, if you allow concurrent writes (i. e. if the "unique number" in your example is not always unique).
Afaik Firebase recommends using push to safely create collections/"arrays". You can retrieve the generated key by calling the key property on the reference returned by push. Like this:
var ref = firebase.database().ref('users/'+userIdState+'/orders/'+<<unique numbervariable>>).push({
"order":{"test":"product","quantity":2}
});
var generatedKey = ref.key; // the value you're looking for
If you decide to use it, you can probably just drop the order number you have right now and just use the generated one.

Resources