Firestore: Check if virtual document exists - android

I am developing an android app, where users can register. To save user data, I user Firebase Firestore. So, When a user registers, a document with the FirebaseUser.userId as id get created.
val exampleObject = ExampleObject(exampleData1, exampleData2)
val firestoreUser = FirebaseAuth.getInstance().currentUser
firebaseFirestore
.collection("users")
.document(firestoreUser.uid)
.collection("exampleCollection")
.document("exampleDocument")
.set(exampleObject)
The document that gets created for each user only contains collections, therefore, Firestore does only create a "dummy document". So, how can I check if this document exists or not?
firebaseFirestore
.collection("users")
.document(firestoreUser.uid)
.get()
.addOnSuccessListener { doc->
if(doc.exists()) {
} else {
}
}
This does not work, because it is only a "dummy document", that does not really exist

Firestore does only create a "dummy document".
It does not create any document. That document does not exist because you didn't create it at all. What you did do, was only to create a subcollection under a document that never existed. In other words, you just reserved an id for a document in a collection and then you created a subcollection under it.
One thing to remember, in Cloud Firestore documents and subcollections don't work like filesystem files and directories. If you create a subcollection under a document, it does not implicitly create any parent documents. Subcollections are not tied in any way to a parent document.
So note that there is no physical document at that location but there is other data under the location, which is the exampleCollection subcollection. Remember that the "dummy document" that you are talking about becomes a real document only when you write at least a property that can hold a value in it.
So in your case, the following statement:
if(doc.exists())
Will be always evaluated to false.

Related

how to check firebase firestore user data collection is empty

I'm making a note app and I have a problem. It is how to check if Firebase Firestore user data collection is empty.
Query query = firebaseFirestore.collection("mNotes").document(firebaseUser.getUid()).collection("userNotes").orderBy("title", Query.Direction.ASCENDING);
FirestoreRecyclerOptions<firebasemodel> allusernotes = new FirestoreRecyclerOptions.Builder<firebasemodel>().setQuery(query, firebasemodel.class).build(); ```
this code is used to get data from Firestore.
how can I check user data collection is empty with the if statement.
I am a beginner.
How to check Firebase Firestore user data collection is empty?
Firestore doesn't have the concept of an empty collection. If a collection doesn't contain any documents, it doesn't exist at all. So there is no API that can help you check if a collection actually exists. A collection in Firestore will start to exist if there is at least one document present in it.
So to solve this, you might consider checking the number of documents within the collection or subcollection like this:
val db = Firebase.firestore
val usersRef = db.collection("users")
usersRef.get().addOnCompleteListener {
if (it.isSuccessful) {
val numberOfDocs = it.result.documents.size
if (numberOfDocs > 0) {
Log.d(TAG,"users collection already exists!")
}
}
}

How to move a firestore document from one collection to another

Is this possible to move a document from one collection to another in Firestore Database? The reason I want to do this is, when someone deletes an Order, I want to move it to deleted_orders collection rather than deleting the entire document to keep the history.
I want to do this from my app which I have developed in Kotlin.
The only way to move a document from one collection to another collection is to use the following sequence, mimicking a "cut & paste":
Create a copy of the source document in the target collection, i.e. read it from the source collection, get the document fields and create a new document in the target collection with these fields, and then;
Delete the document from the source collection.
In other words, there is no kind of "metadata" attached to a document that defines its parent collection and that you could change in one simple action.
Another approach would be to have all the order docs in a unique collection and a field which holds the order status, e.g. active or deleted. Then changing the order status is just a matter of updating a field.
Note that this approach will not have any negative effect on your queries performance since Firestore query performance is proportional to the size of the result set, not the data set size.
private void insertAllFromOneCollectionIntoAnother(CollectionReference crFrom, CollectionReference crTo) {
QuerySnapshot querySnapshot = crFrom.GetSnapshotAsync().Result;
foreach (DocumentSnapshot documentSnapshot in querySnapshot.Documents) {
crTo.Document(documentSnapshot.Id.ToString())
.SetAsync(documentSnapshot.ToDictionary());
}
}

How to get a document from a Cloud Firestore collection based on index?

For developing an Android app in Kotlin, I am trying to find a way to retrieve a document from a Firestore collection based on a certain index and I am not sure how to go about it. For example, if my collection has 50 documents, what would be the best way to retrieve the 10th document so that I can populate text views on my app based on the field values of that document?
Thank you for taking the time to answer my question.
When you add documents to Firestore using the CollectionReference#add() method, you'll see a random document ID that is assigned automatically for each document. If you check the Firebase Console or you try to query a collection programmatically, you will not find any order for the containing documents. This means that there is no index involved. So it's not correct to say, hey Firestore, give me the 10th document, because when you query a collection of 50 documents, the 10th document might be the one you are interested in, but if you add/delete documents, the 10th document won't be the same.
You might think, ok, I can provide my own document IDs, starting from 0 - 49, and when I query the collection I can order them by their document IDs. But, this is not an option, since the document IDs are always strings, and when you order strings, the order is lexicographical.
The best solution I can think of, if you need to get a particular document from a collection, is to add a specific field in that document, so it can be identified very easily. For instance, you can add a particular name or ID for each document, and then you can perform a query that looks like this:
rootRef.collection("users").whereEqualTo("name", "Bhavey")
In this way, you can simply identify each one of the documents in your collection.
To accomplish this, will require some setup.
let's say you have a collection of documents and you want to manage them and find the 10th document, Firestore generates unique hash id's that are based on the timestamp making them have an inherent order based on the time they were generated.
All you need to do is manage a dedicated document in the parent of this collection that contains an array of all document id's in the subcollection.
reading this document and it's array would get you an ordered list of documents without incurring the additional reads of the documents as other methods, even listing the ID's requires 1 read each.
on the successful operation of it being added, you can use a Cloud Function onCreate trigger to push the document id to the master array and the same with onDelete. But if cloud Functions are not available or you want to brute force it from the client, you can do the following:
db.collection("users")
.document("uid")
.collection("posts")
.add(city)
.addOnSuccessListener { documentReference ->
db.collection("users")
.document("uid")
.update("index", FieldValue.arrayUnion(documentReference.id))
.addOnSuccessListener { Log.d(TAG, "Index Added!") }
.addOnFailureListener { e -> Log.w(TAG, "Index Error", e) }
}
.addOnFailureListener { e -> Log.w(TAG, "Error source document", e) }
Cloud Functions sources:
trigger_a_function_when_a_new_document_is_created
trigger_a_function_when_a_document_is_deleted
*note: to scale this past 1mb firestore document limits - you will need multiple index documents with a counter and to manage this with Cloud Functions. The end result should double your writes without crippling your reads

Firestore best practice for refering to document

I am trying to show to user items, he participated in (for example liked posts). Now I store in user document (in users collection) IDs of documents from another collection (posts) and when I want to show them in recycler view firstly I get IDs from user document. Then I get all posts by IDs. Is there any workaround, where I would be able to store user ID in subcollection of post document and then get query of all liked/commented/whatever posts by user? So user document will not have reference to post's IDs and in posts collection I am able to do something like:
Query ref = from db.collection("posts") get all posts where post.likedBy == user;
I do no like idea of putting all users who liked the post into post document - user downloads all ids.
posts (collection)
-postID (post document)
-authorID, ... (fields)
users (collections)
-userID (user document)
-string[] idsOfPosts (fields)
You should use Subcollections as your data model.
Documents in subcollections can contain subcollections as well,
allowing you to further nest data. You can nest data up to 100 levels
deep.
Also you can use a collection group query to retrieve documents from a collection group instead of from a single collection. The link provides you with sample code snippets in different languages.
EDIT:
Based on the use case you have provided in the comments:
I would say the way you are describing your data model to get all posts liked by a user, it would need a query inside a query. Not sure if it's even feasible or efficient.
Here is my suggestion:
Build your data model similar to the following
This way running the following query (I'm using NodeJs) would give you all posts liked by user1.
let postsRef = db.collection('posts');
const user1 = postsRef.where('Liked', 'array-contains',
'user1');
let query1 = user1.get()
.then(snapshot => {
if (snapshot.empty) {
console.log('No matching documents.');
return;
}
snapshot.forEach(doc => {
console.log(doc.id, '=>', doc.data());
});
})
.catch(err => {
console.log('Error getting documents', err);
});
Output:
EDIT: (11/12/2019)
Based on what you have described in the comments, here is an idea that might solve your issue:
Instead of having a list of the Users who liked the post, you can have a reference to a Document that contains the list of users. You can reference to as many Documents as you wish.
Example:
The Documents can be even in a different Collection.

Delete all subcollections of a particular document(i,e.currentuser doc) in collection when app is killed in kotlin/android [duplicate]

I have a collection called lists and it has documents who is ID represents the list ID. This document has a collection called employees and another one called locations.
The structure looks like this:
(lists)
-listId
(employees)
(locations)
If the user wants to delete a specific list then the problem is that we can't delete listId because that will keep the collection (as was mentioned by Firestore docs).
How can the structure be modeled to fit the needs? I can't seem to get around the need for subcollection.
Any recommendation?
There is no need to restructure your database in order to delete some collections. To delete an entire collection or subcollection in Cloud Firestore, retrieve all the documents within the collection or subcollection and delete them. So in order to delete a specific list, please use the following steps:
Find all documents beneath employees collection and delete them
Find all documents beneath locations collection and delete them
Delete the listId document
If you have larger collections, you may want to delete the documents in smaller batches to avoid out-of-memory errors. Repeat the process until you've deleted the entire collection or subcollection.
Even if the delete operation is not recomended by Firebase team because it has negative security and performance implications, you can still do it but only for small collections. If you need to delete entire collections for web, do so only from a trusted server environment.
For Android, you can use the following code:
private void deleteCollection(final CollectionReference collection, Executor executor) {
Tasks.call(executor, () -> {
int batchSize = 10;
Query query = collection.orderBy(FieldPath.documentId()).limit(batchSize);
List<DocumentSnapshot> deleted = deleteQueryBatch(query);
while (deleted.size() >= batchSize) {
DocumentSnapshot last = deleted.get(deleted.size() - 1);
query = collection.orderBy(FieldPath.documentId()).startAfter(last.getId()).limit(batchSize);
deleted = deleteQueryBatch(query);
}
return null;
});
}
#WorkerThread
private List<DocumentSnapshot> deleteQueryBatch(final Query query) throws Exception {
QuerySnapshot querySnapshot = Tasks.await(query.get());
WriteBatch batch = query.getFirestore().batch();
for (DocumentSnapshot snapshot : querySnapshot) {
batch.delete(snapshot.getReference());
}
Tasks.await(batch.commit());
return querySnapshot.getDocuments();
}

Categories

Resources