I'm making a web app using angularfire. I have a url for users at 'url.firebaseio.com/users'. I want to make another url to store chat-room type things at 'url.firebaseio.com/rooms'. When I create a new user using the $createUser() method, it stores the user's information under a unique ID created by $createUser() in the '/users' url. I want to do this with the new '/rooms' url, but I can't find a way to generate unique IDs in the same way without $createUser(). I can't use $createUser() because it requires an email address argument, and I just want to take in a name for the room and a password, all in an object under the unique ID for the room.
I can't think of any code to provide, so here's what a user looks like:
users: {
uniqueUserId: {
email: email#email.com,
name: name
}
}
And here's what I'd like a 'room' to look like:
rooms: {
uniqueRoomId: {
roomName: something
}
}
Is there a built-in way to do this? If not would it be best to generate IDs on my own?
Related
I have started a small project (to learn Nextjs) with supabase and have hit a small roadblock. The basic overview is I have a table for stores (name, email, address, slug) and a table for socials (FK store => stores.id, name, url) which is linked via a foreign key on store => stores.id. Each store should have a separate page in the app where I will display their information and their social accounts.
I started by creating a dynamic route [id].tsx with:
export async function getServerSideProps({ params }) {
const { data: store, error } = await supabase
.from('stores')
.select('*, socials(*)')
.eq('id', params.id)
.single();
if (error) {
throw new Error(error.message);
}
return {
props: {
store
},
}
}
The above works just fine in my export default function Store ({store}) and I can see the stores information by going to localhost:3000/1 (only store set up currently). This does lead to my roadblock unfortunately. I would like the '1' to be the actual store slug (column in the stores table) like localhost:3000/lorem-ipsum but keep the relation between the two tables on the store id.
I understand that the params in my original example is id, and if I wanted the slug, I should rename my file to [slug].tsx and my params would be params.slug. Is it possible to utilize both the id and slug in my params and still have my query/route succeed?
I guess what I really want is to keep the relationship between my tables, but use the slug for querying the data (just for the url). I could make the FK the store slug, but I know it probably isnt the right move as the slug could change down the road.
id is just a variable for whatever is in the route. If you want the route to be using the slug, you just need to change the filter to search for the slug .eq('slug', params.id)
is it possible to insert battery_cost_change: field multiple time with counter value in firebase in same user Message Which is LM2JKCacawaW7tlK4yK. like battery_cost_change1: ,battery_cost_change2:,battery_cost_change3: and so on And how these fields will be retrieve in android programmaticallyenter image description here
You may wish to consider the db structure for battery_cost_changes to hold multiple instances of battery_cost_change object.
Everytime you add a new battery_cost_change - you can push the object into the path .../LM2JKCacawaW7tlK4yK/battery_cost_changes. <pushId> is the random ID Firebase generates, when an object is pushed/added into the node.
{ LM2JKCacawaW7tlK4yK: {
....
battery_cost_changes: {
<pushId>: {
battery_cost_change: 6
},
<pushId>: {
battery_cost_change: 3
}
}
....
}
While retrieving the values, you can read the path .../LM2JKCacawaW7tlK4yK/battery_cost_changes and observe the values for an array of battery_change_cost objects. Please share more details, to explore a better or more suitable answer. However, I feel this is a generic idea to store multiple values under a node.
I'm trying to setup a friend system in Firestore. My data model looks like this at the moment:
collection("users") ->
document("user1")
document("user2")
...
A document in the users collection contains data like the name, email... of the user. I'd like to enable a user to have friends now, but I'm unsure about the best way to model this.
So, I'd for sure add a friends field in the documents of the users, but what should this field contain? My first thought was a pointer to a new collection called friends in which the documents are users. Something like this:
collection("users") {
document("user1") {
name:user1,
friends: -> collection("friends") {
document("user2"),
...
}
}
}
This seems reasonable, but that'd mean that I'd have a lot of duplicate data in my database because each user that has friends will be duplicated in a friends collection. Should I worry about this or is this normal in a Firestore database structure?
Would it perhaps be possible to point to a document in the users collection from the friends collection? Something like:
collection("users") {
document("user1") {
name:user1,
friends: -> collection("friends") {
document, -----
... |
} |
}, |
document("user2")<-
}
Or should I throw away the thought of using a collection for friends and just keep a list with uids of all friends of the user?
Seems you are using two separate collections for users and friends first all you can do it by one collection. But I don't want to go there may be there was another scenario.
As your separate collection way, you can design your friends collection model to meet no duplication:
{
name : 'Name',
email : 'email#mail.com'
has_connected : {
'user1' : true // here you can use anyother unique key from user
}
}
The thing is that firestore recommend this types of design for query and for faster performance you can make that has_connected key as index.
In this approach, you have to check during adding new friend by email or any other unique key. if exists then just put another key into has_connected with the respective user. e.g user2 : true.
Finally, for fetching all friends for a user you have to do a query like this: e.g: in javascript
let ref = firebase.firestore().collection("friends");
ref
.where(`has_connected.${username}`, "==", true)
.get()
.then(//do your logic)
.catch()
Thanks
Lets assume I'm trying to build a group messaging application, so I designed my database structure to look like so:
users: {
uid1: { //A user id using push()
username: "user1"
email: "aaa#bbb.ccc"
timestampJoined: 18594659346
groups: {
gid1: true,
gid3: true
}
}
uid2: {
username: "user2"
email: "ddd#eee.fff"
timestampJoined: 34598263402
groups: {
gid1: true,
gid5: true
}
}
....
}
groups: {
gid1: { //A group id using push()
name: "group1"
users: {
uid1: true,
uid2: true
}
}
gid2: {
name: "group2"
users: {
uid5: true,
uid7: true,
uid80: true
}
}
...
}
messages: {
gid1: {
mid1: { //A message id using push()
sender: uid1
message: "hello"
timestamp: 12839617675
}
mid2: {
sender: uid2
message: "welcome"
timestamp: 39653027465
}
...
}
...
}
According to Firebase's docs this would scale great.
Now lets assume that inside my application, I want to display the sender's username on every message.
Querying the username for every single message is obviously bad, so one of the solutions that I found was to duplicate the username in every message.
The messages node will now look like so:
messages: {
gid1: {
mid1: { //A message id using push()
sender: uid1
username: "user1"
message: "hello"
timestamp: 12839617675
}
mid2: {
sender: uid2
username: "user2"
message: "welcome"
timestamp: 39653027465
}
...
}
...
}
Now I want to add the option for the user to change his username.
So if a user decides to change his username, it has to be updated in the users node, and in every single message that he ever sent.
If I would have gone with the "listener for every message" approach, then changing the username would have been easy, because I would have needed to change the name in a single location.
Now, I have to also update the name in every message of every group that he sent.
I assume that querying the entire messages node for the user id is a bad design, so I thought about creating another node that stores the locations of all the messages that a user has sent.
It will look something like this:
userMessages: {
uid1: {
gid1: {
mid1: true
}
gid3: {
mid6: true,
mid12: true
}
...
}
uid2: {
gid1: {
mid2: true
}
gid5: {
mid13: true,
mid25: true
}
...
}
...
}
So now I could quickly fetch the locations of all the messages for a specific user, and update the username with a single updateChildren() call.
Is this really the best approach? Do I really have to duplicate so much data (millions of messages) only because I'm referencing a dynamic value (the username)?
Or is there a better approach when dealing with dynamic data?
This is a perfect example of why, in general, parent node names (keys) should be disassociated from the values they contain or represent.
So some big picture thinking may help and considering the user experience may provide the answer.
Now lets assume that inside my application, I want to display the
sender's username on every message.
But do you really want to do that? Does your user really want to scroll through a list of 10,000 messages? Probably not. Most likely, the app is going to display a subset of those messages and even at that probably 10 or 12 at a time.
Here's some thoughts:
Assume a users table:
users
uid_0
name: Charles
uid_1
name: Larry
uid_2:
name: Debbie
and a messages table
messages
msg_1
sender: uid_1
message: "hello"
timestamp: 12839617675
observers:
uid_0: true
uid_1: true
uid_2: true
Each user logs in and the app performs a query that observes the messages node they are part of - the app displays displays the message text of the message as well as each users name that's also observing that message (the 'group').
This could also be used to just display the user name of the user that posted it.
Solution 1: When the app starts, load in all of the users in the users node store them in dictionary with the uid_ as the key.
When the messages node is being observed, each message is loaded and you will have the uid's of the other users (or the poster) stored in the users_dict by key so just pick their name:
let name = users_dict["uid_2"]
Solution 2:
Suppose you have a LOT of data stored in your users node (which is typical) and a thousand users. There's no point in loading all of that data when all you are interested in is their name so your could either
a) Use solution #1 and just ignore all of the other data other than the uid and name or
b) Create a separate 'names' node in firebase which only keeps the user name so you don't need to store it in the users node.
names:
uid_0: Charles
uid_1: Larry
uid_2: Debbie
As you can see, even with a couple thousand users, that's a tiny bit of data to load in. And... the cool thing here is that if you add a listener to the names node, if a users changes their name the app will be notified and can update your UI accordingly.
Solution 3:
Load your names on an as needed basis. While technically you can do this, I don't recommend it:
Observe all of the messages nodes the user is part of. Those nodes will be read in and as they are read in, build a dictionary of uid's that you will need the names of. Then perform a query for each user name based on the uid. This can work but you have to take the asynchronous nature of Firebase into account and allow time for the names to be loaded in. Likewise, you could load in a message, then load in the user name for that message with the path: users/uid_x/user_name. Again though this get into an async timing issue where you are nesting async calls within async calls or a loop and that should probably be avoided.
The important point with any solution the user experience and keeping your Firebase structure as flat as possible.
For example, if you do in fact want to load 10,000 messages, consider breaking the message text or subject out into another node, and only load those nodes for your initial UI list. As the user drills down into the message, then load the rest of the data.
Steps to follow:
fetch username at every restart of app
cache them locally
show username from cache based on uid
done
Note: how you fetch username depends on your way of implementation
You only need this structure
mid1: { //A message id using push()
sender: uid1
message: "hello"
timestamp: 12839617675
}
The username can be read from the users directly "users/uid1/username" using a single value event listener after you read each child. Firebase is supposed to be used with sequential calls, since you cannot create complex queries like in SQL,
And just to keep it efficient you could:
1)Create a reference dictionary to use it as a cache handler in which after you read every message you verify if you have the value for each key:
[uid1:"John",uid2:"Peter",....etc...]
And if the key doesn't exist you add with the single value listener pointing to /users/$uid/username that handles the "add to cache" in its callback
2)Use the limitTo startAt and endAt queries to paginate the listener and avoid bringing data the user won't see
*There is no need to actually keep updating all the messages and all the nodes with every user change, imagine a chat group with 100 users in which every user have 20 messages ...2000 updates with your single updateChildren() call that would be extremely inefficient, since it is not scalable and you are updating data that surely no user will ever see again in a real life scenario (like the first message of the 2000 chat messages)
My application keeps multiple profile attributes for its users, such as:
An internal userId
Their phone number
Their email
etc. Each attribute is unique to a user; they can all be used as identity information.
I am designing an API with operations that refer to a specific user, eg charge.
I want to allow clients to identify users by any of the available profile attributes. In my specific domain, it is not possible to just enforce clients to use the internal userId, even if they can receive it in a separate call (eg getUserIdFromProfileAttribute).
Assuming the charge operation, it is a POST request with a JSON document inside the body. What would be the best way to identify the users? I am thinking one of the following:
Top-level key/value pairs for both the id and the id type:
{
"userId": <id>,
"userIdType": <idType>
}
Nested key/value pairs inside a user key:
{
"user": {
"id": <id>,
"type": <idType>
}
}
Single key/value pair, using a URI format with (possibly) custom protocols:
{
"user": <uri> # eg id:1234, tel:+19283912000, email:user#mail.com
}
Single key/value pair, using different keys for each id (one key per call):
{
"userId": <id> *OR*
"userMsisdn": <msisdn> *OR*
"userEmail": <email>
}
Same as above, but nested inside a user key:
{
"user": {
"id": <id> *OR*
"msisdn": <msisdn>
}
}
Any suggestions about best practices? Anyone can point me to some standard / widely used APIs with a similar need?
I should repeat that using just the internal userId in all calls is not possible, and using a separate call for each id (eg chargeById, chargeByEmail) is not practical as there are many such calls.