Search code examples
mysqlloopback

aggregation on loopback mysql query


I want to know how to make aggregation on my loopback query I am using MySQL as the database I have a record like this in my DB - { 'app_name': 'chrome', 'duration' : 10000 }, { 'app_name': 'WhatsApp', 'duration' : 25000 } and so on note that duration is in milliseconds. I am using angular 7 as front end and I want to make loopback query to sum the duration of my all records, right now I am making a query like -

Apps.find({}).toPromise()
    .then(apps => {
        let result = apps.reduce((app, total) = {
            total += app.duration;
            return total
        }, 0)
        console.log(result);
    })
    .catch(err => {
        console.log(err);
     })

However, with this approach, I can get the sum of the duration but if I have millions of records than its not a scalable solution like iterating from millions of records and then sum the duration. I want to know if there is aggregation in loopback query for MySQL. I want a query like -

Apps.find({
      aggregation: {
       sum: 'duration'
     }
    }).toPromise()
    .then(result => {
        console.log(result);
    })
    .catch(err => {
        console.log(err);
     })

something like that.


Solution

  • LoopBack does not support aggregations yet. My recommendation is to write a custom controller method that will execute a custom SQL query to aggregate the results.

    // in your controller
    export class MyController {
      constructor(
        @repository(AppRepository) protected appRepo: AppRepository
      ) {}
    
      // default CRUD methods scaffolded by lb4
    
      // custom method
      @get('/apps/durations')
      getDurations() {
        return this.appRepo.getAggregatedDurations();
      }
    }
    
    // in your AppRepository
    export class AppRepository extends DefaultCrudRepository<
      App,
      typeof App.prototype.id,
      AppRelations
    > {
      constructor(
        @inject('datasources.db') dataSource: juggler.DataSource,
        // ...
      ) {
        super(App, dataSource);
        // ...
      }
    
      // add a new method
      async getAggregatedDurations(): Promise<number> {
        // A mock-up SQL query, you may need to tweak it
        const result = await this.execute('SELECT SUM(duration) as total_duration FROM Apps');
        // I am not entirely sure what's the shape of result object,
        // you have to find out yourself how to access "total_duration" field 
        return result[0].total_duration;
      }
    }
    

    See also API Docs for execute method: https://loopback.io/doc/en/lb4/apidocs.repository.executablerepository.execute.html and LoopBack 3.x documentation: https://loopback.io/doc/en/lb3/Executing-native-SQL.html