Fetch all users initially or on Search? - firebase

In order to invite others to one's group they must search for their target by name or email, then all matches are placed in a table. Right now I'm retrieving all the users from my Firebase database when the invites page initially loads but I'm concerned that if many users sign up it could become an unnecessarily large upfront data fetch.
Is it better to just fetch users based off each search or to fetch all of them at the start?

Related

Firestore model for fetching a list of friends of an user

I have been using Firestore for a very long time. I am building an app now where scalability and keeping low costs is important. (I am using flutter)
My app has users, which have user profiles, also they can add friends and talk to them (like instagram or facebook).
I have a problem building this friends system.
My model for this friends system currently looks like this:
Users collection. Each document id = user id from auth, those docs contain data like name, username, profile picture, etc.
Friends collection. Each document id = user id from auth. For each user, those docs contain a field called: friends, which is an array with each of his friends user ids.
The model looks like:
Friends collection:
- uid:
- friends_list: [friend_uid1, friend_uid2, ...]
This is how my "backend" looks.
Now I want to show my user a list of his friends. How do I do that?
I want a list that looks like instagram, with a nice UI showing each of my user friend profile pic, name, last message, etc.
I can not find a straight forward way to do this with Firestore and queries.
Let's say I do it like this:
Get all my friends user ids in an array.
Get all their user documents using .get() for each document.
This is not doable in firestore cause it would eliminate all the querying power I have (such being able to query only for users with name "x"), I would have to fetch all users and do the query on my front-end (or in a cloud function, same thing, not scalable).
If I do this like:
Get all document using a query for all users in the Friends collection, where friends_list contains my user id.
Save from those documents only the documentID and fetch all the friends user data manually.
This comes with another problem. In Firestore there is no way of fetching a document without fetching all of its fields, so the first query which I use to get the ids only of my friends would actually give me their id + their friend list instead (cause when I query, it also gets the document id + the data), which is not good.
If I do it like:
When you add a friend, instead of just saving its uid, save its uid + data.
Now I can easily show my user his friends list nicely and do some querying on front-end.
The problem here is that now if one of my friends updates his profile photo, I need to update it in every document of all of his friends, which is very write expensive for just a little profile update.
There is also the problem of watching for more data, maybe I have another collection with Chats, and I want to show the last message of my chat with a friend, now I have to fetch the chat rooms too, which is more hard to query data that comes with all the problems that I mentioned before.
In conclusion: I don't see a good scalable way to do this kind of system within Firestore. It seems a simple system which any basic app should have, but I do not see how I can do it in a way that does not make lots of reads or read more data (or sensitive data) than it should.
What kind of model would you do for a friends system like this?
You're decribing a quintessential drawback of NoSQL Databases.
A similar example is actually given in the Get to Know Cloud Firestore series.
Like others have commented, the answer really depends on your application. And this is the assessment you'll have to do. Like which of the options is cheaper depending on the use case of the app.
For example, if you go with your third option and store the friend's user data that you'll need to populate the list. This means you'll have to implement measures to keep the integrity of the copied data whenever the user updates their information.
You can then look at the usage of your app and determine how often users change their information vs how often you would need to retrieve full users if you don't copy the data to find the cheapest method for your application.

Firebase / Firestore how to store user data

I have a project where users can create "companies" and invite other users to join this company.
My firestore database looks like this:
In each company the name of the company and the owner (Google uid) is stored.
In addition, in each company there are subcollections with further data about the company. e.g. tasks, which can then be viewed and edited by users who are invited to the company.
Currently there is a "users" collection where all users are stored. If a user is invited to a company it is stored in the "users" subcollection "companies":
match /companies/{compId} {
function hasUserJoinedTheCompany() {
return exists(/databases/$(database)/documents/users/$(request.auth.uid)/companies/$(compId));
}
allow read: if hasUserJoinedTheCompany();
...
}
With this structure and rules, the user can only access the data from the companies to which he is invited.
In my frontend it looks like this:
The user can switch between the companies (to which he is invited) in the upper left corner. And in blue highlighted he sees the "tasks" of the currently selected company.
My problem: I want to create a page where all users in a company are listed.
With my current structure I don't know how to make it work.
I was thinking of putting the users in a subcollection in "companies" (/companies/{companyId}/users/). So that it is easy to list all users in a company. But then I don't know how to list all the companies a user is invited to (as marked in red in the last screen shot).
Is there a solution for this, without firebase functions?
This is a Problem a lot of devs have when starting to use nonSQL databases. You can/should store the same data twice. In SQL databases we are all forced to link the data and not to store anything twice or more. In a nonSQL database you can and sometimes your realy should do that. In your case it is absolutely OK for you to store the data in 3 places:
all users in the users collection
users of a company in the companies/{id}/users collection
companies that a user belogs to to the users/{id}/companies collection
That way you can very easy query all data you need and write very simple database rule to make everything secure. It would even make the automated sync trogh cloud functions more easy because you know when a company changes for what users to update the data and if a users changes for what companies to update the user data.
I would recommend to just save there the basic data like name and if shown in a list some extra ones. But awoid to save the full user data and full company data on all places. All that is just the link for you to know exactly and easy where what belogs to and to get the main data very easy.

Querying on Firebase or on the client

Basically I have a set a of data on my website and on I have some checkboxes where the user can check to filter the data. The data will be 40 items per page. Should I query my data in firebase or when I get the data back from firebase. The reason I'm asking is because the user can check and uncheck the checkboxes which means every time the users the users does that I'm getting charged, since firebase charges by use by at the end I want to make sure I follow the best practices.
The queries will have OR. I know in firebase there's not such a thing as an OR when querying but I found a workaround
https://medium.com/google-developer-experts/performing-or-queries-in-firebase-cloud-firestore-for-javascript-with-rxjs-c361671b201e

How can Firebase nodes be structured to restrict user access and allow admin to pull report data?

Context: I am putting together a time tracking application using Firebase as my backend. My current node structure has Time Entries and Clients at the root like so:
Time Entry
Entry ID
UserID
clientID, hours, date, description, etc
Clients
ClientID
name, projects, etc
This structure works fine if I'm just adding and pulling time entries based on the user, but I want to start putting together reports on a per client basis. Currently, this means making a separate HTTP request for each user and then filtering by the clientID to get at the data.
The rule structure for Firebase grants access to all child nodes once access is given to the parent node, so one big list doesn't work as it can't restrict users from seeing or editing each other's entries.
Question: Is there a way to structure the nodes that would allow for restricting users to only managing their own time entries, as well as allow for one query to pull all entries tied to a client?
** The only solution I could come up with was duplicating the entries into a single node used just for reporting purposes, but this doesn't seem like a sustainable option
#AL. your answer was what I went up going with after scouring the docs across the web. Duplicating the data is the best route to take.
The new Firestore beta seems to provided some workarounds to this.
The way that I would do this is with Cloud Firestore.
Create a root collection clients and a document for each client. This partitions the data into easily manageable chunks, so that a client admin can see all data for their company.
Within the client document, create a sub-collection called timeEntries. When a user writes to this, they must include a userId field (you can enforce this in the rules) which is equal to request.auth.uid
https://firebase.google.com/docs/firestore/security/rules-conditions#data_validation
You can now create read rules which allow an admin to query any document in the timeEntries sub-collection, but an individual user must query with userId = request.auth.uid in order to only return the entries that they have created.
https://firebase.google.com/docs/firestore/security/rules-conditions#security_rules_and_query_results
Within your users/{uid} collection or clients/{clientId} collection, you can easily create a flag to identify admin users and check this when reading data.
https://firebase.google.com/docs/firestore/security/rules-conditions#access_other_documents

Peoplesoft OPRID include in PERSON_BASIC_FULLSYNC Message

Some query regarding OPRID. i want to include the oprid in the Person Basic Fullsync message. I added the record PSOPRDEFN to the Message. But i am not able to get OPRID values for all the employees. I am getting only for some employees whose user profile is present.
Is OPRID related to only for user profiles?
how can i generate OPRID for all employees??
In PeopleSoft you have persons (identified by EMPLID) as transactional data, and users (identified by OPRID) who can access the application.
Not every user is necessarily related to a person or employee (e.g., system accounts such as PTWEBSERVER, developers' accounts, etc.), and not every person or employee will have a user profile (e.g., employees without access to Self Service transactions, ex-employees who no longer work at the company, etc.).
So, to answer your questions:
Yes, OPRID is only related to user profiles (you can obviously find OPRID values used elsewehere, like in "Last Updated By" fields, security tables, etc., but it only relates to users). Often an OPRID will be linked to an EMPLID (in the PSOPRALIAS and/or PSOPRDEFN tables), but this is neither guaranteed not required.
Generating a user profile for every employee is not always warranted, as there are important security implications to it. Furthermore, if your company uses single sign-on with LDAP authentication, the users are automatically created upon first sign-on if so authorized, so proactively creating them is unnecessary and often ill-advised. Finally, it will often be a security breach to have ex-employees with active user profiles, so you may never truly reach a scenario where all your EMPLIDs have associated OPRIDs. If you've weighed all of these concerns and do indeed want to pre-populate the entire user population, you can use something like Excel-to-CI. However, note that this will solve the issue as of right now, and the moment you create a new employee you'd need to also create its user profile.

Resources