Search code examples
angularjsfirebaseangularfire

Firebase - Filter by a column


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 }));

Solution

  • I have some suggestions that may help here:

    Example

    • Check out this working CodePen demo.
      • 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() {
        const ref = new Firebase(`${fbUrl}/series`);
        return new SeriesArrayFactory(ref);
      }
    });
    
    app.factory('SeriesArrayFactory', function($firebaseArray, $q) {
      return $firebaseArray.$extend({
        findSeries: function(seriesName) {
          const deferred = $q.defer();
    
          // query by 'name'
          this.$ref()
            .orderByChild('name')
            .equalTo(seriesName)
            .once('value', function(dataSnapshot) {
              if (dataSnapshot.exists()) {
                const value = dataSnapshot.val();
                deferred.resolve(value);
              } else {
                deferred.reject('Not found');
              }
            })
    
          return deferred.promise;
        }
      });
    });
    

    Controller

    app.controller('HomeController',function($scope, SeriesFactory, fbUrl) {
      $scope.seriesName = '';
    
      $scope.findSeries = function() {
        const seriesCollection = new SeriesFactory();
    
        seriesCollection
          .findSeries($scope.seriesName)
          .then(function(data) {
            $scope.series = data;
          })
          .catch(function(error) {
            console.error(error);
          });
      };
    });
    

    Without Extended Service

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

    $scope.findSeriesWithoutFactory = function() {
      const seriesRef = new Firebase(`${fbUrl}/series`);
      const seriesCollection = $firebaseArray(seriesRef);
    
      seriesCollection.$ref()
        .orderByChild('name')
        .equalTo($scope.seriesName)
        .once('value', function(dataSnapshot) {
          if (dataSnapshot.exists()){
            $scope.series = dataSnapshot.val();
          } else {
            console.error('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. See the Indexing Your Data portion of the Firebase Security & Rules Guide for more information. Below is an example:

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