Firebase correct logic - firebase

Wondering if I have the right mindset here... In firebase (with Vue) I have a userscollection:
users : {
uid: "09A09IQMSLDK0912",
name: "Gerard",
email: "gerard#mail.com"
}
If I want the user to add friends, should I add it to the userscollection?
users : {
uid: "09A09IQMSLDK0912",
name: "Gerard",
email: "gerard#mail.com",
friends: []
}
... or should I start a new collection (e.g. friendscollection)?
friends: {
{
userId : 09A09IQMSLDK0912,
friendId: 09A09IQMSLDAEAQS
}
}
Thanks for the advice!

You will want to add it as a new collection. You shouldn't model any of your collections to have an array property that can have an endless amount of items in it, otherwise, you will run into issues at scale.
Instead, you should create another collection and have them relate via ID as you mentioned.
I am not an expert on a Firebase DB but I know it functions very similarly to a MongoDB. For example, say the DB is a MongoDB. There is a limit to how large a collection item can be (BSON Limit: https://docs.mongodb.com/manual/reference/limits/) and if you allow a collection item to have an array property that can grow indefinitely, you will quickly reach this limit and you wont able to insert the item into the collection.

You will want to store each user's friends in your Firebase Database in a similar structure to this:
friends: {
userId1: {
friend1UserId: 123123123121 // timestamp of when the users became friends, or whatever value is useful to your application.
friend2UserId: 123123123123
friend3UserId: 213123123123
}
userId2: {
friend1UserId: 412124124124
friend2UserId: 213123213321
}
}
This is because Firebase is very limited with querying - and generally in a NoSQL database you would like to keep you data as wide as possible rather than going deeper.

Related

firebase what is the best way/structure to retrieve by unique child key

I have a firebase database like this structure:
-groups
--{group1id}
---groupname: 'group1'
---grouptype: 'sometype'
---groupmembers
----{uid1}:true
----{uid2}:true
--{group2id}
---groupname: 'group2'
---grouptype: 'someothertype'
---groupmembers
----{uid1}:true
----{uid3}:true
----{uid4}:true
Now, I am trying to pull groups of authenticated user. For example for uid1, it should return me group1id and group2id, and for example uid3 it should just return group2id.
I tried to do that with this code:
database().ref('groups/').orderByChild('groupMembers/' + auth().currentUser.uid).equalTo('true').on('value' , function(snapshot) {
console.log('GROUPS SNAPSHOT >> ' + JSON.stringify(snapshot))
})
but this returns null. if I remove "equalTo" and go it returns all childs under 'groups'.
Do you know any solution or better database structure suggestion for this situation ?
Your current structure makes it easy to retrieve the users for a group. It does not however make it easy to retrieve the groups for a user.
To also allow easy reading of the groups for a user, you'll want to add an additional data structure:
userGroups: {
uid1: {
group1id: true,
group2id: true
},
uid2: {
group1id: true,
group2id: true
},
uid3: {
group2id: true
},
uid3: {
group2id: true
}
}
Now of course you'll need to update both /userGroups and /groups when you add a user to (or remove them from) a group. This is quite common when modeling data in NoSQL databases: you may have to modify your data structure for the use-cases that your app supports.
Also see:
Firebase query if child of child contains a value
NoSQL data modeling
Many to Many relationship in Firebase

Firebase - How do I store user data so that I can easily fetch it by their email? [duplicate]

I have the following structure on my Firebase database:
I would like to search for a user by name, last name or email but as I don't have the user key in the level above I don't know how I can achieve this. I'm doing and administrator session so it wouldn't have access to the user key.
I have tried:
let usersRef = firebase.database().ref('users');
usersRef.orderByValue().on("value", function(snapshot) {
console.log(snapshot.val());
snapshot.forEach(function(data) {
console.log(data.key);
});
});
But it brings all the users on the database. Any ideas?
You can use equalTo() to find any child by value. In your case by name:
ref.child('users').orderByChild('name').equalTo('John Doe').on("value", function(snapshot) {
console.log(snapshot.val());
snapshot.forEach(function(data) {
console.log(data.key);
});
});
The purpose of orderByChild() is to define the field you want to filter/search for. equalTo() can get an string, int and boolean value.
Also can be used with auto generated keys (pushKey) too.
You can find all the documentation here
A warning to avoid unpleasant surprises: when you use orderByChild and equalTo do not forget to add an index on your data (here's the doc)
If you don't all the nods will be downloaded and filtered client side which can become very expensive if your database grows.

Firestore data duplication

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

How to keep two paths in sync in firebase?

I have a complex application and I am using Firebase for my backend data.
I have an object that is used in two different contexts, one for viewing by the user and one for the server to do batch processing. Something like this:
users: {
USER_ID: {
...
txns: {
TXN_ID: {
// data I need
}
}
}
...
},
transactions: {
TXN_ID: {
USER_ID: {
// data I need
},
USER_ID2: {
// different data I need
}
}
}
In relational terms, txn_ids and user_ids are in a many-to-many relationship. So looking through the transactions node to find all the transactions that belong to a user is hard in FB and looking though all the users to find a transaction id is hard in FB. So I denormalize the data. I think this is correct.
But how do I keep them in sync?
I've been using MPUs, which is fine, but I'm worried about it being prone to bugs in the future.
I've considered writing a Firebase Cloud Function to keep them in sync, but I'm worried about that being more fragile than MPUs.
Advice?

Query a specific child node in all instances of a parent node in Firebase

I'm trying to figure out how, if possible, to query a specifically named child node in all instances of a parent node in Firebase. It can be assumed that all parent nodes queried have this specifically named child node in it.
In this example, uid is a unique identifier for each user and I'm trying to get a list of displayNames:
{
users: {
uid: {
displayName: "stringOfSomeKind"
}
uid2: {
displayName: "moreStrings"
}
uid3: {
displayName: "evenMoreStrings"
}
...
}
}
The purpose of this is so I can check to see if a displayName is currently taken. (I can't use the displayName as the primary key because when a user logs in, I'll only have the uid available.)
How can I efficiently check to see if one of these displayNames is already taken? Do I have to denormalize my data to do so efficiently? If so, how?
Firebase world is quite different!
When such scenarios come you have to think to redesign your database structure, In your case uid is unique identifier so is displayName- speaking technically.
You will have to maintain additional data like:
{
users: {
uid: {
displayName: "stringOfSomeKind"
}
uid2: {
displayName: "moreStrings"
}
uid3: {
displayName: "evenMoreStrings"
}
...
}
displayNames: {
"display_name": "uid",
"display_name2": "uid2",
"display_name3": "uid3"
...
}
}
Happy Helping!
When you load a node from Firebase, you also get all data under that node. Assuming that you have more data per user than just their display name, that can indeed lead to needlessly loaded data.
If you only want to load a list of display names, you should indeed store a list of display names.
{
displayNames: {
"stringOfSomeKind": "uid",
"moreStrings": "uid2",
"evenMoreStrings": "uid3"
}
}
If you come from a background of relational/SQL databases, this may seem unnatural at first. For me it helped to realize that these structures are indexes, same as the "index by displayName" that you might add to your relational database. The difference is that in NoSQL/Firebase it is your code that maintains the index, while in most RDBMSs such indexes are maintained by the system.

Resources