Storing and iterating arrays comprises a good portion of the Firebase questions on StackOverflow. This article will explain Firebase's array support, the ins-and-outs of arrays in distributed data, and some common techniques for dealing with array-like data.
First, let's get a handle on the current array support.
Firebase has no native support for arrays. If you store an array, it really gets stored as an "object" with integers as the key names.
// we send this ['hello', 'world'] // Firebase stores this {0: 'hello', 1: 'world'}
However, to help people that are storing arrays in Firebase, when you call .val() or use the REST api to read data, if the data looks like an array, Firebase will render it as an array.
.val()
In particular, if all of the keys are integers, and more than half of the keys between 0 and the maximum key in the object have non-empty values, then Firebase will render it as an array. This latter part is important to keep in mind.
// we send this ['a', 'b', 'c', 'd', 'e'] // Firebase stores this {0: 'a', 1: 'b', 2: 'c', 3: 'd', 4: 'e'} // since the keys are numeric and sequential, // if we query the data, we get this ['a', 'b', 'c', 'd', 'e'] // however, if we then delete a, b, and d, // they are no longer mostly sequential, so // we do not get back an array {2: 'c', 4: 'e'}
You can't currently change or prevent this behavior. Hopefully understanding it will make it easier to see what one can and can't do when storing array-like data.
So why not just store arrays instead of objects?
I bet that got your attention. Actually, arrays are quite handy. But for distributed data, they aren't reliable because they lack a unique, permanent way to access each record.
Consider the case where three clients attempt to modify an array on a remote service like Firebase:
[ {name: 'foo', counter: 1}, {name: 'bar', counter: 1}, {name: 'baz', counter: 1} ]
data[1].counter++
The end result is that record 'foo' is deleted, and the counter for record 'baz' is incremented, while the now incorrect 'bar' sits at position 0. None of the operations worked quite as intended.
If the data were instead stored as an object, keyed by unique ids, the scenario would look more like this:
{foo: {counter: 1}, bar: {counter: 1}, baz: {counter: 1}};
Now the operations resolve in a sensible manner, despite concurrent updates from multiple clients. This isn't possible with an array, where the indices used to reference each record are fluid and change over time.
However, we couldn't just pick up the bar record and move it to the front of the list. What we need is a convenient way to create unique ids, such as a hash or object's keys, while still having a defined ordering for the records.
bar
In Firebase, this is done with push ids and priorities.
Firebase's solution to the problem of unique identification is the push method (note that this is named childByAutoId in iOS).
Like JavaScript's push method, this appends new records to the end of our collection. It does this by assigning a permanent, unique id based on the current timestamp (offset to match server time). This means that each record is naturally sorted at the end and that they maintain a chronological order.
var ref = new Firebase(URL_TO_DATA); // this new, empty ref only exists locally var newChildRef = ref.push(); // we can get its id using key() console.log('my new shiny id is '+newChildRef.key()); // now it is appended at the end of data at the server newChildRef.set({foo: 'bar'});
Firebase provides a set of tools for ordering data at the server level. By using setPriority or setWithPriority, it's possible to specify the order in which records are stored and retrieved. A classic example of this is a leaderboard, where results are stacked by highest score.
var scoresRef = new Firebase(URL_TO_SCORES); var score = 10000000000; // enough to put me in the #1 position! // we sort the scores in descending order, so use a negative score for the priority ordering scoresRef.child('kato').setWithPriority( score, -score ); // retrieve the top 10 scores scoresRef.limitToLast(10).once('value', function(snap) { var i = 0; snap.forEach(function(userSnap) { console.log('user %s is in position %d with %d points', snap.key(), i++, snap.val()); }); });
But what about client-side sorting, such as by table heading, and other ops that really just don't work with objects? There are times when nothing but an array will do.
If all of the following are true, it's okay to store the array in Firebase:
// initial data: [ {name: 'foo', counter: 1}, {name: 'bar', counter: 1}, {name: 'baz', counter: 1} ]; var ref = new Firebase(URL_TO_LIST); // sync down from server var list = []; ref.on('value', function(snap) { list = snap.val(); }); // time to remove 'bar'! // this is the correct way to change an array list.splice(1, 1); ref.set(list); // DO NOT DO THIS! It will eventually turn the array into an object // and may result in null values for deleted indices // ref.child('1').remove();
In Java, if we know that the data is array-like, it can be cast as a List:
Firebase julieRef = new Firebase("https://SampleChat.firebaseIO-demo.com/users/julie/"); julieRef.addValueEventListener(new ValueEventListener() { @Override public void onDataChange(DataSnapshot snapshot) { GenericTypeIndicator<List<String>> t = new GenericTypeIndicator?<List<String>>() {}; List messages = snapshot.getValue(t); if( messages === null ) { System.out.println('No messages'); } else { System.out.println("The first message is: " + messages.get(0) ); } } // onCancelled... });
In Objective-C, array-like data is easy enough to iterate. However, some use cases, such as Table View, require the use of NSArray. It can be obtained with a simple type cast:
Firebase *julieRef = [[Firebase alloc] initWithUrl:@"https://SampleChat.firebaseIO-demo.com/users/julie"]; [julieRef observeEventType:FEventTypeValue withBlock:^(FDataSnapshot *snapshot) { if(snapshot.value == [NSNull null]) { NSLog(@"No messages"); } else { NSArray *messages = [snapshot.value]; NSString *firstMessage = [messages objectAtIndex:0]; NSLog(@"First message is: %@", firstMessage); } }];
What if we want shiny arrays, and still want to have all the power of real-time collaboration? Well Firebase is about building amazing real-time apps so surely we can have both!Check out Synchronized Arrays in Firebase to learn more about this advanced technique.
Find this article useful or have additional questions? Leave a comment below or post a message on the mailing list!
Although it seems like we just moved into our old space not long ago, we’ve already outgrown it! On March 1st we moved again, to a new office in the heart of downtown San Francisco.
The new office is at 22 4th Street, conveniently located near the Powell BART/Muni Metro station. It’s a perfect spot to continue to grow our team. We’re excited for more conference rooms (we were starting to spill out into the hallways of our old office), panoramic views of the San Francisco skyline, and a large area for meetups.
We want to make the space uniquely our own. At our office warming party two weeks ago we invited developers from our community to paint our walls with whatever inspired them. We’re really pleased with the results!
Not to be outdone by our community, we’re having an internal competition to see who can furnish and decorate the best looking conference room.
The Firebase office will be open to the local developer community to host meetups. We’re starting by hosting the SFJS meetup on May 8, 2014. If you’re interested in us hosting your local meetup please drop me a note. With our new office we have a lot more room to grow, and we are! If you want to build something that helps developers create extraordinary experiences, we’re hiring!
Two years ago today there was a torrential downpour in San Francisco. Despite the abysmal weather, a group of 250 friends and passionate technologists came to see Andrew, Vikrum, Michael and I introduce Firebase to the world.
Vikrum, who doubles as our Head of Operations and our Chief Naming Officer, codenamed the product ‘Plankton’ when we started coding in the fall of 2011. As 2011 turned into 2012 we found a name we loved, iterated on our API, and systematically gathered feedback from an incredible group of developers -- many of whom presented their own Firebase-powered apps at our launch on the rainy night of April 12th, 2012. (Thanks Sara, Judy, Smita, Ted, Jeff, Felix, Melih, Joey, Ben, & Mehul!)
The past two years have been incredible. We’ve consistently been astonished with the support you’ve shown for both our product and our team. You’ve built things on Firebase that we never dreamed of, and your creativity and enthusiasm inspire us to keep making Firebase better every day.
Our community just crossed 62,000 developers and is growing every day. Our team is now 15 people strong with 2 more joining soon, and we’re hiring!
Some of the highlights from the past two years include:
We’ve got a number of great new features and improvements coming in the next few weeks and months. Follow us @Firebase on Twitter for the latest news, email firebase-support@google.com with technical questions, and join our Google Group to get involved with our developer community. We can’t wait to see what you build.
Finally, here’s the video from our launch two years ago!
Yesterday the OpenSSL Project released an update to address the "Heartbleed" vulnerability. This is a serious vulnerability that allows an attacker to see the contents of memory on a server. Given the widespread use of OpenSSL and the versions affected, this vulnerability affects a large percentage of services on the internet.
Although the Firebase realtime servers were unaffected by this exploit, our servers managing authentication to the Firebase website were vulnerable. This could have allowed an attacker to obtain authentication credentials while they were in-flight. Internally we protect passwords by hashing them using bcrypt, but that precaution did not help given the nature of this exploit.
Once the exploit was revealed our infrastructure team responded immediately and all Firebase services were secured the same day, by 11pm PDT on Monday, April 7. Firebase is no longer vulnerable to this exploit.
We do not have any evidence that passwords or any other private information has been compromised. However, given that this exploit existed in the wild for such a long time, it is possible that an attacker could have stolen passwords without our knowledge. As a result, we recommend that all Firebase users change the passwords on their accounts. We also recommend that you reset your Firebase Secrets, which you can do from the "Secrets" tab in your App Dashboard.
The safety and security of our customer data is our highest priority. We are continuing to monitor the situation and will be responding rapidly to any other potential threats that are discovered.
If you have any questions or concerns, please email us directly at security@firebase.com.