This question already has answers here:
How can I write a firebase rule to allow reading only part of a collection/document? [duplicate]
(1 answer)
How to allow only particular fields of a firestore document to be accessed publicly
(2 answers)
Firestore security rules for public and private fields
(2 answers)
Closed 2 years ago.
I want to get a Document from Firestore, but just with some fields of the doc.
I found 2 use cases for this. One of these is:
Users in Firestore
UsersCollection
UserDocument
email
uid
photoUrl
name
If I want to make a chat with all of the users in the users collection, I need to get all the docs in the user collection. But these docs contain some important info that's not useful to our chat, like the e-mail. Can't I just request from firebase the user document without the e-mail field? Can't a hacker find the user document in memory and get their e-mail? The e-mail is not useful for the chat, I only need the name, uid and photoUrl, the e-mail is no use for this feature, but I still get it with the whole document.
How do I "except" or get the document without the e-mail? This is more about security and user privacy.
Another use case for this would be a like system. Let's say we have a post structured like this in firebase:
PostsCollection
PostDocument
number of likes
arrayOfUsersThatLiked
photoUrl
number of likes is incremented by a cloud function that when arrayOfUsersThatLiked is modified, the function adds the length of the array in the number of likes field.
(arrayOfUsersThatLiked is used to not let the user like more than 1 time, so if someone liked the post, arrayOfUsersThatLiked will keep track of who and will not let them like again)
When we get the post document from the db, we will actually get that usersThatLiked array with a lot of uids along with the things that we need, we only need the photoUrl and the number of likes on the post, not the usersThatLiked array. (it would be a pain also to store such a huge string array).
So the main question remains, how do I only get the document with only the fields that I want to? It's a matter of privacy/security and performance.
With the Client SDKs (including the FlutterFire plugin) it is not possible to get only a subset of the fields for a Document. When you fetch a Document you get it with all its fields.
Here are three possible approaches:
Dernomalize your data: You create another collection which contains documents that only have the fields you want to display in the front end. The complexity is that you need to keep the two collections in sync: when you create/modify/delete a doc in the master collection, you need to update the other collection. This can be done from your front-end (e.g. you write to the two collections in a batched write) or with a Cloud Function, triggered in the back-end with an onWrite trigger. In terms of security, you open read access only to the second collection, not to the master one.
Use the Firestore REST API: With the REST API you can use a DocumentMask when fetching documents, which will "restrict a get operation on a document to a subset of its fields. ". Note, however, that a "hacker" could get the collection ID and fetch the documents without the Mask.
Use a Cloud Function to access the Firestore documents. With a Cloud Function, you have full control on what you want to send back to the front-end, since you query the collection from within the Cloud Function and build the response in the Cloud Function. However, using a Cloud Function instead of a Client SDK has several drawbacks: see this article for more details.
Related
I'm managing a company website, where we have to display our products. We however do not want to handle the admin edit for this CPT, nor offer the ability to access to the form. But we have to read some product data form the admin edit page. All has to be created or updated via our CRM platform automatically.
For this matter, I already setup a CPT (wprc_pr) and registered 6 custom hierarchical terms: 1 generic for the types (wprc_pr_type) and 5 targeting each types available: wprc_pr_rb, wprc_pr_sp, wprc_pr_pe, wprc_pr_ce and wprc_pr_pr. All those taxonomies are required for filtering purposes (was the old way of working, maybe not the best, opened to suggestions here). We happen to come out with archive pages links looking like site.tld/generic/specific-parent/specific-child/ which is what is desired here.
I have a internal tool, nodeJS based, to batch create products from our CRM. The job is simple: get all products not yet pushed to the website, format a new post, push it to the WP REST API, wait for response, updated CRM data in consequence, and proceed to next product. Handle about 1600 products today on trialn each gone fine
The issue for now is that in order for me to put the correct terms to the new post, I have to compute for each product the generic type and specific type children.
I handled that by creating 6 files, one for each taxonomy. Each file is basically a giant JS object with the id from the CRM as a key, and the term id as a value. My script handles the category assertion like that:
wp_taxonomy = [jsTaxonomyMapper[crm_id1][crm_id2]] // or [] if not found
I have to say it is working pretty well, and that I could stop here. But I will have to take that computing to the wp_after_insert_post hook, in order to reaffect the post to the desired category on updated if something changed on the CRM.
Not quite difficult, but if I happen to add category on the CRM, I'll have to manually edit my mappers to add the new terms, and believe me that's a hassle.
Not waiting for a full solution here, but a way to work the thing. Maybe a way to computed those mappers and store their values in the options table maybe, or have a mapper class, I don't know at all.
Additional information:
Data from the CRM comes as integers (ids corresponding to a label) and the mappers today consist of 6 arrays (nested or not), about 600 total entries.
If you have something for me, or even suggestions to simplify the process, I'll go with it.
Thanks.
EDIT :
Went with another approach, see comment below.
I am working on a web app for doing certain problems. The database is firestore.
The feature I want to ask is that on a page, there will show {x}/{y},
where x is the number of problems the user completed, and y is the total number of problems, something like 6/10.
Sample image
My doubt is how to design data model to achieve this feature. I already have users collection and problems collection. My thought is choosing one of them, but feeling none of them is good practice:
A. Adding user_response field in each problem doc, which means if there are 1000 users, each problem needs to add 1000 users' responses. It is also hard for new users coming.
B. Quite like A but adding problems_response into user doc.
C. Adding a new collection repsonses which has problem_id, user_id fields.
I am new to NoSQL, thanks for your help.
I added the database structure in the following images:
database-1
database-2
database-3
database-4
database-5
I have a large collection where each document is of a substantial size and therefore cannot be embedded into a single document. For my feature, I need to sort the collection using orderBy and filter the collection using "array-contains" and "==" where the sort and filter parameters are provided by the user. I was wondering if there was an efficient way to do this by cacheing documents that had already been fetched in previous queries. That being said, does firebase do any cacheing itself and does it already optimize what I'm trying to do in this case, or is there any custom cacheing/optimization I can do?
This is what my implementation looks like right now. This works fine for now however it's not very scalable as it creates a new realtime listener each time any of the filter/sort state changes. Is there any way can I improve this to minimize the total number of documents read?
useEffect(() => {
if (!sort || !status || !search) return;
const unsubscribe = firebase.collection("users")
.where("searchTerms", "array-contains", search)
.where("status", "==", status)
.orderBy(sort)
.onSnapshot((snapshot) => {
// update state hook with snapshot data...
});
return () => unsubscribe();
}, [sort, status, search]);
Thank you for any and all help!
Typically, you will want to use a search indexer to enable functionality like this.
The Firebase documentation recommends using Algolia for full-text search. You want to create a cloud function that indexes the data you want to search on, along with the Firestore document ID. On the frontend, you can use the Algolia API to get search results and then fetch the whole document From Firestore when you need to display it.
An alternative to pagination for "big data" and still supporting complex queries can be done by baking the data for the client into a simplified search collection.
This displays only key information that represents a source document by combining the essential data into a dedicated collection of all the results.
Each document can hold up to 1MB of data each and that can equate to roughly 10k-200k entries based on your data size. It does take some time to set up but it has been effective for handling long-lived data within firebase without additional 3rd party solutions.
The key takeaways are as follows:
This is ideal for data that doesn't update too frequently, multiple changes at once can hit the 1 second limit per document.
All search documents contain two properties, a counter to maintain the current entries and an array of strings that represent your essential data.
Each source document needs to maintain a document ID of its entry document for future updates
On update, you find the search index ID, and use arrayUnion and arrayRemove methods, preferably with a transaction and update the source document.
Optionally, you can use the new Bundle Method to bundle this collection with your app
Resources:
https://firebase.google.com/docs/functions/firestore-events#event_triggers
https://firebase.google.com/docs/firestore/manage-data/add-data#update_elements_in_an_array
https://firebase.googleblog.com/2021/04/firestore-supports-data-bundles.html
I'm trying to learn how to use Firebase by creating a simple social media application.
https://i.stack.imgur.com/wfeyw.png (Here is how my database is laid out)
I was able to add data to the database but I'm having trouble understanding how to retrieve data. I want to be able to retrieve all the posts from a certain user and then display each one as an item in a list.
let Posts = firebase.database().ref('Posts').child(userId);
So far thats my query and here is the code I'm using to add the posts to the database. Could someone also tell me what is the term for the Id that is being generated for each post?
firebase.database().ref('/Posts').child(userId).push({
Text: post,
Score: 0
});
When using Cloud Functions with the Firebase Realtime Database you are able to target a specific field with a cloud function. For example, given this JSON structure, I could target when user1's email field changed with a ('/user/{userId}/email').onUpdate cloud function.
{
"user": {
"user1": {
"name": "John Doe",
"phone": "555-5555",
"email": "john#gmail.com"
}
}
}
Now with Firestore it seems that I can't target a specific field and can only target documents. If this is the case a Firestore cloud function to target the email field would have to look like this, ('/user/{userId}').onUpdate, and fire every time any document in the user collection changed. This would result in a lot of wasted cloud functions firing. Is this how Firestore works and is there an elegant work around to it?
You are correct, due to the different data models Cloud Firestore only allows you to trigger Cloud Functions on document level events rather than field level.
One method is to store email in a separate document (e.g. in a subcollection called Email), so updating email is tye only change that will fire. This does require you reading an extra document each time you need the email though.
Another similar method is to still have it in the same document, but also write it into the subcollection as a second write to trigger the function. Use email as the doc I'd and have a timestamp field in the document to make it easy to clean up the old document ( select oldest email doc to delete, maybe even in the function)