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

Commit fe5a85b0 by Jan Aagaard Meier

refactor(scopes): Simplified implementation, added more tests, 💄 remove…

…d cruft from query generator
1 parent f970157b
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#### Backwards compatibility changes #### Backwards compatibility changes
- Events support have been removed so using `.on('succes')` or `.succes()` is no longer supported. - Events support have been removed so using `.on('succes')` or `.succes()` is no longer supported.
- Trying to apply a scope that does not exist will always throw an error
# 2.0.6 # 2.0.6
- [BUG] Don't update virtual attributes in Model.update. Fixes [#2860](https://github.com/sequelize/sequelize/issues/2860) - [BUG] Don't update virtual attributes in Model.update. Fixes [#2860](https://github.com/sequelize/sequelize/issues/2860)
......
...@@ -351,7 +351,16 @@ module.exports = (function() { ...@@ -351,7 +351,16 @@ module.exports = (function() {
}); });
} }
return association.target.findAll(options, queryOptions); var model = association.target;
if (options.hasOwnProperty('scope')) {
if (!options.scope) {
model = model.unscoped();
} else {
model = model.scope(options.scope);
}
}
return model.findAll(options, queryOptions);
}; };
obj[this.accessors.hasAll] = function(instances, options) { obj[this.accessors.hasAll] = function(instances, options) {
...@@ -359,21 +368,24 @@ module.exports = (function() { ...@@ -359,21 +368,24 @@ module.exports = (function() {
, where; , where;
options = options || {}; options = options || {};
options.scope = false;
instances.forEach(function(instance) { instances.forEach(function(instance) {
if (instance instanceof association.target.Instance) { if (instance instanceof association.target.Instance) {
where = new Utils.or([where, instance.primaryKeyValues]); where = { $or: [where, instance.primaryKeyValues]};
} else { } else {
var _where = {}; var _where = {};
_where[association.target.primaryKeyAttribute] = instance; _where[association.target.primaryKeyAttribute] = instance;
where = new Utils.or([where, _where]); where = { $or: [where, _where]};
} }
}); });
options.where = new Utils.and([ options.where = {
where, $and: [
options.where where,
]); options.where
]
};
return instance[association.accessors.get]( return instance[association.accessors.get](
options, options,
...@@ -388,6 +400,7 @@ module.exports = (function() { ...@@ -388,6 +400,7 @@ module.exports = (function() {
, where; , where;
options = options || {}; options = options || {};
options.scope = false;
if (param instanceof association.target.Instance) { if (param instanceof association.target.Instance) {
where = param.primaryKeyValues; where = param.primaryKeyValues;
...@@ -396,10 +409,12 @@ module.exports = (function() { ...@@ -396,10 +409,12 @@ module.exports = (function() {
where[association.target.primaryKeyAttribute] = param; where[association.target.primaryKeyAttribute] = param;
} }
options.where = new Utils.and([ options.where = {
where, $and: [
options.where where,
]); options.where
]
};
return instance[association.accessors.get]( return instance[association.accessors.get](
options, options,
...@@ -419,7 +434,9 @@ module.exports = (function() { ...@@ -419,7 +434,9 @@ module.exports = (function() {
options = options || {}; options = options || {};
var instance = this; var instance = this;
return instance[association.accessors.get]({}, { return instance[association.accessors.get]({
scope: false
}, {
transaction: options.transaction, transaction: options.transaction,
logging: options.logging logging: options.logging
}).then(function(oldAssociatedObjects) { }).then(function(oldAssociatedObjects) {
...@@ -668,6 +685,7 @@ module.exports = (function() { ...@@ -668,6 +685,7 @@ module.exports = (function() {
} }
return instance[association.accessors.get]({ return instance[association.accessors.get]({
scope: false,
where: newInstance.primaryKeyValues where: newInstance.primaryKeyValues
}, { }, {
transaction: (additionalAttributes || {}).transaction, transaction: (additionalAttributes || {}).transaction,
......
...@@ -97,20 +97,31 @@ module.exports = (function() { ...@@ -97,20 +97,31 @@ module.exports = (function() {
BelongsTo.prototype.injectGetter = function(instancePrototype) { BelongsTo.prototype.injectGetter = function(instancePrototype) {
var association = this; var association = this;
instancePrototype[this.accessors.get] = function(params) { instancePrototype[this.accessors.get] = function(options) {
var where = {}; var where = {};
where[association.targetIdentifier] = this.get(association.identifier);
params = association.target.__optClone(params) || {}; options = association.target.__optClone(options) || {};
params.where = (params.where && [params.where]) || [];
where[association.targetIdentifier] = this.get(association.identifier); options.where = {
params.where.push(where); $and: [
options.where,
where
]
};
params.where = new Utils.and(params.where); if (options.limit === undefined) options.limit = null;
if (params.limit === undefined) params.limit = null; var model = association.target;
if (options.hasOwnProperty('scope')) {
if (!options.scope) {
model = model.unscoped();
} else {
model = model.scope(options.scope);
}
}
return association.target.find(params); return model.find(options);
}; };
return this; return this;
......
...@@ -18,16 +18,27 @@ module.exports = (function() { ...@@ -18,16 +18,27 @@ module.exports = (function() {
}.bind(this)); }.bind(this));
} }
options.where = new Utils.and([ options.where = {
new Utils.where( $and: [
this.target.rawAttributes[this.association.identifier], new Utils.where(
this.instance[this.source.primaryKeyAttribute] this.target.rawAttributes[this.association.identifier],
), this.instance[this.source.primaryKeyAttribute]
scopeWhere, ),
options.where scopeWhere,
]); options.where
]
return this.association.target.all(options, queryOptions); };
var model = this.association.target;
if (options.hasOwnProperty('scope')) {
if (!options.scope) {
model = model.unscoped();
} else {
model = model.scope(options.scope);
}
}
return model.all(options, queryOptions);
}; };
HasManySingleLinked.prototype.injectSetter = function(oldAssociations, newAssociations, defaultAttributes) { HasManySingleLinked.prototype.injectSetter = function(oldAssociations, newAssociations, defaultAttributes) {
...@@ -100,7 +111,7 @@ module.exports = (function() { ...@@ -100,7 +111,7 @@ module.exports = (function() {
updateWhere[primaryKey] = unassociatedIds; updateWhere[primaryKey] = unassociatedIds;
promises.push(this.association.target.update( promises.push(this.association.target.unscoped().update(
update, update,
Utils._.extend(options, { Utils._.extend(options, {
allowNull: [self.association.identifier], allowNull: [self.association.identifier],
......
...@@ -334,6 +334,7 @@ module.exports = (function() { ...@@ -334,6 +334,7 @@ module.exports = (function() {
obj[this.accessors.get] = function(options, queryOptions) { obj[this.accessors.get] = function(options, queryOptions) {
options = association.target.__optClone(options) || {}; options = association.target.__optClone(options) || {};
queryOptions = queryOptions || {}; queryOptions = queryOptions || {};
var Class = Object(association.through.model) === association.through.model ? HasManyDoubleLinked : HasManySingleLinked; var Class = Object(association.through.model) === association.through.model ? HasManyDoubleLinked : HasManySingleLinked;
return new Class(association, this).injectGetter(options, queryOptions); return new Class(association, this).injectGetter(options, queryOptions);
...@@ -344,21 +345,24 @@ module.exports = (function() { ...@@ -344,21 +345,24 @@ module.exports = (function() {
, where; , where;
options = options || {}; options = options || {};
options.scope = false;
instances.forEach(function(instance) { instances.forEach(function(instance) {
if (instance instanceof association.target.Instance) { if (instance instanceof association.target.Instance) {
where = new Utils.or([where, instance.primaryKeyValues]); where = { $or: [where, instance.primaryKeyValues]};
} else { } else {
var _where = {}; var _where = {};
_where[association.target.primaryKeyAttribute] = instance; _where[association.target.primaryKeyAttribute] = instance;
where = new Utils.or([where, _where]); where = { $or: [where, _where]};
} }
}); });
options.where = new Utils.and([ options.where = {
where, $and: [
options.where where,
]); options.where
]
};
return instance[association.accessors.get]( return instance[association.accessors.get](
options, options,
...@@ -373,6 +377,7 @@ module.exports = (function() { ...@@ -373,6 +377,7 @@ module.exports = (function() {
, where; , where;
options = options || {}; options = options || {};
options.scope = false;
if (param instanceof association.target.Instance) { if (param instanceof association.target.Instance) {
where = param.primaryKeyValues; where = param.primaryKeyValues;
...@@ -381,10 +386,12 @@ module.exports = (function() { ...@@ -381,10 +386,12 @@ module.exports = (function() {
where[association.target.primaryKeyAttribute] = param; where[association.target.primaryKeyAttribute] = param;
} }
options.where = new Utils.and([ options.where = {
where, $and: [
options.where where,
]); options.where
]
};
return instance[association.accessors.get]( return instance[association.accessors.get](
options, options,
...@@ -420,7 +427,9 @@ module.exports = (function() { ...@@ -420,7 +427,9 @@ module.exports = (function() {
var instance = this; var instance = this;
return instance[association.accessors.get]({}, { return instance[association.accessors.get]({
scope: false
}, {
transaction: (additionalAttributes || {}).transaction, transaction: (additionalAttributes || {}).transaction,
logging: (additionalAttributes || {}).logging logging: (additionalAttributes || {}).logging
}).then(function(oldAssociatedObjects) { }).then(function(oldAssociatedObjects) {
...@@ -467,7 +476,8 @@ module.exports = (function() { ...@@ -467,7 +476,8 @@ module.exports = (function() {
} }
return instance[association.accessors.get]({ return instance[association.accessors.get]({
where: newInstance.primaryKeyValues where: newInstance.primaryKeyValues,
scope: false
}, { }, {
transaction: (additionalAttributes || {}).transaction transaction: (additionalAttributes || {}).transaction
}).then(function(currentAssociatedObjects) { }).then(function(currentAssociatedObjects) {
...@@ -483,7 +493,9 @@ module.exports = (function() { ...@@ -483,7 +493,9 @@ module.exports = (function() {
obj[this.accessors.remove] = function(oldAssociatedObject, options) { obj[this.accessors.remove] = function(oldAssociatedObject, options) {
var instance = this; var instance = this;
return instance[association.accessors.get]({}, options).then(function(currentAssociatedObjects) { return instance[association.accessors.get]({
scope: false
}, options).then(function(currentAssociatedObjects) {
var newAssociations = []; var newAssociations = [];
if (!(oldAssociatedObject instanceof association.target.Instance)) { if (!(oldAssociatedObject instanceof association.target.Instance)) {
...@@ -506,7 +518,9 @@ module.exports = (function() { ...@@ -506,7 +518,9 @@ module.exports = (function() {
obj[this.accessors.removeMultiple] = function(oldAssociatedObjects, options) { obj[this.accessors.removeMultiple] = function(oldAssociatedObjects, options) {
var instance = this; var instance = this;
return instance[association.accessors.get]({}, options).then(function(currentAssociatedObjects) { return instance[association.accessors.get]({
scope: false
}, options).then(function(currentAssociatedObjects) {
var newAssociations = []; var newAssociations = [];
// Ensure the oldAssociatedObjects array is an array of target instances // Ensure the oldAssociatedObjects array is an array of target instances
......
...@@ -96,20 +96,31 @@ module.exports = (function() { ...@@ -96,20 +96,31 @@ module.exports = (function() {
HasOne.prototype.injectGetter = function(instancePrototype) { HasOne.prototype.injectGetter = function(instancePrototype) {
var association = this; var association = this;
instancePrototype[this.accessors.get] = function(params) { instancePrototype[this.accessors.get] = function(options) {
var where = {}; var where = {};
where[association.identifier] = this.get(association.sourceIdentifier);
params = association.target.__optClone(params) || {}; options = association.target.__optClone(options) || {};
params.where = (params.where && [params.where]) || [];
where[association.identifier] = this.get(association.sourceIdentifier); options.where = {
params.where.push(where); $and: [
options.where,
where
]
};
params.where = new Utils.and(params.where); if (options.limit === undefined) options.limit = null;
if (params.limit === undefined) params.limit = null; var model = association.target;
if (options.hasOwnProperty('scope')) {
if (!options.scope) {
model = model.unscoped();
} else {
model = model.scope(options.scope);
}
}
return association.target.find(params); return model.find(options);
}; };
return this; return this;
...@@ -121,6 +132,8 @@ module.exports = (function() { ...@@ -121,6 +132,8 @@ module.exports = (function() {
instancePrototype[this.accessors.set] = function(associatedInstance, options) { instancePrototype[this.accessors.set] = function(associatedInstance, options) {
var instance = this; var instance = this;
options = options || {};
options.scope = false;
return instance[association.accessors.get](options).then(function(oldInstance) { return instance[association.accessors.get](options).then(function(oldInstance) {
if (oldInstance) { if (oldInstance) {
oldInstance[association.identifier] = null; oldInstance[association.identifier] = null;
......
...@@ -366,7 +366,7 @@ Mixin.getAssociation = function(target, alias) { ...@@ -366,7 +366,7 @@ Mixin.getAssociation = function(target, alias) {
if (this.associations.hasOwnProperty(associationName)) { if (this.associations.hasOwnProperty(associationName)) {
var association = this.associations[associationName]; var association = this.associations[associationName];
if (association.target === target && (alias === undefined ? !association.isAliased : association.as === alias)) { if (association.target.name === target.name && (alias === undefined ? !association.isAliased : association.as === alias)) {
return association; return association;
} }
} }
......
...@@ -1223,7 +1223,7 @@ module.exports = (function() { ...@@ -1223,7 +1223,7 @@ module.exports = (function() {
} else if (Utils._.isPlainObject(options.where)) { } else if (Utils._.isPlainObject(options.where)) {
options.where['__' + as] = subQueryWhere; options.where['__' + as] = subQueryWhere;
} else { } else {
options.where = new Utils.and(options.where, subQueryWhere); options.where = { $and: [options.where, subQueryWhere] };
} }
} }
...@@ -1533,7 +1533,9 @@ module.exports = (function() { ...@@ -1533,7 +1533,9 @@ module.exports = (function() {
result = (value === 'NULL') ? key + ' IS NULL' : [key, value].join(smth.comparator); result = (value === 'NULL') ? key + ' IS NULL' : [key, value].join(smth.comparator);
} else if (_.isPlainObject(value)) { } else if (_.isPlainObject(value)) {
result = this.plainObjectToWhere(value, key, key, factory).join(' AND '); result = this.whereItemQuery(smth.attribute, value, {
model: factory
});
} else { } else {
if (typeof value === 'boolean') { if (typeof value === 'boolean') {
value = this.booleanValue(value); value = this.booleanValue(value);
...@@ -1726,7 +1728,6 @@ module.exports = (function() { ...@@ -1726,7 +1728,6 @@ module.exports = (function() {
outerBinding = ''; outerBinding = '';
if (key === '$not') outerBinding = 'NOT '; if (key === '$not') outerBinding = 'NOT ';
if (Array.isArray(value)) { if (Array.isArray(value)) {
value = value.map(function (item) { value = value.map(function (item) {
var itemQuery = self.whereItemsQuery(item, options, ' AND '); var itemQuery = self.whereItemsQuery(item, options, ' AND ');
...@@ -2166,83 +2167,6 @@ module.exports = (function() { ...@@ -2166,83 +2167,6 @@ module.exports = (function() {
return [_key, _value].join(' ' + logicResult + ' '); return [_key, _value].join(' ' + logicResult + ' ');
}, },
/*
Takes a hash and transforms it into a mysql where condition: {key: value, key2: value2} ==> key=value AND key2=value2
The values are transformed by the relevant datatype.
*/
hashToWhereConditions: function(hash, dao, options) {
var result = [];
options = options || {};
// Closures are nice
Utils._.each(hash, function(value, key) {
var _key
, _value = null;
if (value && value._isSequelizeMethod === true && (value instanceof Utils.literal)) {
result.push(value.val);
return;
}
if (options.keysEscaped) {
_key = key;
} else {
if (this.isAssociationFilter(key, dao)) {
_key = key = this.getAssociationFilterColumn(key, dao, options);
} else {
_key = this.quoteIdentifiers(key);
}
}
if (Array.isArray(value)) {
result.push(this.arrayValue(value, key, _key, dao, 'IN'));
} else if (value && Utils._.isPlainObject(value)) {
result = result.concat(this.plainObjectToWhere(value, key, _key, dao));
} else {
if (typeof value === 'boolean') {
_value = this.booleanValue(value);
} else {
_value = this.escape(value);
}
result.push((_value === 'NULL') ? _key + ' IS NULL' : [_key, _value].join('='));
}
}.bind(this));
return result.join(' AND ');
},
plainObjectToWhere: function (value, key, _key, dao) {
var _value
, result = [];
if (!!value.join) {
//using as sentinel for join column => value
_value = this.quoteIdentifiers(value.join);
result.push([_key, _value].join('='));
} else {
for (var logic in value) {
var logicResult = Utils.getWhereLogic(logic, value[logic]);
if (logicResult === 'BETWEEN' || logicResult === 'NOT BETWEEN') {
_value = this.escape(value[logic][0]);
var _value2 = this.escape(value[logic][1]);
result.push(' (' + _key + ' ' + logicResult + ' ' + _value + ' AND ' + _value2 + ') ');
} else if (logicResult === 'IN' || logicResult === 'NOT IN' || Array.isArray(value[logic])) {
var values = Array.isArray(value[logic]) ? value[logic] : [value[logic]];
result.push(this.arrayValue(values, key, _key, dao, logicResult));
} else {
_value = this.escape(value[logic]);
result.push([_key, _value].join(' ' + logicResult + ' '));
}
}
}
return result;
},
booleanValue: function(value) { booleanValue: function(value) {
return value; return value;
} }
......
...@@ -481,7 +481,7 @@ module.exports = (function() { ...@@ -481,7 +481,7 @@ module.exports = (function() {
if (!resultMap[itemHash]) { if (!resultMap[itemHash]) {
$parent = resultMap[parentHash]; $parent = resultMap[parentHash];
$lastKeyPrefix = lastKeyPrefix(prevKey); $lastKeyPrefix = lastKeyPrefix(prevKey);
//console.log($parent, prevKey, $lastKeyPrefix);
if (includeMap[prevKey].association.isSingleAssociation) { if (includeMap[prevKey].association.isSingleAssociation) {
$parent[$lastKeyPrefix] = resultMap[itemHash] = values; $parent[$lastKeyPrefix] = resultMap[itemHash] = values;
} else { } else {
...@@ -561,7 +561,7 @@ module.exports = (function() { ...@@ -561,7 +561,7 @@ module.exports = (function() {
if (!resultMap[itemHash]) { if (!resultMap[itemHash]) {
$parent = resultMap[parentHash]; $parent = resultMap[parentHash];
$lastKeyPrefix = lastKeyPrefix(prevKey); $lastKeyPrefix = lastKeyPrefix(prevKey);
//console.log($parent, prevKey, $lastKeyPrefix);
if (includeMap[prevKey].association.isSingleAssociation) { if (includeMap[prevKey].association.isSingleAssociation) {
$parent[$lastKeyPrefix] = resultMap[itemHash] = values; $parent[$lastKeyPrefix] = resultMap[itemHash] = values;
} else { } else {
......
...@@ -680,22 +680,6 @@ module.exports = (function() { ...@@ -680,22 +680,6 @@ module.exports = (function() {
options.type = QueryTypes.SELECT; options.type = QueryTypes.SELECT;
// See if we need to merge options and model.scopeObj
// we're doing this on the QueryInterface level because it's a bridge between
// sequelize and the databases
if (model.options.defaultScope && Object.keys(model.options.defaultScope).length > 0) {
if (!!options) {
Utils.injectScope.call(model, options, true);
}
var scopeObj = buildScope.call(model);
Object.keys(scopeObj).forEach(function(method) {
if (typeof scopeObj[method] === 'number' || !Utils._.isEmpty(scopeObj[method])) {
options[method] = scopeObj[method];
}
});
}
options.instance = model; options.instance = model;
return this.sequelize.query( return this.sequelize.query(
this.QueryGenerator.selectQuery(tableName, options, model), this.QueryGenerator.selectQuery(tableName, options, model),
...@@ -935,16 +919,5 @@ module.exports = (function() { ...@@ -935,16 +919,5 @@ module.exports = (function() {
return this.sequelize.query(sql, options); return this.sequelize.query(sql, options);
}; };
// 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(', ')};
};
return QueryInterface; return QueryInterface;
})(); })();
...@@ -568,7 +568,8 @@ module.exports = (function() { ...@@ -568,7 +568,8 @@ module.exports = (function() {
delete options.modelName; delete options.modelName;
var factory = new Model(modelName, attributes, options); var factory = new Model(modelName, attributes, options);
this.modelManager.addDAO(factory.init(this.modelManager)); factory = factory.init(this.modelManager);
this.modelManager.addDAO(factory);
this.runHooks('afterDefine', factory); this.runHooks('afterDefine', factory);
......
...@@ -290,7 +290,7 @@ if (Support.dialectIsMySQL()) { ...@@ -290,7 +290,7 @@ if (Support.dialectIsMySQL()) {
) )
}; };
}], }],
expectation: "SELECT * FROM `myTable` WHERE (`myTable`.`archived` IS NULL AND COALESCE(`place_type_codename`, `announcement_type_codename`) IN ('Lost','Found'));", expectation: "SELECT * FROM `myTable` WHERE (`myTable`.`archived` IS NULL AND COALESCE(`place_type_codename`, `announcement_type_codename`) IN ('Lost', 'Found'));",
context: QueryGenerator, context: QueryGenerator,
needsSequelize: true needsSequelize: true
}, { }, {
...@@ -564,38 +564,6 @@ if (Support.dialectIsMySQL()) { ...@@ -564,38 +564,6 @@ if (Support.dialectIsMySQL()) {
arguments: ['User', ['foo', 'bar']], arguments: ['User', ['foo', 'bar']],
expectation: 'DROP INDEX user_foo_bar ON `User`' expectation: 'DROP INDEX user_foo_bar ON `User`'
} }
],
hashToWhereConditions: [
{
arguments: [{ id: [1, 2, 3] }],
expectation: '`id` IN (1,2,3)'
},
{
arguments: [{ id: [] }],
expectation: '`id` IN (NULL)'
},
{
arguments: [{ maple: false, bacon: true }],
expectation: '`maple`=false AND `bacon`=true'
},
{
arguments: [{ beaver: [false, true] }],
expectation: '`beaver` IN (false,true)'
},
{
arguments: [{birthday: new Date(Date.UTC(2011, 6, 1, 10, 1, 55))}],
expectation: "`birthday`='2011-07-01 10:01:55'"
},
{
arguments: [{ birthday: new Date(Date.UTC(2011, 6, 1, 10, 1, 55)),
otherday: new Date(Date.UTC(2013, 6, 2, 10, 1, 22)) }],
expectation: "`birthday`='2011-07-01 10:01:55' AND `otherday`='2013-07-02 10:01:22'"
},
{
arguments: [{ birthday: [new Date(Date.UTC(2011, 6, 1, 10, 1, 55)), new Date(Date.UTC(2013, 6, 2, 10, 1, 22))] }],
expectation: "`birthday` IN ('2011-07-01 10:01:55','2013-07-02 10:01:22')"
}
] ]
}; };
......
...@@ -368,8 +368,8 @@ describe(Support.getTestDialectTeaser('Model'), function() { ...@@ -368,8 +368,8 @@ describe(Support.getTestDialectTeaser('Model'), function() {
return self.Environment.find({ return self.Environment.find({
where: { name: 'environment' }, where: { name: 'environment' },
include: [ include: [
{ daoFactory: self.Domain, as: 'PrivateDomain' }, { model: self.Domain, as: 'PrivateDomain' },
{ daoFactory: self.Domain, as: 'PublicDomain' } { model: self.Domain, as: 'PublicDomain' }
] ]
}).then(function(environment) { }).then(function(environment) {
expect(environment).to.exist; expect(environment).to.exist;
...@@ -559,7 +559,7 @@ describe(Support.getTestDialectTeaser('Model'), function() { ...@@ -559,7 +559,7 @@ describe(Support.getTestDialectTeaser('Model'), function() {
it('throws an error if alias is not associated', function() { it('throws an error if alias is not associated', function() {
var self = this; var self = this;
return self.Worker.find({ include: [{ daoFactory: self.Task, as: 'Work' }] }).catch (function(err) { return self.Worker.find({ include: [{ model: self.Task, as: 'Work' }] }).catch (function(err) {
expect(err.message).to.equal('Task (Work) is not associated to Worker!'); expect(err.message).to.equal('Task (Work) is not associated to Worker!');
}); });
}); });
...@@ -567,7 +567,7 @@ describe(Support.getTestDialectTeaser('Model'), function() { ...@@ -567,7 +567,7 @@ describe(Support.getTestDialectTeaser('Model'), function() {
it('returns the associated task via worker.task', function() { it('returns the associated task via worker.task', function() {
return this.Worker.find({ return this.Worker.find({
where: { name: 'worker' }, where: { name: 'worker' },
include: [{ daoFactory: this.Task, as: 'ToDo' }] include: [{ model: this.Task, as: 'ToDo' }]
}).then(function(worker) { }).then(function(worker) {
expect(worker).to.exist; expect(worker).to.exist;
expect(worker.ToDo).to.exist; expect(worker.ToDo).to.exist;
...@@ -648,7 +648,7 @@ describe(Support.getTestDialectTeaser('Model'), function() { ...@@ -648,7 +648,7 @@ describe(Support.getTestDialectTeaser('Model'), function() {
where: { where: {
name: 'Boris' name: 'Boris'
}, },
include: [self.PhoneNumber, { daoFactory: self.Photo, as: 'Photos' }] include: [self.PhoneNumber, { model: self.Photo, as: 'Photos' }]
}).then(function(fetchedContact) { }).then(function(fetchedContact) {
expect(fetchedContact).to.exist; expect(fetchedContact).to.exist;
expect(fetchedContact.Photos.length).to.equal(1); expect(fetchedContact.Photos.length).to.equal(1);
...@@ -720,7 +720,7 @@ describe(Support.getTestDialectTeaser('Model'), function() { ...@@ -720,7 +720,7 @@ describe(Support.getTestDialectTeaser('Model'), function() {
it('throws an error if alias is not associated', function() { it('throws an error if alias is not associated', function() {
var self = this; var self = this;
return self.Worker.find({ include: [{ daoFactory: self.Task, as: 'Work' }] }).catch (function(err) { return self.Worker.find({ include: [{ model: self.Task, as: 'Work' }] }).catch (function(err) {
expect(err.message).to.equal('Task (Work) is not associated to Worker!'); expect(err.message).to.equal('Task (Work) is not associated to Worker!');
}); });
}); });
...@@ -728,7 +728,7 @@ describe(Support.getTestDialectTeaser('Model'), function() { ...@@ -728,7 +728,7 @@ describe(Support.getTestDialectTeaser('Model'), function() {
it('returns the associated task via worker.task', function() { it('returns the associated task via worker.task', function() {
return this.Worker.find({ return this.Worker.find({
where: { name: 'worker' }, where: { name: 'worker' },
include: [{ daoFactory: this.Task, as: 'ToDos' }] include: [{ model: this.Task, as: 'ToDos' }]
}).then(function(worker) { }).then(function(worker) {
expect(worker).to.exist; expect(worker).to.exist;
expect(worker.ToDos).to.exist; expect(worker.ToDos).to.exist;
......
...@@ -630,7 +630,7 @@ describe(Support.getTestDialectTeaser('Model'), function() { ...@@ -630,7 +630,7 @@ describe(Support.getTestDialectTeaser('Model'), function() {
it('throws an error if alias is not associated', function() { it('throws an error if alias is not associated', function() {
var self = this; var self = this;
return self.Worker.findAll({ include: [{ daoFactory: self.Task, as: 'Work' }] }).catch (function(err) { return self.Worker.findAll({ include: [{ model: self.Task, as: 'Work' }] }).catch (function(err) {
expect(err.message).to.equal('Task (Work) is not associated to Worker!'); expect(err.message).to.equal('Task (Work) is not associated to Worker!');
}); });
}); });
...@@ -638,7 +638,7 @@ describe(Support.getTestDialectTeaser('Model'), function() { ...@@ -638,7 +638,7 @@ describe(Support.getTestDialectTeaser('Model'), function() {
it('returns the associated task via worker.task', function() { it('returns the associated task via worker.task', function() {
return this.Worker.findAll({ return this.Worker.findAll({
where: { name: 'worker' }, where: { name: 'worker' },
include: [{ daoFactory: this.Task, as: 'ToDo' }] include: [{ model: this.Task, as: 'ToDo' }]
}).then(function(workers) { }).then(function(workers) {
expect(workers).to.exist; expect(workers).to.exist;
expect(workers[0].ToDo).to.exist; expect(workers[0].ToDo).to.exist;
...@@ -722,7 +722,7 @@ describe(Support.getTestDialectTeaser('Model'), function() { ...@@ -722,7 +722,7 @@ describe(Support.getTestDialectTeaser('Model'), function() {
it('throws an error if alias is not associated', function() { it('throws an error if alias is not associated', function() {
var self = this; var self = this;
return self.Worker.findAll({ include: [{ daoFactory: self.Task, as: 'Work' }] }).catch (function(err) { return self.Worker.findAll({ include: [{ model: self.Task, as: 'Work' }] }).catch (function(err) {
expect(err.message).to.equal('Task (Work) is not associated to Worker!'); expect(err.message).to.equal('Task (Work) is not associated to Worker!');
}); });
}); });
...@@ -730,7 +730,7 @@ describe(Support.getTestDialectTeaser('Model'), function() { ...@@ -730,7 +730,7 @@ describe(Support.getTestDialectTeaser('Model'), function() {
it('returns the associated task via worker.task', function() { it('returns the associated task via worker.task', function() {
return this.Worker.findAll({ return this.Worker.findAll({
where: { name: 'worker' }, where: { name: 'worker' },
include: [{ daoFactory: this.Task, as: 'ToDos' }] include: [{ model: this.Task, as: 'ToDos' }]
}).then(function(workers) { }).then(function(workers) {
expect(workers).to.exist; expect(workers).to.exist;
expect(workers[0].ToDos).to.exist; expect(workers[0].ToDos).to.exist;
...@@ -741,7 +741,7 @@ describe(Support.getTestDialectTeaser('Model'), function() { ...@@ -741,7 +741,7 @@ describe(Support.getTestDialectTeaser('Model'), function() {
it('returns the associated task via worker.task when daoFactory is aliased with model', function() { it('returns the associated task via worker.task when daoFactory is aliased with model', function() {
return this.Worker.findAll({ return this.Worker.findAll({
where: { name: 'worker' }, where: { name: 'worker' },
include: [{ daoFactory: this.Task, as: 'ToDos' }] include: [{ model: this.Task, as: 'ToDos' }]
}).then(function(workers) { }).then(function(workers) {
expect(workers[0].ToDos[0].title).to.equal('homework'); expect(workers[0].ToDos[0].title).to.equal('homework');
}); });
......
'use strict';
/* jshint -W030 */
/* jshint -W110 */
var chai = require('chai')
, Sequelize = require('../../../../index')
, expect = chai.expect
, Promise = Sequelize.Promise
, Support = require(__dirname + '/../../support');
describe(Support.getTestDialectTeaser('Model'), function() {
describe('scope', function () {
describe('associations', function () {
beforeEach(function () {
this.ScopeMe = this.sequelize.define('ScopeMe', {
username: Sequelize.STRING,
email: Sequelize.STRING,
access_level: Sequelize.INTEGER,
other_value: Sequelize.INTEGER,
parent_id: Sequelize.INTEGER
}, {
defaultScope: {
where: {
access_level: {
gte: 5
}
}
},
scopes: {
highValue: {
where: {
other_value: {
gte: 10
}
}
},
isTony: {
where: {
username: 'tony'
}
},
actualValue: function(value) {
return {
where: {
other_value: value
}
};
},
lowAccess: {
where: {
access_level: {
lte: 5
}
}
},
escape: {
where: {
username: "escape'd"
}
}
}
});
this.Company = this.sequelize.define('company', {
active: Sequelize.BOOLEAN
}, {
defaultScope: {
where: { active: true }
},
scopes: {
notActive: {
where: {
active: false
}
},
users: {
include: [
{ model: this.ScopeMe.unscoped(), as: 'users'}
]
},
reversed: {
order: [['id', 'DESC']]
}
}
});
this.Project = this.sequelize.define('Project');
this.Project.belongsToMany(this.Company, { through: 'CompanyProjects' });
this.Company.belongsToMany(this.Project, { through: 'CompanyProjects' });
this.ScopeMe.belongsTo(this.Company);
this.UserAssociation = this.Company.hasMany(this.ScopeMe, { as: 'users'});
return this.sequelize.sync({force: true}).bind(this).then(function() {
return Promise.all([
this.ScopeMe.create({username: 'dan', email: 'dan@sequelizejs.com', access_level: 5, other_value: 10, parent_id: 1}),
this.ScopeMe.create({username: 'tobi', email: 'tobi@fakeemail.com', access_level: 10, other_value: 11, parent_id: 2}),
this.ScopeMe.create({username: 'tony', email: 'tony@sequelizejs.com', access_level: 3, other_value: 7, parent_id: 1}),
this.ScopeMe.create({username: 'fred', email: 'fred@foobar.com', access_level: 3, other_value: 7, parent_id: 1}),
this.Company.create({active:true}),
this.Company.create({active:false}),
this.ScopeMe.create({username: 'bob', email: 'bob@foobar.com', access_level: 1, other_value: 9, parent_id: 5}),
]);
}).spread(function (u1, u2, u3, u4, c1, c2, u5) {
return Promise.all([
c1.setUsers([u1, u2, u3, u4]),
c2.setUsers([u5])
]);
});
});
describe('include', function () {
describe('target', function () {
it('should scope columns properly', function () {
// Will error with ambigous column if id is not scoped properly to `Company`.`id`
return expect(this.Company.findAll({
where: { id: 1},
include: [this.UserAssociation]
})).to.be.resolved;
});
it('should apply default scope when including an associations', function () {
return this.Company.findAll({
include: [this.UserAssociation]
}).get(0).then(function (company) {
expect(company.users).to.have.length(2);
});
});
it('should apply default scope when including a model', function () {
return this.Company.findAll({
include: [{ model: this.ScopeMe, as: 'users'}]
}).get(0).then(function (company) {
expect(company.users).to.have.length(2);
});
});
it('should be able to include a scoped model', function () {
return this.Company.findAll({
include: [{ model: this.ScopeMe.scope('isTony'), as: 'users'}]
}).get(0).then(function (company) {
expect(company.users).to.have.length(1);
expect(company.users[0].get('username')).to.equal('tony');
});
});
});
describe('source', function () {
it('should include model with scope', function () {
return this.Company.scope('users').findAll({ where: { id: 1 } }).get(0).then(function (company) {
expect(company.users).to.have.length(4);
});
});
it('should be able to override scoped include', function () {
return this.Company.scope('users').findAll({
include: [
{ association: this.UserAssociation, where: { username: 'dan'} }
]
}).get(0).then(function (company) {
expect(company.users).to.have.length(1);
expect(company.users[0].get('username')).to.equal('dan');
});
});
it('should be able to append to includes', function () {
// ? or should it
});
});
});
describe('get', function () {
beforeEach(function () {
return Promise.all([
this.Project.create(),
this.Company.unscoped().findAll()
]).spread(function (p, companies) {
return p.setCompanies(companies);
});
});
describe('it should be able to unscope', function () {
it('hasMany', function () {
return this.Company.find(1).then(function (company) {
return company.getUsers({ scope: false});
}).then(function (users) {
expect(users).to.have.length(4);
});
});
it('hasOne', function () {
});
it('belongsTo', function () {
return this.ScopeMe.unscoped().find({ where: { username: 'bob' }}).then(function (user) {
return user.getCompany({ scope: false });
}).then(function (company) {
expect(company).to.be.ok;
});
});
it('belongsToMany', function () {
return this.Project.findAll().get(0).then(function (p) {
return p.getCompanies({ scope: false});
}).then(function (companies) {
expect(companies).to.have.length(2);
});
});
});
describe('it should apply default scope', function () {
it('hasMany', function () {
return this.Company.find(1).then(function (company) {
return company.getUsers();
}).then(function (users) {
expect(users).to.have.length(2);
});
});
it('hasOne', function () {
});
it('hasMany double', function () {
// Do we need to - it's deprecated?
})
it('belongsTo', function () {
return this.ScopeMe.unscoped().find({ where: { username: 'bob' }}).then(function (user) {
return user.getCompany();
}).then(function (company) {
expect(company).not.to.be.ok;
});
});
it('belongsToMany', function () {
return this.Project.findAll().get(0).then(function (p) {
return p.getCompanies();
}).then(function (companies) {
expect(companies).to.have.length(1);
expect(companies[0].get('active')).to.be.ok;
});
});
});
describe('it should be able to apply another scope', function () {
it('hasMany', function () {
return this.Company.find(1).then(function (company) {
return company.getUsers({ scope: 'isTony'});
}).then(function (users) {
expect(users).to.have.length(1);
expect(users[0].get('username')).to.equal('tony');
});
});
it('hasOne', function () {
});
it('belongsTo', function () {
return this.ScopeMe.unscoped().find({ where: { username: 'bob' }}).then(function (user) {
return user.getCompany({ scope: 'notActive' });
}).then(function (company) {
expect(company).to.be.ok;
});
});
it('belongsToMany', function () {
return this.Project.findAll().get(0).then(function (p) {
return p.getCompanies({ scope: 'reversed' });
}).then(function (companies) {
expect(companies).to.have.length(2);
expect(companies[0].id).to.equal(2);
expect(companies[1].id).to.equal(1);
});
});
});
});
});
});
});
'use strict';
/* jshint -W030 */
/* jshint -W110 */
var chai = require('chai')
, Sequelize = require('../../../../index')
, expect = chai.expect
, Support = require(__dirname + '/../../support');
describe(Support.getTestDialectTeaser('Model'), function() {
describe('scope', function () {
describe('destroy', function () {
beforeEach(function () {
this.ScopeMe = this.sequelize.define('ScopeMe', {
username: Sequelize.STRING,
email: Sequelize.STRING,
access_level: Sequelize.INTEGER,
other_value: Sequelize.INTEGER
}, {
defaultScope: {
where: {
access_level: {
gte: 5
}
}
},
scopes: {
lowAccess: {
where: {
access_level: {
lte: 5
}
}
}
}
});
return this.sequelize.sync({force: true}).then(function() {
var records = [
{username: 'tony', email: 'tony@sequelizejs.com', access_level: 3, other_value: 7},
{username: 'tobi', email: 'tobi@fakeemail.com', access_level: 10, other_value: 11},
{username: 'dan', email: 'dan@sequelizejs.com', access_level: 5, other_value: 10},
{username: 'fred', email: 'fred@foobar.com', access_level: 3, other_value: 7}
];
return this.ScopeMe.bulkCreate(records);
}.bind(this));
});
it('should apply defaultScope', function () {
return this.ScopeMe.destroy({ where: {}}).bind(this).then(function () {
return this.ScopeMe.unscoped().findAll();
}).then(function (users) {
expect(users).to.have.length(2);
expect(users[0].get('username')).to.equal('tony');
expect(users[1].get('username')).to.equal('fred');
});
});
it('should be able to override default scope', function () {
return this.ScopeMe.destroy({ where: { access_level: { lt: 5 }}}).bind(this).then(function () {
return this.ScopeMe.unscoped().findAll();
}).then(function (users) {
expect(users).to.have.length(2);
expect(users[0].get('username')).to.equal('tobi');
expect(users[1].get('username')).to.equal('dan');
});
});
it('should be able to unscope destroy', function () {
return this.ScopeMe.unscoped().destroy({ where: {}}).bind(this).then(function() {
return expect(this.ScopeMe.unscoped().findAll()).to.eventually.have.length(0);
});
});
it('should be able to apply other scopes', function () {
return this.ScopeMe.scope('lowAccess').destroy({ where: {}}).bind(this).then(function () {
return this.ScopeMe.unscoped().findAll();
}).then(function (users) {
expect(users).to.have.length(1);
expect(users[0].get('username')).to.equal('tobi');
});
});
it('should be able to merge scopes with where', function () {
return this.ScopeMe.scope('lowAccess').destroy({ where: { username: 'dan'}}).bind(this).then(function () {
return this.ScopeMe.unscoped().findAll();
}).then(function (users) {
expect(users).to.have.length(3);
expect(users[0].get('username')).to.equal('tony');
expect(users[1].get('username')).to.equal('tobi');
expect(users[2].get('username')).to.equal('fred');
});
});
});
});
});
'use strict';
/* jshint -W030 */
/* jshint -W110 */
var chai = require('chai')
, _ = require('lodash')
, Sequelize = require('../../../../index')
, expect = chai.expect
, Support = require(__dirname + '/../../support');
describe(Support.getTestDialectTeaser('Model'), function() {
describe('scope', function () {
describe('update', function () {
beforeEach(function () {
this.ScopeMe = this.sequelize.define('ScopeMe', {
username: Sequelize.STRING,
email: Sequelize.STRING,
access_level: Sequelize.INTEGER,
other_value: Sequelize.INTEGER
}, {
defaultScope: {
where: {
access_level: {
gte: 5
}
}
},
scopes: {
lowAccess: {
where: {
access_level: {
lte: 5
}
}
}
}
});
return this.sequelize.sync({force: true}).then(function() {
var records = [
{username: 'tony', email: 'tony@sequelizejs.com', access_level: 3, other_value: 7},
{username: 'tobi', email: 'tobi@fakeemail.com', access_level: 10, other_value: 11},
{username: 'dan', email: 'dan@sequelizejs.com', access_level: 5, other_value: 10},
{username: 'fred', email: 'fred@foobar.com', access_level: 3, other_value: 7}
];
return this.ScopeMe.bulkCreate(records);
}.bind(this));
});
it('should apply defaultScope', function () {
return this.ScopeMe.update({ username: 'ruben' }, { where: {}}).bind(this).then(function () {
return this.ScopeMe.unscoped().findAll({ where: { username: 'ruben' }});
}).then(function (users) {
expect(users).to.have.length(2);
expect(users[0].get('email')).to.equal('tobi@fakeemail.com');
expect(users[1].get('email')).to.equal('dan@sequelizejs.com');
});
});
it('should be able to override default scope', function () {
return this.ScopeMe.update({ username: 'ruben' }, { where: { access_level: { lt: 5 }}}).bind(this).then(function () {
return this.ScopeMe.unscoped().findAll({ where: { username: 'ruben' }});
}).then(function (users) {
expect(users).to.have.length(2);
expect(users[0].get('email')).to.equal('tony@sequelizejs.com');
expect(users[1].get('email')).to.equal('fred@foobar.com');
});
});
it('should be able to unscope destroy', function () {
return this.ScopeMe.unscoped().update({ username: 'ruben' }, { where: {}}).bind(this).then(function() {
return this.ScopeMe.unscoped().findAll();
}).then(function (rubens) {
expect(_.every(rubens, function (r) {
return r.get('username') === 'ruben';
})).to.be.true;
});
});
it('should be able to apply other scopes', function () {
return this.ScopeMe.scope('lowAccess').update({ username: 'ruben' }, { where: {}}).bind(this).then(function () {
return this.ScopeMe.unscoped().findAll({ where: { username: { $ne: 'ruben' }}});
}).then(function (users) {
expect(users).to.have.length(1);
expect(users[0].get('email')).to.equal('tobi@fakeemail.com');
});
});
it('should be able to merge scopes with where', function () {
return this.ScopeMe.scope('lowAccess').update({ username: 'ruben' }, { where: { username: 'dan'}}).bind(this).then(function () {
return this.ScopeMe.unscoped().findAll({ where: { username: 'ruben' }});
}).then(function (users) {
expect(users).to.have.length(1);
expect(users[0].get('email')).to.equal('dan@sequelizejs.com');
});
});
});
});
});
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!