I have the following Firebase database structure. uIds is a type of List<String>. I am trying to add another uId under uIds with an incremented index. setValue() and updateChildren() would require me to retrieve existing data, and push() will add an item with a randomly generated string as a key instead of an incremented index. Is there a simpler way that does not require to retrieve the existing data? Thanks!
"requests" : {
"request001" : {
"interests" : [ "x" ],
"live" : true,
"uIds" : [ "user1" ] // <---- from this
},
"request002" : {
"interests" : [ "y" ],
"live" : true,
"uIds" : [ "user2" ]
}
}
--------------------------------
Edit:
Sorry for the unclarity. Let me elaborate to make it clear.
Say I have the above database and want to update it to the following.
"requests" : {
"-KSVYZwUQPfyosiyRVdr" : {
"interests" : [ "x" ],
"live" : true,
"uIds" : [ "user1", "user2" ] // <--- to this
},
"-KSl1L60g0tW5voyv0VU" : {
"interests" : [ "y" ],
"live" : true,
"uIds" : [ "user2" ]
}
}
ishmaelMakitla's suggestion, mDatabase.child("requests").child("request001").setValue(newRequest), will overwrite the "request001" with "newRequest". So I should retrieve the existing data of "request001" and add "user2" to the list uIds. It will be something like this:
mDatabase.child("requests").child("request001").addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
Request newRequest = dataSnapshot.getValue(Request.class);
newRequest.uIds.add("user2");
mDatabase.child("requests").child("request001").setValue(newRequest);
}
#Override
public void onCancelled(DatabaseError databaseError) {}
});
But I am wondering if this process is necessary since what I am trying to do is simply to add one item to the list uIds.
The Firebase documentation on creating data that scales proposes that you use a different data structure:
"requests" : {
"-KSVYZwUQPfyosiyRVdr" : {
"interests" : { "x": true },
"live" : true,
"uIds" : {
"user1": true,
"user2": true
}
},
"-KSl1L60g0tW5voyv0VU" : {
"interests" : { "y": true },
"live" : true,
"uIds" : {
"user2": true
}
}
}
Here are a few of the reasons why this data structure works better:
each uid can now automatically only appear once. We've essentially modeled our data as a set, instead of using an array.
adding an item is now as easy as ref.child("uUids").child("user3").setValue(true)
you can now check if a uid exists in your security rules.
I have started re-iterating to myself: whenever you find yourself doing array.contains("xyz"), you should probably be using a set instead of an array. The above mapping with "key": true is an implementation of a set on Firebase.
Efficiency
Some people may think arrays are a more efficient way of storing the data, but in the case of Firebase that is not true:
What you see:
"uIds" : [ "user1", "user2" ]
What Firebase stores:
"uIds" : {
"0": "user1",
"1": "user2"
}
So storing a set is pretty much the same:
"uIds" : {
"user1": true,
"user2": true
}
Not sure what you mean when you say setValue, etc require you to retrieve existing data. The basic flow for inserting new record is as follows:
private DatabaseReference mDatabase;
// get reference to your Firebase Database.
mDatabase = FirebaseDatabase.getInstance().getReference();
//and here you add a new child to your 'requests' collection
//I am assuming you have a Request model like this..
Request newRequest = new Request(some-params);
mDatabase.child("requests").child(someRequestId).setValue(newRequest);
You can take a look at basic usage guide for Saving Data on Android Firebase.
Update:
Following your comment - I think what you are looking to do can be achieved like this:
You use the push() method which generates a unique ID every time a new child is added to the specified Firebase reference:
Firebase newRequestRef = mDatabase.child("request").push();
newRequestRef.setValue(newRequest);
This should do it.
I hope this helps.
There is a good old article from the Firebase official blog explaining why we should avoid array in our database : Arrays are Evil
So it's not possible to modify an array without replacing the array. I suggest to change your database structure to this
"requests" : {
"<pushKey1>" : {
"interests" : [ "x" ],
"live" : true,
"uIds" : {
"<pushKey1>" : "user1",
"<pushKey2>" : "user2"
}
},
"<pushKey2>" : {
"interests" : [ "y" ],
"live" : true,
"uIds" : {
"<pushKey1>" : "user2"
}
}
}
To get the pushKey, you can use push() method (the same as what you have done to each Request item)
Then the code will be like this if you just want to add a new uid to a request.
String requestKey = "request001";
mDatabase.child("requests").child(requestKey).child("uIds").push().setValue("user2");
Comment here if you have questions, hope this helps :)
Adding 2 cents to Frank van Puffelen answer, you can use the key from push operation as a unique identifier of your request. Plus if you use hash map to update child then your DB will not be overridden
// Create a node on Server and get key
String requestID = AdminController.getInstance().referenceFromUrl
.child(END_POINT_REQUESTS)
.push().getKey();
//use key as ID for your Object which you want to push as unique identifier of your model
requestToPush.setRequestId(requestID );
//Add updated Model Object in a Map to update DB instead of over writing
requestsMap.put(requestID , requestToPush);
//Update newly created DB nodes with data
referenceFromUrl
.child(END_POINT_REQUESTS)
.updateChildren(productsMap,
new DatabaseReference.CompletionListener() {
#Override
public void onComplete(DatabaseError databaseError, DatabaseReference databaseReference) {
if (databaseError != null) {
Log.e(TAG, "Error: Data could not be saved "
+ databaseError.getMessage());
} else {
Log.e(TAG, "Success : Data saved successfully.");
}
}
});
Result
Related
I'm working on an app that contains USERs and EVENTs.
Each event has several users and each users has several events.
In Firebase, both events and users as "details" key(see json below), and those details are duplicate so all the event users has the details of the event, so in my main activity I have a recyclerview that shows those events easly (and the same way for users list inside event activity.
To sync all those duplicates I use firebase functions.
Now I want to move ti firestore but I'm not sure how should it be.
From what I see I can have list of users IDs in event document and then do a query for all events with user.id = current_user_id.
The issue is that I have more fields per user (expenses, and I want to add more) So either I have also subcollection for each user, or have more complicate list.
Can I query the events according to their subcollection id?
I will appreciate any help with that.
The current Json:
{
"events" : {
"-L7v0K***" : {
"average" : 110,
"details" : {
"date" : {
"date" : 1520354933426
},
"location" : "Tel Aviv",
"name" : "test"
},
"items" : {},
"require_transactions" : [ {}],
"totalexpenses" : 220,
"users" : {
"ARKuwgrDHcNnXHoPlCgIBXOObjD3" : {
"details" : {
"uid" : "ARKuw***",
"userEmail" : "r***g#walla.com",
"username" : "R** G***"
},
"expenses" : 20
},
"pDFtk***" : {}
},
"-L84Gg***" : {}
}
},
"users" : {
"ARKuw***" : {
"details" : {
"uid" : "ARKuwg***",
"userEmail" : "r***g#walla.com",
"username" : "R** G***"
},
"events" : {
"-L7v0Kx***" : {
"date" : {
"date" : 1520354933426
},
"location" : "Tel Aviv",
"name" : "test"
},
"-L97_3***" : { }
},
"TAJK6***" : {}
}
}
}
Firebase is pretty close to firestore, i also started everything with firebase realtime and moved to firestore, to have more complex queries.
So, as i understood, you will have two primary collections, you can create them by:
firebase.firestore().collection('users')
firebase.firestore().collection('events')
inside users you will have a subcollection called events to trigger all events a specific user have, so it will be like this:
firebase.firestore().collection('users').doc(userId).collection('events')
To make queries, for example, to get all events that a specific user is attending you can type:
firebase.firestore().collection('users').doc(userId).collection('events').get()
(here you can do filters like limiting, 'where', byId... by 'field equals to'... all of this is the documentation)
this will return all eventsId that are in the collection he attended.
Remember to work thinking on scale to get lower cost.
If you can explain more the task you need i can work in a solution.
Documentation of firestore is very easy.
Say I have the following database in Firebase, when a user send a new message to another user, it need to push a new message to "ChatThreads/hash(User1,User2)" and update both "UserChats/User1/User2" and "UserChats/User2/User1".
"ChatThreads" : {
"hash(User1,User2)" : {
"1" : {
"message" : "Hello",
"sender" : "User2",
"time" : 9835592
},
"2" : {
"message" : "hi",
"sender" : "User1",
"time" : 10000000
},
"3" : {
"message" : "I am boss",
"sender" : "User2",
"time" : 14835592
}
}
}
},
"UserChats" : {
"User1" : {
"User2" : {
"lastTime" : 14835592,
"latestMessage" : "I am boss"
}
},
"User2" : {
"User1" : {
"lastTime" : 14835592,
"latestMessage" : "I am boss"
}
}
}
How do I keep data consistency in the case?
In the document of Firebase, the method of creating a reference to multiple update's common parent and put all updates in a map was introduced. However, in this case, not only update but also push is needed.
When you call push() without any arguments, you get a unique key that's always generated on the client. (Push ids are always generated locally.) With that key, you can then construct the path to the location where you want to add the data you along with all the other data for that update.
sorry for my bad English level, I'm from Argentina.
I have the following messages data structure in Firebase:
"messages"
"-KezmqXSdKCNFFA432Uc___-KfCEwklG_y3naRDIUiY"
"messageDate": "20170620"
"messageTime": "18:44"
"message": "Hi"
"-KezFDSAADFASFFS3221___-KASDF32324SDFASD1FS"
"messageDate": "20170620"
"messageTime": "22:23"
"message": "How are you?"
Where -KezmqXSdKCNFFA432Uc, -KfCEwklG_y3naRDIUiY, -KezFDSAADFASFFS3221 and -KASDF32324SDFASD1FS are users.
My problem is that I created a childEventListener in "messages" node to receive new users messages but I am receiving all the new messages of all the users (I'm logged in one user per app) because my childListener is in "messages" node.
Is it correct that if I have 1000 users when adding a message, a new message reaches the 1000 users? (Assuming that within the app, you can check to which user that message belongs).
Thanks!
If you do a structure like similar to this:
-chats
- chatUID
- members
- userUID
- lastMessageSent:messageUID
- ... more properties
-chatMessages
- chatUID
- messageUID
- sentBy: userUID
- messageDate:""
- messageTime:""
- message:""
-userChats
- userUID
- chatUID
you can attach a listener to /userChats/userUID, which will display active chats, and a listener to /chatMessages/chatUID, which will get all chat messages for a specific chat conversation.
This way is a lot easier to setup firebase security rules, and users will only receive chat messages which they are apart of.
Thanks to #Linxy for a brilliant answer
I have created a firebase database regarding #Linxy answer
Here is the complete JSON export
{
"Chats" : {
"-Lsfsd234xda" : {
"lastMessageSent" : "-LrDEBo1-Message",
"members" : [ "-LrDEBoLokW-5mhaT3ys", "-LrDEBoLokW-5mhaT3yz" ],
"more_properties" : "goes here"
}
},
"Users" : {
"-LrDEBoLokW-5mhaT3ys" : {
"id" : "-LrDEBoLokW-5mhaT3ys",
"userDisplayName" : "Qadir Hussain",
"userEmail" : "XXXXX.XXXX#gmail.com",
"userPhotoUrl" : "https://lh3.googleusercontent.com/a-/AAuE7XXXXXXXXX"
},
"-LrDEBoLokW-5mhaT3yz" : {
"id" : "-LrDEBoLokW-5mhaT3yz",
"userDisplayName" : "Ishaq Bhojani",
"userEmail" : "XXXXXXX.XXXXXX#gmail.com",
"userPhotoUrl" : "https://lh3.googleusercontent.com/a-/AAuE7mB3KTbXXXXXXXX"
}
},
"chatMessages" : {
"-Lsfsd234xda" : {
"-LrDEBo-MessageUID" : {
"message" : "Hi there!",
"messageDate" : "10/10/2019",
"messageTime" : "10:16pm",
"sentBy" : "-LrDEBoLokW-5mhaT3ys"
},
"-LrDEBo1-MessageUID" : {
"message" : "Hello",
"messageDate" : "10/10/2019",
"messageTime" : "10:17pm",
"sentBy" : "-LrDEBoLokW-5mhaT3yz"
}
}
},
"userChats" : {
"-LrDEBoLokW-5mhaT3ys" : {
"0" : "-Lsfsd234xda",
"1" : "-Lsfsd234xda1",
"chatUID" : "-Lsfsd234xda"
}
}
}
I know it's late to answer but for future readers although Linxy's answer is neater, I would like to point out a more efficient one having been tried both structures:
ChatMessages
smallerUID_biggerUID
messageUID
sentBy : userUID
messageDate : ""
message : ""
.
.
.
.
UserChats
userUID
pairUID
lastMessage : ""
.
.
.
.
In this way, instead of first finding out the chatId then finding out which user is associated with that chatId, we can directly search which users should appear in our active chat tab and get thouse users' information (username, profilePicture). The reason for that is we can always calculate the chatId if we know the user's id we would like to message with. So for the message tab, we calculate the chatId (smallerUID_biggerUID) in client side and search for the messages in referencing it.
In order to structure your database, please read this post: Structuring your Firebase Data correctly for a Complex App. You'll find here for sure the answer to your question.
As a conclusion, try to flatten(denormalize) your database as much as possible.
Hope it helps.
this structure doesn't support what you want to do, it better to change it by using something like channels, where a channel contains the messages between two persons, so when any one of them send a message the other one will be notified.
{
"users": {
"userId": {
"conversations": {
"conversationId": {
"unseenCount": 0
},
"conversationId2": {
"unseenCount": 3
}
}
},
"conversations": {
"conversationId": {
"displayedMessage": "Message",
"members": {
"userId1": true,
"userId2": true
},
"messages": {
"messageId": {
"type": "text",
"text": "Hello",
"createdAt": "",
"senderId": "userId",
"status": "sent",
"payload": ""
}
},
"lastMessage": "my last message"
}
}
}
I think this will be the best structure for it:
{
messages: {
A8Fcn28ak9ask46: {
chat_id: "combination of sender and receivers number",
sender_id: "person sending the message",
receiver_id: "person send it to",
text: "message that the user types",
timestamp: "123981849404"
},
...
}
}
then when u get the results, you can filter through the chat_id's in forward and in reverse, which will get the conversation between two people.
Hope it helps.
This question already has an answer here:
Firebase query if child of child contains a value
(1 answer)
Closed 6 years ago.
What is the proper way to query for every Comment (POJO) attached to a Photo (POJO) in Android. In Firebase, I have the following structure for a photo collection:
"photos" : {
"-KaeuYf9jXcy4oqpRqbi" : {f
"entities" : {
"type" : {
"image" : {
"bucket" : "bucket",
"prefix" : "https://s3-us-west-2.amazonaws.com/",
"suffix" : "image.jpg"
}
}
},
"stats" : {
"liked" : 0
},
"text" : "Person Detected!",
"timestamp" : 1484631194257
}
}
and for comments collection:
"comments" : {
"-KdE6Hwua8d6sBQimPos" : {
"attachphoto" : {
"-KaeuYf9jXcy4oqpRqbi" : true
},
"attachuser" : {
"-KaeuYHjkdf9okflslf" : true
},
"displayName" : "Gary",
"text" : "ok",
"timestamp" : 1487385995844
},
"-KdE6IPc-NL-6zGkwXq3" : {
"attachphoto" : {
"-KaeuYf9jXcy4oqpRqbi" : true
},
"attachuser" : {
"-KaeuYHjkdf9okflslf" : true
},
"displayName" : "Thomas",
"text" : "ok",
"timestamp" : 1487385997735
}
}
In Android I'm using a FirebaseAdapter, but I am having trouble defining a proper Query that would only retrieve comments attached to a specific photo. I have the key of the Photo
FirebaseListAdapter adapter = new CommentAdapter(this, Comment.class, R.layout.item_comment, QUERY){
#Override
protected void populateView(View v, Comment model, int position) {
if(model.getDisplayName()!=null) {
String[] name = model.getDisplayName().split(" ");
((TextView) v.findViewById(R.id.id)).setText(name[0]);
}
((TextView) v.findViewById(R.id.content)).setText(model.getText());
}
};
I thought I could define something like:
final FirebaseDatabase database = FirebaseDatabase.getInstance();
final DatabaseReference commentsRef = database.getReference("comments");
Query query = commentsRef.orderByChild("attachphoto").equalTo()
But I'm having some disconnect on how to finish this. First project using Firebase, so any help/tips would be much appreciated!
Firebase is rather noSQL, therefore there are limits which one has to work around... especially when designing the data-structure. for example, in this case it might make sense to add the ids of the related users & comments (or even the related comments altogether) into a nested node of the structure, which represents the photo. there are other ways to get there - while one always needs to consider how to look up the details later on (because the structure dictates which queries are possible and which not).
I'm novice with firebase and I try to structure my data to store friendship between users of my app.
My data are structured like this :
{ users: {
user_1: {
name: John
},
user_2: {
name: Jack
},
user_3: {
name: Georges
},
},
{ friendships : {
user_1 : {
user_2: {
name: Jack
},
user_3: {
name: Georges
},
user_3: {
user_1: {
name: John
},
user_2: {
name: Jack
}
}
}
I don't know if my data structure is OK, but I wonder what is the best way to update a name in the friendships if a user changes his name, how can I easily update the reference in the friendships (brows each node ?) ?
Thank you if someone can help me to understand the right way to do that.
A.
The database structure that you are following is a bit messy and might prove a bit hard to navigate through it, Might i suggest :-
Users{
userID_1 : {
username : “Shelly Morgan” ,
useremail : “sehlly1#yahoo.co.in”
friends :{
userID_2 : true,
userID_3 : true,
userID_4 : true,
}
},
userID_2 : {
username : “Mikael” ,
useremail : “mike1232#gmail.com”
friends :{
userID_1 : true,
userID_4 : true,
}
},
userID_3 : {
username : “Lenoard Narish” ,
useremail : “leo_nar12#gmail.com”
friends :{
userID_1 : true,
userID_2 : true,
userID_4 : true,
}
},
userID_4 : {
username : “Rob Stark” ,
useremail : “robStar781#gmail.com”
friends :{
userID_1 : true
}
}
}
In this manner you only store the userID of friends in that users database, and all you have to do is change the values in only friends_uid node.To retrieve the friends database:-
1.) just hit the friends node of that User
2.) listen for every friends uid,
3.) and hit the database with the desired uid to retrieve database of respective friend
Read the documentation in here for retrieving the data : https://firebase.google.com/docs/database/android/retrieve-data
Your structure is almost good, but I would change the friendships node to this:
{ users: {
user_1: {
name: John
},
user_2: {
name: Jack
},
user_3: {
name: Georges
},
},
{ friendships : {
user_1 : {
user_2: true,
user_3: true,
user_3: {
user_1: true,
user_2: true
}
}
This is similar to what Dravidian showed, but depending on how many friends you have, it might be a better idea to not store the users friendslist under the users account details node because if you had 1000+ friends you would have to download 1000 children even if you just wanted to get a single value like his name.
You shouldn't store name in the friendships structure, only the usernames. So friendships should look more like this:
{
friendships: {
user_1: [
user_2,
user_3
],
user_2: [
user_1
]
}
}
This way if you update name, you only have update it in one place (users). You can cross-reference the username from friendships with the username in users to find name.
Also in case you use push() for friendships, the structure will change a little, but the same principle applies:
{
friendships: {
user_1: {
-KPE008BeSzNR5D3W7t3: user_2,
-KPE0nF4AW7Lj66xTLUu: user_3
},
...
}
}