Firebase: Now with more querying!

Today we’re launching enhanced query support across our iOS, Android, and Web clients, and the Firebase REST API! You can now query your data by any child key in Firebase. Querying has been a frequently requested feature and we’ve made great strides based on developer feedback since our beta release last month.

This release dramatically improves the way you can query your data in Firebase. With our new querying methods you can order and retrieve your data using a child key, by key name, or by priority. Our existing priorities-based ordering will continue to work, but I encourage you to try out the new queries features as it is much more flexible.

A basic Firebase query starts with one of our orderBy functions: orderByChild(), orderByKey() or orderByPriority(). You can then combine these with five other methods to conduct complex queries: limitToFirst(), limitToLast(), startAt(), endAt(), and equalTo(). Since all of us at Firebase agree that dinosaurs are pretty cool, we’ll use this sample firebase of dinosaur facts to demonstrate how you can write complex, realtime queries. To start, we can use orderByChild() to retrieve dinosaurs ordered by height:

var ref = new Firebase("https://dinosaur-facts.firebaseio.com/dinosaurs");
ref.orderByChild("height").on("child_added", function(snapshot) {
  console.log(snapshot.key() + " was " + snapshot.val().height + " meters tall");
});
Firebase *ref = [[Firebase alloc] initWithUrl:@"https://dinosaur-facts.firebaseio.com/dinosaurs"];
[[ref queryOrderedByChild:@"height"]
    observeEventType:FEventTypeChildAdded withBlock:^(FDataSnapshot *snapshot) {

    NSLog(@"%@ was %@ meters tall", snapshot.key, snapshot.value[@"height"]);
}];
Firebase ref = new Firebase("https://dinosaur-facts.firebaseio.com/dinosaurs");
Query queryRef = ref.orderByChild("height");

postsQuery.addChildEventListener(new ChildEventListener() {
    @Override
    public void onChildAdded(DataSnapshot snapshot, String previousChild) {
        System.out.println(snapshot.getKey() + " was " + snapshot.getValue().get("height") + " meters tall");
    }
    // ....
});
curl 'https://dinosaur-facts.firebaseio.com/dinosaurs.json?orderBy="height"'

We can also use our limit methods to return the two heaviest dinosaurs in our Firebase:

var ref = new Firebase("https://dinosaur-facts.firebaseio.com/dinosaurs");
ref.orderByChild("weight").limitToLast(2).on("child_added", function(snapshot) {
  console.log(snapshot.key());
});
Firebase *ref = [[Firebase alloc] initWithUrl:@"https://dinosaur-facts.firebaseio.com/dinosaurs"];
[[[ref queryOrderedByChild:@"weight"] queryLimitedToLast:2]
    observeEventType:FEventTypeChildAdded withBlock:^(FDataSnapshot *snapshot) {

    NSLog(@"%@", snapshot.key);
}];
Firebase ref = new Firebase("https://dinosaur-facts.firebaseio.com/dinosaurs");
Query queryRef = ref.orderByChild("weight").limitToLast(2);

queryRef.addChildEventListener(new ChildEventListener() {
    @Override
    public void onChildAdded(DataSnapshot snapshot, String previousChild) {
        System.out.println(snapshot.getKey());
    }
    // ....
});
curl 'https://dinosaur-facts.firebaseio.com/dinosaurs.json?orderBy="weight"&limitToLast=2'

Adding it all together with our range methods (startAt() and endAt()) we can find all dinosaurs that are at least three meters tall:

var ref = new Firebase("https://dinosaur-facts.firebaseio.com/dinosaurs");
ref.orderByChild("height").startAt(3).on("child_added", function(snapshot) {
  console.log(snapshot.key())
});
Firebase *ref = [[Firebase alloc] initWithUrl:@"https://dinosaur-facts.firebaseio.com/dinosaurs"];
[[[ref queryOrderedByChild:@"height"] queryStartingAt:@3]
    observeEventType:FEventTypeChildAdded withBlock:^(FDataSnapshot *snapshot) {

    NSLog(@"%@", snapshot.key);
}];
Firebase ref = new Firebase("https://dinosaur-facts.firebaseio.com/dinosaurs");
Query queryRef = ref.orderByChild("height").startAt(3);

queryRef.addChildEventListener(new ChildEventListener() {
    @Override
    public void onChildAdded(DataSnapshot snapshot, String previousChild) {
        System.out.println(snapshot.getKey());
    }
    // ....
});
curl 'https://dinosaur-facts.firebaseio.com/dinosaurs.json?orderBy="height"&startAt=3'

The best part is that all these queries will update in realtime as the data changes in your Firebase. There’s more I haven’t covered, so be sure to read the documentation for the iOS, Android or Web clients or the REST API to learn about all the new features.

With querying, we’re also introducing a new indexOn rule type to the Firebase Security and Rules language. You can use indexOn to tell your Firebase which child keys your app uses in queries. A node’s key and and priority are indexed automatically, so there is no need to index them explicitly. Using indexOn is optional and can be left off for prototyping, but it will dramatically improve performance so you’ll want to add it once you’ve figured out the indexes your queries will use.

There’s more coming!

While this represents a big step forward in terms of our querying capabilities it also lays the groundwork for even more improvements. We have a number of exciting features in the pipeline for our SDKs, including more queries improvements. And as always, we’d welcome your feedback on what you’d like to see next.

Let us know what you think in our Google Group or on Twitter, and send any suggestions to firebase-support@google.com.