Can Firebase Cloud Function support wildcard collection name? - node.js

I am trying to support multiple clients in Firebase with Firestore. And for a collection, each company has one copy. For example, each company has a collection "forms" in such a way: (prefix + collection name)
_c_companyABC_forms
_c_companyXYZ_forms
_c_companyAnyName_forms
...
There might be 10-20 companies but they all have the same database structure, I just use prefixes to distinguish them.
Is there a way for Firebase Cloud Function to be able to listen to the document change for all the collections above also I can extract the company name from the path, something like below (where I use * as a pseudo-code to indicate any name in that collection name)
exports.formChangeTask = functions.firestore
.document('_c_*_forms/{formId}')
.onWrite(async (change, context) => {
const path = context.resource.name.split('/');
const collectionName = path[0];
const companyShortName = collectionName.split('_')[2];
}

Why not create a simple form without prefix collection and collect their all documents there? Or create collection with companies and assign to document's form subcollection with documents?
You are trying to add extra, useless complexity to your database. If you want to extend database by one client, you will need to add every time extra security rules by your self or create extra complex function that will do it for you every time new client apers not mention that you will need to track somehow all those collections names because just admin SDK can get list of collection names.
Try to think about collection names as a tables names in a regular database.

The simplest database schema I can think of would be to add all companies into a single collection. The structure might look like this:
db
|
--- companies (collection)
|
--- $companyId (document)
|
--- name: "ABC"
In this way, you can get all companies in the database, if you need to. You can also get only a company with a specific name using where('name', '==', 'ABC'). If you however, need to add, for example, some forms to each company, then you should simply add a sub-collection under each company document, a case in which the database schema should look like this:
db
|
--- companies (collection)
|
--- $companyId (document)
|
--- name: "ABC"
|
--- forms (collection)
|
--- $formId (document)
|
--- name: "Any"
In this way you can always query to get all forms of a specific company and use a collection group query, to get all forms of all companies.

Related

How to fetch all documents from a firebase collection where each document has some sub collection and in sub collection there is a document?

I am making an Admin dashboard. I want to show all user's details and their orders. When I want to fetch all documents inside the user collection its returning empty. For more In user collection, each document has some sub-collection. In the account sub-collection, there is a document exists with name details where user account details are available as shown in snapshots.
My code is
export function getUsers() {
return firebase.firestore().collection("users").get();
}
If you store user's details directly in the document instead of 'account' sub-collection then fetching "users" collection will return all users' documents with their data. If you say there's no reason then I'd recommend doing this.
Other option would be to use collectionGroup query on "account" which will fetch all the documents from sub-collections named as "account" i.e. giving you every user's account details.
const snap = await db.collectionGroup('account').get()
const users = snap.docs.map(d => ({id: doc.ref.parent.parent.id, data: d.data()))
Here, id is user's document ID.
Firestore queries only access a single collection, or all collections with a specific name. There is no way to query a collection based on values in another collection.
The most common options are:
Query the parent collection first, then check the subcollection for each document. This approach works best if you have relatively few false positives in the parent collection.
Query all child collections with a collection group query, then check the parent document for each result. This approach works best if you have relatively few false positive in your child collection query.
Replicate the relevant information from the child documents into the parent document, and then query the parent collection based on that. For example, you could add a hasOrders field or an orderCount in the user document. This approach always gives optimal results while querying, but requires that you modify the code that writes the data to accommodate.
The third approach is typically the best for a scalable solution. If you come from a background in relation databases, this sort of data duplication may seen unnatural, but it is actually very common in NoSQL databases where you often have to change your data model to allow the queries your app needs.
To learn more about this, I recommend reading NoSQL data modeling and watching Getting to know Cloud Firestore.

Database modeling for firestore. Collection - categories

So i'm trying to model a database schema for firestore. It's called categories and i don't know how to store items. I tried watching https://fireship.io/lessons/advanced-firestore-nosql-data-structure-examples/. It looks logical, there are documents witch hold their children id and parent id, if parent is top level, then parent is null, but how to send data from back-end(Node.js) to front-end(Vue.js).
Is there any other approach?
You can read the data from Firestore using its service from the firebase package.
Let's say you have a collection (called a table in SQL databases) called Users and you want to access the document (called a row in SQL databases) with the id of 1. You can do this via this snippet:
import firebase from 'firebase/app'
import 'firebase/firestore'
const firestore = firebase.firestore()
const data = firestore.collection('Users').doc('1').get()
.then(snapshot => {
console.log(snapshot.data())
})
The important thing to note when working with Firestore is that there are collections and documents. Collections, for example categories_name can store multiple documents, which are stored by {id}. For example,
.collection("categories")
.doc("all")
.collection("todos")
.doc("DataForTodo")
.set(data);
For more information on exactly how to structure Firestore, please refer to the official documentation: https://firebase.google.com/docs/firestore/manage-data/structure-data

How to model MongoDB User Schema and their corresponding data?

I'm creating a Google Keeper replica where a user can log in and the list of todo list for that user is stored.
I'm new to mongoDB, express, and react, and I was wondering how someone would go about doing this. Would you create a User Schema with the "list objects" or create a User Schema and a separate List schema.
I think the creating one schema would be more efficient, but when I go to update or delete a note, I don't know how I would target a specific note without an ID since the ID would be associated with the entire user schema.
Thank you!
You can assign an unique id to the list entries while inserting them into the database. For example, you can use timestamp. The structure of your list items will be something like-
{ itemId: "1595488458403", value: "Do the laundry" }
As the items will be created one by one, therefore, there timestamps will be different. To create the timestamp of the present time, use-- new Date().getTime()
Here's the roadmap
Bring the value of the list item from your frontend to the backend. (say, "Do the laundry")
Define a variable "itemId" in the backend route:
itemId = new Date().getTime()
While inserting the item to the user's list of to-dos, insert:
{ itemId: itemId, value: "Do the laundry" }

Why is data retrieved from Firestore returning empty array when there are subcollections present?

I am trying to retrieve data from my the my firestore database using angularfire2.
This is what my current database looks like. I have a users collection that contains the userId doc which binds the userDetails and userPosts together.
However when I query this collection, it returns an empty array in the console.
I am using a firebase function to retrieve the data.
Firebase Function Index.ts
export const getFeed = functions.https.onCall(async (req,res) =>{
const docs = await admin.firestore().collection('users').get()
return docs.docs.map(doc => {
return {
postID: doc.id,
...doc.data()
}
})
})
TS File
tabTwoFeedInit (){
const getFeed = this.aff.httpsCallable('getFeed')
this.ajax = getFeed({}).subscribe(data=> {
console.log(data)
this.posts = data
})
}
How can I retrieve data from this firebase database successfully?
Firestore reads are shallow, and so they won't return subcollections automatically. Thus, your get() will only return the document ID, since the document has no fields.
To return the subcollections of a document, you need to call the getCollections method on that document. This can only by done by the admin API, but that should be fine for you since you are running inside a cloud function. As the documentation notes, it is generally expected that collection names are predictable (as they appear to be in your case), but if they aren't, you might consider restructuring your data.
Why are shallow reads desirable? It makes it possible to avoid retrieving potentially large collections of information that might be associated with, say, a user, so you can structure data more naturally. Depending on the size of the data, its possible that a field that is a map might make more sense for userDetails (but a collection is probably the right thing for userPosts).
If you are just creating the cloud function to retrieve the posts from this structure. I would prefer to restructure the database a bit and just use Collection Group Query on client side (with no requirement of cloud functions) to pull the posts.
By restructuring I mean, you should store userID inside the documents of userPosts sub collection.
Then simply user Collection Group Query to retrieve post of specific users.
The syntax is of firebase javascript library. You can find it's equivalent of angularfire
let posts= db.collectionGroup('userPosts').where('userId', '==', 'some-user-id');
I also ran into the same problem but I solved it by adding a field to the particular document i am trying to retrieve.Sometimes the cause is because the documents you are trying to get has no field in it. What I mean is that a document must have a field before the server can recognize it as an existing document.
Possibly there may be no field in the document you are trying to retrieve which makes tye server say it is not existing and so will not be retrieved.
So if you run into this problem, then consider adding a field to that document before you can successfully retrieve it.

MongoDb integrating with external db

I have a database which contains data from two separate systems/servers. The first is generated locally [I develop and create this data] (users, activity logs, orders, ...). The second comes from a "product provider" [I only have READ access from API] These objects were created by MySQL and sent in JSON. They already have an "id" property.
With NodeJS, I use request to get a product by "id", and then store it with newProduct.save() appends an _id.
In products, "id" is necessary form relationships with the other collections in my database (such as products_price), and access dynamic endpoints, such as "products/:id/promos".
Note that products are constantly being updated externally and I need to be able to update my documents by "id" not by "_id" as the external server has no knowledge about "_id." [id is unique on a collection level, as each collection is a fresh iteration]
For my first question: should I treat "product.id" as a "regular" MongoDB field and use aggregate/lookup to merge documents from my collections? Or should I overwrite ObjectID() with id? (before saving rename "id" to "_id")
At some point, Orders (local) and Products (external) need to form a relationship where Order _id and Product id (or _id) are stored together for easy retrieval.
Which id do I use in this case?
if you are pretty sure that 'id' coming from your product provider API is unique you better use that as _id (overwrite _id), it will save you:
an unneeded index ('_id' is indexed any way)
some CPU cycles that mongoDB would take to produce the ObjectID
some disk and memory space
(*) even if you find yourself dealing with many different product providers, assuming its one is using his own unique product id you could use a combined _id to make it unique as:
_id = {provider: 'foo', id: xxx}
or _id = [provider_name, product_id]
or _id = provider_name + product_id
etc. etc.
in this use case of multiple providers format depends on how you plan to fetch those products later.

Resources