不要怂,就是干,撸起袖子干!

Commit 8e621213 by Sascha Depold

Merge branch 'scopes' of https://github.com/durango/sequelize into durango-scopes

Conflicts:
	lib/dialects/mysql/query-generator.js
	lib/query-interface.js
	lib/utils.js
	spec/dao-factory.spec.js
2 parents 042249b6 36546551
...@@ -19,7 +19,9 @@ module.exports = (function() { ...@@ -19,7 +19,9 @@ module.exports = (function() {
whereCollection: null, whereCollection: null,
schema: null, schema: null,
schemaDelimiter: '', schemaDelimiter: '',
language: 'en' language: 'en',
defaultScope: null,
scopes: null
}, options || {}) }, options || {})
// error check options // error check options
...@@ -46,6 +48,7 @@ module.exports = (function() { ...@@ -46,6 +48,7 @@ module.exports = (function() {
this.rawAttributes = attributes this.rawAttributes = attributes
this.daoFactoryManager = null // defined in init function this.daoFactoryManager = null // defined in init function
this.associations = {} this.associations = {}
this.scopeObj = {}
} }
Object.defineProperty(DAOFactory.prototype, 'attributes', { Object.defineProperty(DAOFactory.prototype, 'attributes', {
...@@ -106,6 +109,10 @@ module.exports = (function() { ...@@ -106,6 +109,10 @@ module.exports = (function() {
this.primaryKeyCount = Object.keys(this.primaryKeys).length; this.primaryKeyCount = Object.keys(this.primaryKeys).length;
this.options.hasPrimaryKeys = this.hasPrimaryKeys = this.primaryKeyCount > 0; this.options.hasPrimaryKeys = this.hasPrimaryKeys = this.primaryKeyCount > 0;
if (typeof this.options.defaultScope === "object") {
Utils.injectScope.call(this, this.options.defaultScope)
}
addDefaultAttributes.call(this) addDefaultAttributes.call(this)
addOptionalClassMethods.call(this) addOptionalClassMethods.call(this)
findAutoIncrementField.call(this) findAutoIncrementField.call(this)
...@@ -222,6 +229,87 @@ module.exports = (function() { ...@@ -222,6 +229,87 @@ module.exports = (function() {
return this.QueryGenerator.addSchema(this) return this.QueryGenerator.addSchema(this)
} }
DAOFactory.prototype.scope = function(option) {
var self = Object.create(this)
, type
, options
, merge
, i
, scope
, scopeName
, argLength = arguments.length
, lastArg = arguments[argLength-1]
// Set defaults
scopeOptions = (typeof lastArg === "object" && !Array.isArray(lastArg) ? lastArg : {}) || {} // <-- for no arguments
scopeOptions.silent = (scopeOptions !== null && scopeOptions.hasOwnProperty('silent') ? scopeOptions.silent : true)
// Clear out any predefined scopes...
self.scopeObj = {}
// Possible formats for option:
// String of arguments: 'hello', 'world', 'etc'
// Array: ['hello', 'world', 'etc']
// Object: {merge: 'hello'}, {method: ['scopeName' [, args1, args2..]]}, {merge: true, method: ...}
if (argLength < 1 || !option) {
return self
}
for (i = 0; i < argLength; i++) {
options = Array.isArray(arguments[i]) ? arguments[i] : [arguments[i]]
options.forEach(function(o){
type = typeof o
scope = null
merge = false
scopeName = null
if (type === "object") {
// Right now we only support a merge functionality for objects
if (!!o.merge) {
merge = true
scopeName = o.merge[0]
if (Array.isArray(o.merge) && !!self.options.scopes[scopeName]) {
scope = self.options.scopes[scopeName].apply(self, o.merge.splice(1))
}
else if (typeof o.merge === "string") {
scopeName = o.merge
scope = self.options.scopes[scopeName]
}
}
if (!!o.method) {
if (Array.isArray(o.method) && !!self.options.scopes[o.method[0]]) {
scopeName = o.method[0]
scope = self.options.scopes[scopeName].apply(self, o.method.splice(1))
merge = !!o.merge
}
else if (!!self.options.scopes[o.method]) {
scopeName = o.method
scope = self.options.scopes[scopeName].apply(self)
}
} else {
scopeName = o
scope = self.options.scopes[scopeName]
}
} else {
scopeName = o
scope = self.options.scopes[scopeName]
}
if (!!scope) {
Utils.injectScope.call(self, scope, merge)
}
else if (scopeOptions.silent !== true && !!scopeName) {
throw new Error("Invalid scope " + scopeName + " called.")
}
})
}
return self
}
// alias for findAll // alias for findAll
DAOFactory.prototype.all = function(options, queryOptions) { DAOFactory.prototype.all = function(options, queryOptions) {
return this.findAll(options, queryOptions) return this.findAll(options, queryOptions)
......
...@@ -467,8 +467,13 @@ module.exports = (function() { ...@@ -467,8 +467,13 @@ module.exports = (function() {
result.push([_key, _value].join("=")) result.push([_key, _value].join("="))
} else { } else {
for (var logic in value) { for (var logic in value) {
var logicResult = this.getWhereLogic(logic) var logicResult = Utils.getWhereLogic(logic)
if (logicResult === "BETWEEN" || logicResult === "NOT BETWEEN") { if (logic === "IN" || logic === "NOT IN") {
var values = Array.isArray(where[i][ii]) ? where[i][ii] : [where[i][ii]]
_where[_where.length] = i + ' ' + logic + ' (' + values.map(function(){ return '?' }).join(',') + ')'
_whereArgs = _whereArgs.concat(values)
}
else if (logicResult === "BETWEEN" || logicResult === "NOT BETWEEN") {
_value = this.escape(value[logic][0]) _value = this.escape(value[logic][0])
var _value2 = this.escape(value[logic][1]) var _value2 = this.escape(value[logic][1])
...@@ -488,33 +493,6 @@ module.exports = (function() { ...@@ -488,33 +493,6 @@ module.exports = (function() {
return result.join(" AND ") return result.join(" AND ")
}, },
getWhereLogic: function(logic) {
switch (logic) {
case 'gte':
return '>='
break
case 'gt':
return '>'
break
case 'lte':
return '<='
break
case 'lt':
return '<'
break
case 'ne':
return '!='
break
case 'between':
return 'BETWEEN'
break
case 'nbetween':
case 'notbetween':
return 'NOT BETWEEN'
break
}
},
attributesToSQL: function(attributes) { attributesToSQL: function(attributes) {
var result = {} var result = {}
......
...@@ -572,8 +572,13 @@ module.exports = (function() { ...@@ -572,8 +572,13 @@ module.exports = (function() {
result.push([_key, _value].join("=")) result.push([_key, _value].join("="))
} else { } else {
for (var logic in value) { for (var logic in value) {
var logicResult = this.getWhereLogic(logic) var logicResult = Utils.getWhereLogic(logic)
if (logicResult === "BETWEEN" || logicResult === "NOT BETWEEN") { if (logic === "IN" || logic === "NOT IN") {
var values = Array.isArray(where[i][ii]) ? where[i][ii] : [where[i][ii]]
_where[_where.length] = i + ' ' + logic + ' (' + values.map(function(){ return '?' }).join(',') + ')'
_whereArgs = _whereArgs.concat(values)
}
else if (logicResult === "BETWEEN" || logicResult === "NOT BETWEEN") {
_value = this.escape(value[logic][0]) _value = this.escape(value[logic][0])
var _value2 = this.escape(value[logic][1]) var _value2 = this.escape(value[logic][1])
...@@ -593,33 +598,6 @@ module.exports = (function() { ...@@ -593,33 +598,6 @@ module.exports = (function() {
return result.join(' AND ') return result.join(' AND ')
}, },
getWhereLogic: function(logic) {
switch (logic) {
case 'gte':
return '>='
break
case 'gt':
return '>'
break
case 'lte':
return '<='
break
case 'lt':
return '<'
break
case 'ne':
return '!='
break
case 'between':
return 'BETWEEN'
break
case 'nbetween':
case 'notbetween':
return 'NOT BETWEEN'
break
}
},
attributesToSQL: function(attributes) { attributesToSQL: function(attributes) {
var result = {} var result = {}
......
...@@ -405,6 +405,22 @@ module.exports = (function() { ...@@ -405,6 +405,22 @@ module.exports = (function() {
QueryInterface.prototype.select = function(factory, tableName, options, queryOptions) { QueryInterface.prototype.select = function(factory, tableName, options, queryOptions) {
options = options || {} options = options || {}
// See if we need to merge options and factory.scopeObj
// we're doing this on the QueryInterface level because it's a bridge between
// sequelize and the databases
if (Object.keys(factory.scopeObj).length > 0) {
if (!!options) {
Utils.injectScope.call(factory, options, true)
}
var scopeObj = buildScope.call(factory)
Object.keys(scopeObj).forEach(function(method) {
if (typeof scopeObj[method] === "number" || !Utils._.isEmpty(scopeObj[method])) {
options[method] = scopeObj[method]
}
})
}
var sql = this.QueryGenerator.selectQuery(tableName, options, factory) var sql = this.QueryGenerator.selectQuery(tableName, options, factory)
queryOptions = Utils._.extend({}, queryOptions, { include: options.include }) queryOptions = Utils._.extend({}, queryOptions, { include: options.include })
return queryAndEmit.call(this, [sql, factory, queryOptions], 'select') return queryAndEmit.call(this, [sql, factory, queryOptions], 'select')
...@@ -502,6 +518,17 @@ module.exports = (function() { ...@@ -502,6 +518,17 @@ module.exports = (function() {
return this.QueryGenerator.escape(value) return this.QueryGenerator.escape(value)
} }
// private
var buildScope = function() {
var smart
// Use smartWhere to convert several {where} objects into a single where object
smart = Utils.smartWhere(this.scopeObj.where || [], this.daoFactoryManager.sequelize.options.dialect)
smart = Utils.compileSmartWhere.call(this, smart, this.daoFactoryManager.sequelize.options.dialect)
return {limit: this.scopeObj.limit || null, offset: this.scopeObj.offset || null, where: smart, order: (this.scopeObj.order || []).join(', ')}
}
var queryAndEmit = QueryInterface.prototype.queryAndEmit = function(sqlOrQueryParams, methodName, options, emitter) { var queryAndEmit = QueryInterface.prototype.queryAndEmit = function(sqlOrQueryParams, methodName, options, emitter) {
options = Utils._.extend({ options = Utils._.extend({
success: function(){}, success: function(){},
......
...@@ -61,6 +61,68 @@ var Utils = module.exports = { ...@@ -61,6 +61,68 @@ var Utils = module.exports = {
var timeZone = null; var timeZone = null;
return SqlString.formatNamedParameters(sql, parameters, timeZone, dialect) return SqlString.formatNamedParameters(sql, parameters, timeZone, dialect)
}, },
injectScope: function(scope, merge) {
var self = this
scope = scope || {}
self.scopeObj = self.scopeObj || {}
if (Array.isArray(scope.where)) {
self.scopeObj.where = self.scopeObj.where || []
self.scopeObj.where.push(scope.where)
return true
}
if (typeof scope.order === "string") {
self.scopeObj.order = self.scopeObj.order || []
self.scopeObj.order[self.scopeObj.order.length] = scope.order
}
// Limit and offset are *always* merged.
if (!!scope.limit) {
self.scopeObj.limit = scope.limit
}
if (!!scope.offset) {
self.scopeObj.offset = scope.offset
}
// Where objects are a mixed variable. Possible values are arrays, strings, and objects
if (!!scope.where) {
// Begin building our scopeObj
self.scopeObj.where = self.scopeObj.where || []
// Reset if we're merging!
if (merge === true && !!scope.where && !!self.scopeObj.where) {
var scopeKeys = Object.keys(scope.where)
self.scopeObj.where = self.scopeObj.where.map(function(scopeObj) {
if (!Array.isArray(scopeObj) && typeof scopeObj === "object") {
return lodash.omit.apply(undefined, [scopeObj].concat(scopeKeys))
} else {
return scopeObj
}
}).filter(function(scopeObj) {
return !lodash.isEmpty(scopeObj)
})
self.scopeObj.where = self.scopeObj.where.concat(scope.where)
}
if (Array.isArray(scope.where)) {
self.scopeObj.where.push(scope.where)
}
else if (typeof scope.where === "object") {
Object.keys(scope.where).forEach(function(){
self.scopeObj.where.push(scope.where)
})
} else { // Assume the value is a string
self.scopeObj.where.push([scope.where])
}
}
if (!!self.scopeObj.where) {
self.scopeObj.where = lodash.uniq(self.scopeObj.where)
}
},
// smartWhere can accept an array of {where} objects, or a single {where} object. // smartWhere can accept an array of {where} objects, or a single {where} object.
// The smartWhere function breaks down the collection of where objects into a more // The smartWhere function breaks down the collection of where objects into a more
// centralized object for each column so we can avoid duplicates // centralized object for each column so we can avoid duplicates
...@@ -225,45 +287,32 @@ var Utils = module.exports = { ...@@ -225,45 +287,32 @@ var Utils = module.exports = {
return 'JOIN' return 'JOIN'
case 'gte': case 'gte':
return '>=' return '>='
break
case 'gt': case 'gt':
return '>' return '>'
break
case 'lte': case 'lte':
return '<=' return '<='
break
case 'lt': case 'lt':
return '<' return '<'
break
case 'eq': case 'eq':
case 'join':
return '=' return '='
break
case 'ne': case 'ne':
return '!=' return '!='
break
case 'between': case 'between':
case '..': case '..':
return 'BETWEEN' return 'BETWEEN'
break
case 'nbetween': case 'nbetween':
case 'notbetween': case 'notbetween':
case '!..': case '!..':
return 'NOT BETWEEN' return 'NOT BETWEEN'
break
case 'in': case 'in':
return 'IN' return 'IN'
break
case 'not': case 'not':
return 'NOT IN' return 'NOT IN'
break
case 'like': case 'like':
return 'LIKE' return 'LIKE'
break
case 'nlike': case 'nlike':
case 'notlike': case 'notlike':
return 'NOT LIKE' return 'NOT LIKE'
break
default: default:
return '' return ''
} }
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!