Yun C Han Yun C Han - 2 months ago 10
Android Question

Add an item to a list in Firebase Databse

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
.

Answer

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 
}