Check multiple Document exists id Firestore Collection - google-cloud-firestore

I want check within a list of 10 document IDs how many exists in a collection. One way to do it would store the document ID inside the document and use in operator.
const result = await this.afs.collection(path).where('id', 'in', ['doc1', 'doc2']).get();
Is there a way we can avoid storing the document id inside document.

For this particular query, you can use FieldPath.documentId(). It returns a token that you can use in the field path of the query:
this.afs.collection(path).where(FieldPath.documentId(), 'in', ['doc1', 'doc2'])
I've linked you to the plain JavaScript documentation. If you're using Angular, it might have a slightly different way of getting this token value, but it will be in a class called FieldPath.

Related

What is the effect of passing an array to Firestore database's orderBy clause?

Imagine the documents in my Firestore database represent recipes and each document has a 'tags' field, which is an array containing tags for that recipe (for example: spicy, French, vegetarian).
When a user searches for some tags, I use the array-contains-any operator to pull back all the recipes that contain at least one of the tags that a user has searched for. I then order the returned documents by the number of tags that have been matched. For example, if a user searched for 'spicy French vegetarian' then the documents whose 'tags' field contains all three of those tags appears first, followed by the documents whose 'tags' field contains two of those tags etc.
I am currently sorting manually using:
const userSearchedTags: string[] = [...] // array of tags searched for by user
recipes.sort((r1, r2) => {
const r1Tags: string[] = r1.get('tags');
const r2Tags: string[] = r2.get('tags');
const r1TagMatchQuant = r1Tags.filter(r1Tag => userSearchedTags.includes(r1Tag)).length;
const r2TagMatchQuant = r2Tags.filter(r2Tag => userSearchedTags.includes(r2Tag)).length;
return r2TagMatchQuant - r1TagMatchQuant;
});
But I want to paginate this query, which requires them to be ordered by Firebase so I thought I might be able to use the orderBy clause in my query so that Firebase gives me the documents back already ordered by number of matched tags. If this worked then I would be able to use the startAfter() clause to paginate my query. I attempted this:
FirebaseFirestore.instance
.collection('recipes')
.where('tags', arrayContainsAny: userSearchedTags)
.orderBy('tags')
.get();
But it does not return the documents ordered by number of matched tags. I thought it might be returning the documents ordered by the length of the 'tags' array on each document, but further testing has shown it is not doing that either. So my question is how is Firebase ordering these documents when I pass 'tags' (a field that is an array) to the orderBy clause?
Also, is there a way to get Firebase to return the documents to me ordered by the number of matched tags or is the way I am doing it now (manually sorting after pulling back all documents with at least one matching tag) the only way?
The order in which document are returned is typically the same as however the documents are present in the index that is being searched. Assuming your tags are strings, then according to the documentation on data types that'd be:
UTF-8 encoded byte order
So I expect it to be the order of that first tag that it matches, and definitely not the number of tags matched that you were hoping for. That type of results prioritization based on a calculated value is simply not something Firestore queries are made for as it would require that Firestore reorders the matches before returning them to you.

How to create a composite index in Firestore with document Id

So I'm using firebase cloud firestore with swift (but this is a general question with firestore), and I want to sort through some documents using a query, something like
fetchQ.whereField(fieldName, isGreaterThan: startingValue)
But then I want to guarantee some kind of order if the field has the same value, and it stands to reason that the document id is good for this, so I add
.order(by: FieldPath.documentID(), descending: false)
But now I get the error in the console where I have to paste the url in order to create a composite index. I do that, except it's only for the single index "fieldName", leaving out the document id, so obviously I get an error for trying to create a composite index with a single field. I also tried it with two fields plus the document id, and sure enough the url generates a composite index for the two fields but leaving out the document id.
The composite indexing page in the firebase console also does not have an option to create a composite index involving the document id.
So it would seem to me that maybe using document id for sorting is not the intended practice? Should I create a unique id for each document for sorting purposes or if I can use document id for ordering how should I do it?
From the docs: "By default, a query retrieves all documents that satisfy the query in ascending order by document ID" (firebase.google.com/docs/firestore/query-data/…). So the behaviour you want to to achieve is what you get out of the box.

Firestore security rules: check if array contains strings different from user's ID

I know how to check if an array contains a given string (as explained for example here). My requirement however is different: I have a document with an array updatedByHistoryArray written at server side that contains the history of the ids of all users who updated such a document, for example [id1, id2, ..., idn].
I would like to allow a delete operation for this document only if the latter has been updated exclusively by the user who wants to delete it.
So, for example, if a user with id24 wants to delete a document, the updatedByHistoryArray of this document has to be [id24, id24, ..., id24].
Is it possible to implement this requirement in the security rules of Firestore?
It sounds possible. Try using hasOnly() to see if the list field contains only a single user ID.
resource.data.updatedByHistoryArray.hasOnly([request.auth.uid])

Firestore: get an Observable doc from another field which is not the Id

I want to get a single Observable from a collection, but I want to get it from a different field that is not the id. It is possible?
I do not want to do a query and limit to 1. I need to get a Single Observable not an array Observable.
Schema:
Code:
this.afs.doc<Credit>('credits/uid/'+ uid).valueChanges();
Error:
Invalid document reference. Document references must have an even number of segments, but credits/uid/d1Zt8sozYqb6H27lhoJgF1Gx2Cc2 has 3
I am not sure if I understand correctly, but I guess that you want to get document with particular uid field value, not using document id.
This particular error is related with common feature of Firestore, that every document has to be in collection not in document. So path values for documents, (nested as well) are always checked, if the segments (devided by /) number is even ex. collection1/doc1/collection2/doc2/collection3/doc3
As results in your code we have 3 segments (like credits/uid/<uid_value>) so this is the error.
I am not very familiar with angularFire2 itself, but I have done it in JS. The approach is normally to query collection and than use method on the results, which in classic JS returns Query object on which the same methods can be used as on CollectionReference (which extends 'Query' btw - reference 1).
Combining this approach with those references: querying and collection I propose following solution:
this.afs.collection('credits', ref => ref.where('uid', '==', <uid_value>)).valueChanges()
If uid_value will be unique you should get your doc.
Unfortunately I do not have any playground to test the solution so please let me know how it works - or if there will be any additional errors.
I hope it will help! Good Luck!

Get a document from couchbase lite database swift

I want to retrieve a document from database to update it, so I need the document ID, how to get this ID if I didn't create the document with a custom ID (I've used database.createDocument() instead of database.documentWithID("docId"))
Thanks
If you haven't created the document with a custom Id then you can use
some other uniquely identifying property (or properties) of the document to query for the specific document.
Instead of querying and iterating over entire list of documents to find the match, you can create a view with the specified property/properties as index. You can then query for that view with startKey / endKey set to the desired property value. That will return the document of interest.
Alternatively,if it's an option, create the document with a custom ID.