I'm trying to migrate the following structure from real time database to Firestore :
• Resources
o SENT
resId1
• name : xxxx
• url : xxx
resI2
• name ……
o ACCEPT
resId3
etc……
o REFUSED
restIdn
etc….
So under root Node" Resources" I have some sub nodes (SENT, ACCEPT, REFUSED, ...) that contain List of resources items.
With Firestore it seems I can't have subCollection directly under collection (When I try to reproduce this structure with Firestore in the admin console I need to create an intermediate document like:
Collection --> document --> SubCollection --> documents
Witch lead to that structure:
Resources --> SENT --> SENT --> resId1 {name: xxx, url: yyyy}
So the sub node "SENT" is duplicated twice (one for the document and one for the sub collection).
This is not an improvement or a simplification at all, if I compare with firebase real time database.
Do I miss something? What is the best way for such kind of database structure?
(Edited 10/13/2017 # 11am)
It seems that there are two structures that will work for you.
Option 1: Three Root Collections
Create three collections at the root of your Firestore database
resources-sent
resources-accept
resources-refused
Each one contains documents.
Option 2: One Root Collection
Firestore allows for compound queries, so you could just make one collection at the root called resources and add a type parameter to each document where type is one of [sent, accept, refused].
Then you can do queries like this:
// Get all sent resources
db.collection("resources").where("type", "==", "sent").get()
Because of Firestore's built in indexes this query will always be fast!'
Option 3: Subcollections.
Create a root collection called resources containing only three docs:
sent
accept
refused
For each of these docs create a resources subcollection.
So to get all sent resources:
// Get all sent resources
db.collection("resources").doc("sent").collection("resources").get()
Related
Say I have the following structure:
collectionA/documentA/collectionB/documentB
But I have set documentB directly to the above path (without explicitly creating collectionA, documentA or collectionB), so the document "documentA" is a non existent document that does not show in queries if I list all documents of collectionA even with admin sdk.
However the firebase web console somehow manages to list such documents. Not only that, I have also seen a third party app Firefoo list such documents and somehow even paginate the results. How can I achieve this?
Turns out this can be achieved with the
Firestore REST API v1
using the listDocuments endpoint and setting query param showMissing to true.
All Firestore queries are based on having some data about that document present in the index that is used for the query. There is no public API to show non-existing documents.
The Firebase console shows these documents based on getting a list of all collections, and then building a data model from that.
I use Google Firestore for my iOS app built in Swift/SwiftUI and would like to implement the Snapshot listeners feature to my app.
I want to list all documents in debts collection in realtime by using snapshot listeners. Every document in this collection has subcollection debtors, which I want to get in realtime for each debts document as well. Each document in debtors has field userId, which refers to DocumentID in users collection which I would also love to have realtime connection on (for example when user changes his name I would love to see it instantly in the debt entity inside the list). This means I must initialize 2 more snapshot listeners for each document in debts collection. I'm concerned that this is too many opened connections once I have like 100 debts in the list. I can't come up with no idea apart from doing just one time fetches.
Have anyone of you ever dealt with this kind of nested snapshot listeners? Do I have a reason to worry?
This is my Firestore db
Debts
document
- description
- ...
- debtors (subcollection)
- userId
- amount
- ...
Users
document
- name
- profileImage
- email
I uploaded this gist where you can see how I operate with Firestore right now.
https://gist.github.com/michalpuchmertl/6a205a66643c664c46681dc237e0fb5d
If you want to read all debtors documents anywhere in the database with a given value for userId, you can use a collection group query to do so.
In Swift that'd look like:
db.collectionGroup("debtors").whereField("userId", isEqualTo: "uidOfTheUser").getDocuments { (snapshot, error) in
// ...
}
This will read from any collection name debtors. You'll have to add the index for this yourself, and set up the proper security rules. Both of those are documented in the link I included above.
I have a firestore collections "Usuarios", in this collections have a lot Documents, each Documents is a one user from my app, i need add more fields on the specific document, but, the Id of document is different that my (CurrentUser.getUUid());.
See here:
What I need is to write several more fields (dynamically) to a logged in user document, but currentUser.getUid returns a document field, not the document ID itself. Que seria o necessário para gravar naquele documento.
My solution was use other method to add documents to collection and the uuid from getCurrent user is already the id of document, i do not know if it is correct (for security), but, was my solution...
I have this Firebase Cloud Firestore schema:
my_db -> users -> uid -> places -> placeId
and
my_db -> places -> placeId
Will this collection group queries get places within both path? If yes, for sure I get duplicates. Is there any way I can avoid that?
Edit: My goal is to get all places within all users and not all places within all collections in my database.
Collection group queries always look at all top-level collections and subcollections with any path at any depth with the name you provide. There is no way to change this behavior. You will need to name your collections differently if you want to limit the scope of the query.
If you can't change your database structure, you could at least ignore the results from the collections you're not interested in by filtering the documents on the client by looking at the path of the document in the snapshots you receive, and deciding for yourself if you should process that document.
Another strategy would be to put a boolean field in the documents of each one of your subcollections that does not exist in the top-level collection documents. You can use this field to query only for only the documents in the subcollections.
I am building an iOS app that is using Cloud Firestore (not Firebase realtime database) as a backend/database.
Google is trying to push new projects towards Cloud Firestore, and to be honest, developers with new projects should opt-in for Firestore (better querying, easier to scale, etc..).
My issue is the same that any relational database developer has when switching to a no-SQL database: data modeling
I have a very simple scenario, that I will first explain how I would configure it using MySQL:
I want to show a list of posts in a table view, and when the user clicks on one post to expand and show more details for that post (let say the user who wrote it). Sounds easy.
In a relational database world, I would create 2 tables: one named "posts" and one named "users". Inside the "posts" table I would have a foreign key indicating the user. Problem solved.
Poor Barry, never had the time to write a post :(
Using this approach, I can easily achieve what I described, and also, if a user updates his/her details, you will only have to change it in one place and you are done.
Lets now switch to Firestore. I like to think of RDBMS's table names as Firestore's collections and the content/structure of the table as the documents.
In my mind i have 2 possible solutions:
Solution 1:
Follow the same logic as the RDBMS: inside the posts collection, each document should have a key named "userId" and the value should be the documentId of that user. Then by fetching the posts you will know the user. Querying the database a second time will fetch all user related details.
Solution 2:
Data duplication: Each post should have a map (nested object) with a key named "user" and containing any user values you want. By doing this the user data will be attached to every post it writes.
Coming from the normalization realm of RDBMS this sounds scary, but a lot of no-SQL documents encourage duplication(?).
Is this a valid approach?
What happens when a user needs to update his/her email address? How easily you make sure that the email is updated in all places?
The only benefit I see in the second solution is that you can fetch both post and user data in one call.
Is there any other solution for this simple yet very common scenario?
ps: go easy on me, first time no-sql dev.
Thanks in advance.
Use solution 1. Guidance on nesting vs not nesting will depend on the N-to-M relationship of those entities (for example, is it 1 to many, many to many?).
If you believe you will never access an entity without accessing its 'parent', nesting may be appropriate. In firestore (or document-based noSQL databases), you should make the decision whether to nest that entity directly in the document vs in a subcollection based on the expect size of that nested entity. For example, messages in a chat should be a subcollection, as they may in total exceed the maximum document size.
Mongo, a leading noSQL db, provides some guides here
Firestore also provided docs
Hope this helps
#christostsang I would suggest a combination of option 1 and option 2. I like to duplicate data for the view layer and reference the user_id as you suggested.
For example, you will usually show a post and the created_by or author_name with the post. Rather than having to pay additional money and cycles for the user query, you could store both the user_id and the user_name in the document.
A model you could use would be an object/map in firestore here is an example model for you to consider
posts = {
id: xxx,
title: xxx,
body: xxx,
likes: 4,
user: {refId: xxx123, name: "John Doe"}
}
users = {
id: xxx,
name: xxx,
email: xxx,
}
Now when you retrieve the posts document(s) you also have the user/author name included. This would make it easy on a postList page where you might show posts from many different users/authors without needed to query each user to retrieve their name. Now when a user clicks on a post, and you want to show additional user/author information like their email you can perform the query for that one user on the postView page. FYI - you will need to consider changes that user(s) make to their name and if you will update all posts to reflect the name change.