Firebase - Filter by a column

Matan Yadaev picture Matan Yadaev · Mar 20, 2015 · Viewed 9k times · Source

I'm using Firebase for my Angular.js application.

I'm looking for the equivalent of SQL's WHERE statement for Firebase.

I have an array of TV series stored in Firebase, and I want to fetch only these that has the name that the user entered (in the example searchQuery).

Does Firebase support it? Does it have something like this?

var seriesRef = new Firebase('http://{app}.firebaseio.com/series');
var seriesObject = $firebaseObject(seriesRef.query({ name: searchQuery }));

Answer

Sinan Bolel picture Sinan Bolel · Mar 21, 2015

I have some suggestions that may help here:

Example

  • Check out this working PLNKR example.
    • I replicated your data in one of my public Firebase instances.
    • The query that you're looking for is seriesCollectionRef.orderByChild("name").equalTo(seriesName)
    • If you enter 'Avatar: The Last Airbender' in the input and click "Find", you'll get the matching series object.
  • In my example, I extended the $firebaseArray service to include a method for finding a specific series by name.

Factories

app.factory('SeriesFactory', function(SeriesArrayFactory, fbUrl){
  return function(){
    var ref = new Firebase(fbUrl+'/series');
    return new SeriesArrayFactory(ref);
  }
});

app.factory('SeriesArrayFactory', function($firebaseArray, $q){
  return $firebaseArray.$extend({
    findSeries:function(seriesName){
      var deferred = $q.defer();
      // query by 'name'
      this.$ref().orderByChild("name").equalTo(seriesName).once("value", function(dataSnapshot){
        if(dataSnapshot.exists()){
          deferred.resolve(dataSnapshot.val());
        } else {
          deferred.reject("Not found.");
        }
      });
      return deferred.promise;
    }
  });
});

Controller

app.controller('HomeController',function($scope, SeriesFactory, fbUrl) {

  $scope.seriesName = '';

  $scope.findSeries = function() {
    console.log("Finding series with name:'",$scope.seriesName,"'");
    var seriesCollection = new SeriesFactory();
    seriesCollection.findSeries($scope.seriesName).then(function(data){
      console.log("Found",data);
      $scope.series = data;
    }).catch(function(error){
      console.warn(error);
    });
  };

});

Without Extended Service

Here is what a controller function would look like if you weren't using the factories:

  $scope.findSeriesWithoutFactory = function() {
    var seriesRef = new Firebase(fbUrl+'/series');
    var seriesCollection = $firebaseArray(seriesRef);
    seriesCollection.$ref().orderByChild("name").equalTo($scope.seriesName).once("value", function(dataSnapshot){
        var series = dataSnapshot.val();
        if(dataSnapshot.exists()){
          console.log("Found", series);
          $scope.series = series;
        } else {
          console.warn("Not found.");
        }
    });
  };

Rules

Note: It's important to note that you should add ".indexOn":"name" to your Firebase rules so that the query runs efficiently. Like so:

  "yourfirebaseapp": {
    ".read": "...",
    ".write": "...",
    "series": {
      ".indexOn": "name"
    }
  }

Hope that helps!