Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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 }));
like image 897
Matan Yadaev Avatar asked Mar 20 '15 10:03

Matan Yadaev


1 Answers

I have some suggestions that may help here:

  • Check out the Firebase Query documentation. Specifically,
    • .orderByChild()
    • .equalTo()
  • You can use queries in conjunction with .$ref() to get the desired record.

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.
    • See the documentation for extending AngularFire services.
    • You can accomplish the same thing without extending the service, see last code snippet.

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"
  }
}
like image 176
sbolel Avatar answered Nov 16 '22 00:11

sbolel