Querying and Limiting Data in Firebase

When working with large lists of data it is often useful to only retreive a subset of the list. For example, in a chat log, a user may only be interested in the last few messages. Using Firebase Queries, we can selectively retreive data based on various factors.

Firebase allows you to construct queries that filter the children at a location, so that events are only triggered for a subset of them. There are 3 types of queries:

The Limit Query

This query is used to set a maximum number of children to be synced for a given callback. If you set a limit of 100, you will initially only receive up to 100 Child Added events. As new children are created, you will receive Child Removed events to keep the total number of "active" children at or below 100. For example:

var messageListRef = new Firebase('https://SampleChat.firebaseIO-demo.com/message_list/');
var messageListQuery = messageListRef.limit(100);
messageListQuery.on('child_added', function(snapshot) {
  var messageInfo = snapshot.val();
  alert('User ' + messageInfo.user_id + ' said: ' + messageInfo.text);
});
messageListQuery.on('child_removed', function(snapshot) {
  var messageInfo = snapshot.val();
  alert('Message ' + messageInfo.text + ' from user ' + messageInfo.user_id + ' should no longer be displayed.');
});

Firebase* messageListRef = [[Firebase alloc] initWithUrl:@"https://SampleChat.firebaseIO-demo.com/message_list/"];
FQuery* messageListQuery = [messageListRef queryLimitedToNumberOfChildren:100];
[messageListQuery observeEventType:FEventTypeChildAdded withBlock:^(FDataSnapshot *snapshot) {
  NSDictionary* messageInfo = snapshot.value;
  NSLog(@"User %@ said %@", messageInfo[@"user_id"], messageInfo[@"text"]);
}];
[messageListQuery observeEventType:FEventTypeChildRemoved withBlock:^(FDataSnapshot *snapshot) {
  NSDictionary* messageInfo = snapshot.value;
  NSLog(@"Message %@ from user %@ should no longer be displayed.", messageInfo[@"text"],  messageInfo[@"user_id"]);
}];

Firebase messageListRef = new Firebase("https://SampleChat.firebaseIO-demo.com/message_list/");
Query messageListQuery = messageListRef.limit(100);
messageListQuery.addChildEventListener(new ChildEventListener() {
    @Override
    public void onChildAdded(DataSnapshot snapshot, String previousChildName) {
        GenericTypeIndicator<Map<String, String>> t = new GenericTypeIndicator<Map<String, String>>() {};
        Map<String, String> userData = snapshot.getValue(t);
        System.out.println("User " + userData.get("user_id") + " said " + userData.get("text"));
    }

    @Override public void onChildChanged(DataSnapshot snapshot, String previousChildName) { }

    @Override public void onChildRemoved(DataSnapshot snapshot) { }

    @Override
    public void onChildMoved(DataSnapshot snapshot, String previousChildName) {
        GenericTypeIndicator<Map<String, String>> t = new GenericTypeIndicator<Map<String, String>>() {};
        Map<String, String> userData = snapshot.getValue(t);
        System.out.println("Message " + userData.get("message") + " from user " + userData.get("user_id") + " should no longer be displayed");
    }

    @Override public void onCancelled() { }
});

The Start At and End At Queries

These queries can be used to set a starting priority or ending priority for the children to be synced. For example:

// For this example, we’ll assume the priority on all chat messages has been set to
// Unix epoch time of the time the message was sent.
// This displays all chat messages sent at or after 7:32 PM on 2/11/2012:
messageListRef.startAt(1329017600).on('child_added', function(snapshot) {
  var messageInfo = snapshot.val();
  alert('User ' + messageInfo.user_id + ' said: ' + messageInfo.text);
});

// This displays all chat messages sent at or before 7:32 PM on 2/11/2012:
messageListRef.endAt(1329017600).on('child_added', function(snapshot) {
  var messageInfo = snapshot.val();
  alert('User ' + messageInfo.user_id + ' said: ' + messageInfo.text);
});

// For this example, we’ll assume the priority on all chat messages has been set to
// Unix epoch time of the time the message was sent.
// This displays all chat messages sent at or after 7:32 PM on 2/11/2012:
[[messageListRef queryStartingAtPriority:@1329017600] observeEventType:FEventTypeChildAdded withBlock:^(FDataSnapshot *snapshot) {
  NSDictionary* messageInfo = snapshot.value;
  NSLog(@"User %@ said %@", messageInfo[@"user_id"], messageInfo[@"text"]);
}];

// This displays all chat messages sent at or before 7:32 PM on 2/11/2012:
[[messageListRef queryEndingAtPriority:@1329017600] observeEventType:FEventTypeChildAdded withBlock:^(FDataSnapshot *snapshot) {
  NSDictionary* messageInfo = snapshot.value;
  NSLog(@"User %@ said %@", messageInfo[@"user_id"], messageInfo[@"text"]);
}];

// For this example, we'll assume the priority on all chat messages has been set to
// Unix epoch time of the time the message was sent.
// This displays all chat messages set at or after 7:32 PM on 2/11/2012:
messageListRef.startAt(1329017600).addChildEventListener(new ChildEventListener() {
    @Override
    public void onChildAdded(DataSnapshot snapshot, String previousChildName) {
        GenericTypeIndicator<Map<String, String>> t = new GenericTypeIndicator<Map<String, String>>() {};
        Map<String, String> userData = snapshot.getValue(t);
        System.out.println("User " + userData.get("user_id") + " said " + userData.get("text"));
    }

    @Override public void onChildChanged(DataSnapshot snapshot, String previousChildName) { }

    @Override public void onChildRemoved(DataSnapshot snapshot) { }

    @Override public void onChildMoved(DataSnapshot snapshot, String previousChildName) { }

    @Override public void onCancelled() { }
});

// This displays all chat messages set at or before 7:32 PM on 2/11/2012:
messageListRef.endAt(1329017600).addChildEventListener(new ChildEventListener() {
    @Override
    public void onChildAdded(DataSnapshot snapshot, String previousChildName) {
        GenericTypeIndicator<Map<String, String>> t = new GenericTypeIndicator<Map<String, String>>() {};
        Map<String, String> userData = snapshot.getValue(t);
        System.out.println("User " + userData.get("user_id") + " said " + userData.get("text"));
    }

    @Override public void onChildChanged(DataSnapshot snapshot, String previousChildName) { }

    @Override public void onChildRemoved(DataSnapshot snapshot) { }

    @Override public void onChildMoved(DataSnapshot snapshot, String previousChildName) { }

    @Override public void onCancelled() { }
});

In many cases, multiple children may have the same priority. In the previous example, for instance, more than one chat message could be sent at the same time. If you would like to be more specific in your query and also specify the child name you would like to begin or end your query on, you can do so with an optional second argument as shown:

messageListRef.startAt(1329017600, '-IKxAoc').on('child_added', function(snapshot) {
  var messageInfo = snapshot.val();
  alert('User ' + messageInfo.user_id + ' said: ' + messageInfo.text);
});

[[messageListRef queryStartingAtPriority:@1329017600 andChildName:@"-IKxAoc"] observeEventType:FEventTypeChildAdded withBlock:^(FDataSnapshot *snapshot) {
  NSDictionary* messageInfo = snapshot.value;
  NSLog(@"User %@ said %@", messageInfo[@"user_id"], messageInfo[@"text"]);
}];

// This displays all chat messages set at or before 7:32 PM on 2/11/2012:
messageListRef.startAt(1329017600, "-IKxAoc").addChildEventListener(new ChildEventListener() {
    @Override
    public void onChildAdded(DataSnapshot snapshot, String previousChildName) {
        GenericTypeIndicator<Map<String, String>> t = new GenericTypeIndicator<Map<String, String>>() {};
        Map<String, String> userData = snapshot.getValue(t);
        System.out.println("User " + userData.get("user_id") + " said " + userData.get("text"));
    }

    @Override public void onChildChanged(DataSnapshot snapshot, String previousChildName) { }

    @Override public void onChildRemoved(DataSnapshot snapshot) { }

    @Override public void onChildMoved(DataSnapshot snapshot, String previousChildName) { }

    @Override public void onCancelled() { }
});

For a startAt query with a priority and a name, the event will trigger for all children with a priority greater than the specified priority or those with an equal priority and a name that is greater than or equal to the specified name. The endAt query works in a similar manner, but instead of specifying the first priority and/or name, it specifies the last and will trigger events for all the preceeding children.

Combining Queries

Any query method will return a Firebase reference. This reference can be used to combine query methods to produce more complex queries.

For example, you could create a query which uses both an endAt and a limit query.

// Trigger event for 100 most recent messages before 7:32 PM on 2/11/2012
messageListRef.endAt(1329017600).limit(100).on('child_added', ...);

// Trigger event for 100 most recent messages before 7:32 PM on 2/11/2012
[[[messageListRef queryEndingAtPriority:@1329017600] queryLimitedToNumberOfChildren:100] observeEventType:FEventTypeChildAdded withBlock:^(FDataSnapshot *snapshot) { ... }];

// Trigger event for 100 most recent messages before 7:32 PM on 2/11/2012
messageListRef.endAt(1329017600).limit(100).addChildEventListener(new ChildEventListener() { ... });

You could also combine the startAt and endAt queries to trigger events for children in a specific range.

// Trigger event for messages between 7:32 PM and 8:11 PM on 2/11/2012
messageListRef.startAt(1329017600).endAt(1329019865).on('child_added', ...);

// Trigger event for messages between 7:32 PM and 8:11 PM on 2/11/2012
[[[messageListRef queryStartingAtPriority:@1329017600] queryEndingAtPriority:@1329019865] observeEventType:FEventTypeChildAdded withBlock:^(FDataSnapshot *snapshot) { ... }];

// Trigger event for messages between 7:32 PM and 8:11 PM on 2/11/2012
messageListRef.startAt(1329017600).endAt(1329019865).addChildEventListener(new ChildEventListener() { ... });

Resources

We have published a series of blog posts to help users take full advantage of Firebase queries.


Have a suggestion to improve the documentation on this page? Tell us!