Question

I would like to define a custom finder on my sequelize object. For example, suppose I have a Order model.

var Orders = sequelize.define('Orders', {
  ...
})

I have a complex query that I would like to reuse in various places.

var sql = 'SELECT'
  + ' CONCAT_WS(\':\', `type`, `id`) AS `id`'
  + ' , `type`'
  + ' , `name`'
  + ' , `amount`'
  + ' , `quantity`'
  + ' , `total`'
  + ' , `createdAt`'
  + ' FROM (' 
  + ' SELECT'
  + '  OrderItems.id AS `id`'
  + ' , \'item\' AS `type`'
  + ' , Items.name AS `name`'
  + ' , Items.price AS `amount`'
  + ' , OrderItems.quantity AS `quantity`'
  + ' , OrderItems.quantity * Items.price AS `total`'
  + ' , OrderItems.createdAt AS `createdAt`'
  + ' FROM OrderItems'
  + ' INNER JOIN Items ON Items.id = OrderItems.ItemId'
  + ' WHERE OrderId = :OrderId'
  + ' UNION'
  + ' SELECT'
  + '  OrderPayments.id AS `id`'
  + ' , \'payment\' AS `type`'
  + ' , Payments.name AS `name`'
  + ' , OrderPayments.amount AS `amount`'
  + ' , 0 AS `quantity`'
  + ' , OrderPayments.amount AS `total`'
  + ' , OrderPayments.createdAt AS `createdAt`'
  + ' FROM OrderPayments'
  + ' INNER JOIN Payments ON Payments.id = OrderPayments.PaymentId'
  + ' WHERE OrderId = :OrderId'
  + ' ) OrderLines'
  + ' ORDER BY createdAt DESC'

Here's how I'd like to run the query.

Orders.find(123).success(function(order) {
    order.getDataFromMyComplexQuery('arg1', 'arg2').success(function(results){
        // Do something with the results
    })
});

Or like:

Orders.getDataFromMyComplexQuery('arg1', 'arg2').success(function(results){
    // Do something with the results
})

It's like a stored procedure but in Sequelize.

Was it helpful?

Solution 2

You can define custom methods with the instanceMethods and classMethods objects. From the docs:

Sequelize allows you to pass custom methods to a model and it's instances. Just do the following:

var Foo = sequelize.define('Foo', { /* attributes */}, {
  classMethods: {
    method1: function(){ return 'smth' }
  },
  instanceMethods: {
    method2: function() { return 'foo' }
  }
})

// Example:
Foo.method1()
Foo.build().method2()

OTHER TIPS

Well, it seems here we fall into terminology mess provided by different javascript developers. The thing called in Sequelize as Model looks like Repository. But the model – is the argument passed in Repository on definition. And exactly the model (or models collection) being returned on any finder method. Clearing this things out making easy to understand that your task is to extend repository with custom finder. Here is how I did this:

/**
     * custom method to retrieve matches by complex conditions
     * @param roundId – integer, required
     * @param conditions – optional object of type: { teamId: integer, isFinished: boolean, searchTerm: string }
     * to catch result chain .success( function( matches ){} ) call
     * to catch error chain .error( function( errorText ){} ) call
     * @return this to make chaining available
     */
    matchesRepository.getByRound = function( roundId, conditions ){
        /** handling chaining for .success and .error callbacks */
        var successCallback,
            errorCallback
            self = this;
        this.success = function( callback ){
            if( 'function' != typeof callback ){
                throw "Function expected as successCallback"
            }
            successCallback = callback;
            return self;
        }
        this.error = function( callback ){
            if( 'function' != typeof callback ){
                throw "Function expected as callback";
            }
            errorCallback = callback;
            return self;
        }

        /** validate params */
        var roundId = parseInt( roundId );
        if( isNaN( roundId ) ){
            errorCallback( "Unable to getByRound matches until roundId not specified" );
            return this;
        }

        if( 'undefined' == typeof conditions ){
            conditions = {};
        }

        /** build statement */
        var statement = "SELECT @matches.* FROM @matches " +
                        "LEFT OUTER JOIN @teams as team1 ON team1.team_id = @matches.id_team1 " +
                        "LEFT OUTER JOIN @teams as team2 ON team2.team_id = @matches.id_team2 WHERE ";
        var where = [];

        if( 'undefined' != typeof conditions.teamId ){
            var teamId = parseInt( conditions.teamId );
            if( isNaN( teamId ) ){
                errorCallback( "Integer or eval string expected in conditions.teamId; `" +
                                conditions.teamId + "` provided" );
                return this;
            }
            where.push( '( (id_team1 = ' + teamId + ' OR id_team2 = ' + teamId + ') )' );
        }
        if( 'undefined' != typeof conditions.isFinished ){
            if( true == conditions.isFinished ){
                where.push( '( gf IS NOT NULL AND ga IS NOT NULL )' );
            } else {
                where.push( '( gf IS NULL AND ga IS NULL )' );
            }
        }
        if( 'undefined' != typeof conditions.searchTerm ){
            where.push( '( team1.title LIKE "%' + conditions.searchTerm + '%" OR ' +
                        'team2.title LIKE "%' + conditions.searchTerm + '%" )' );
        }
        var matchesTable = core.factories.match.name,
            teamsTable = core.factories.team.name,
            preparedQuery = statement + where.join( " AND " ),
            sqlQuery = preparedQuery.replace( /@matches/g, matchesTable )
                                    .replace( /@teams/g, teamsTable );

        sequelize.query( sqlQuery )
            .success( function( matches ){
                successCallback( matches );
            })
            .error( function( errorText ){
                errorCallback( errorText );
            });
        return this;
    }
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top