Search code examples
angularfirebase-realtime-databasemomentjsionic3angular2-moment

Retrieve specific data by date with Angular and moment.js


I have a database structure like these (Firebase):

+ statistics
|-+ 2016
| |-+ 9
|   |-+ hours: 0
|-+ 2017
  |-+ 6
  | |-+ hours: 0
  |-+ 7
  |-+ 8
  |-+ 9 

The parents are years, the childs are months (9 for example is September) It´s easy to create a list to show the sum of all hours from one year. For example from 2017. But what if I want to show the sum of all hours from Steptember 2016 to September 2017?

I'm planning an application where this would be important for this app a year begins at September and ends at September next year. So I need the data from 2016->9 to 2017->9 How can I handle this?

I'm using Ionic3 / Angular2, Firebase and moment.js for date handling.

More concrete:

I want to get data like this: Give me all 'hours'-values from September 2016 to September 2017 and build these to an array


Solution

  • I'll assume that your using the Realtime Database; however, I'm sure the Firestore design/implementation is similar.

    You may want to consider an alternate structure like so:

    statistics: {
      unix_ts_for_sept_2016: {
        unix_ts: unix_ts_for_sept_2016
        hours:
      },
      unix_ts_for_oct_2016: {
        unix_ts: unix_ts_for_oct_2016
        hours:
      },
      unix_ts_for_nov_2016: {
        unix_ts: unix_ts_for_nov_2016
        hours:
      }
      [,...]
    }
    

    The timestamp as a key allows you to get/set any single month, while the timestamp as a child value allows you to perform range queries.

    const path_to_statistics = 'statistics/';
    const hours_field_name = 'hours';
    const ts_field_name = 'unix_ts';
    
    // Update the hours for a given month/yr in 'MM-DDDD' format
    var update_hours = function(mm_dash_yyyy, delta_hours) {
      const unix_ts = moment(mm_dash_yyyy, 'MM-YYYY').valueOf();
      const path = path_to_statistics + unix_ts + '/' + hours_field_name;
      const ref = firebase.database().ref(path);
      var deferred = $q.defer();
    
      ref.transaction(function(current) {
        if (current !== null) { return current + delta_hours; }
        return current;
      }, function(error) {
        if (error) { deferred.reject(error); }
        else { deferred.resolve(); }
      });
    
      return deferred.promise;
    };
    
    // Returns sum of hours in a given date range.
    var get_hours_in_range = function(start_mm_dash_yyyy, end_mm_dash_yyyy) {
      const start_ts = moment(start_mm_dash_yyyy, 'MM-YYYY').valueOf();
      const end_ts = moment(end_mm_dash_yyyy, 'MM-YYYY').valueOf();
      const ref = firebase.database().ref(path_to_statistics);
      const query = ref.orderByChild(ts_field_name).startAt(start_ts).endAt(end_ts);
      var deferred = $q.defer();
    
      query.once('value').then(function(snap) {
        var hours = 0;
        snap.forEach(function(child_snap) {
          hours += child_snap.val()[hours_field_name];
        });
        deferred.resolve(hours);
      }).catch(function(error) { deferred.reject(error); });
    
      return deferred.promise;
    };
    
    //usage
    update_hours('09-2016', 10).then(function() {
      console.log('updated hours.');
    }).catch(function(error) { console.error(error); })
    
    get_hours_in_range('09-2016', '10-2016').then(function(hours) {
      console.log(hours);
    }).catch(function(error) { console.error(error); });