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

Commit 825eb75f by Mick Hansen

added: preliminary support for include.on, include.or and .$ where query support

1 parent b2a31525
# Next # Next
- [ADDED] Preliminary support for `include.on`.
- [FIXED] Partial rollback of datatype validations by hiding it behind the `typeValidation` flag. - [FIXED] Partial rollback of datatype validations by hiding it behind the `typeValidation` flag.
- [FIXED] Don't try to select the primary key for models without primary key [#4607](https://github.com/sequelize/sequelize/issues/4607) - [FIXED] Don't try to select the primary key for models without primary key [#4607](https://github.com/sequelize/sequelize/issues/4607)
- [FIXED] Apply `attributes` when including a scoped model. [#4625](https://github.com/sequelize/sequelize/issues/4625) - [FIXED] Apply `attributes` when including a scoped model. [#4625](https://github.com/sequelize/sequelize/issues/4625)
......
...@@ -113,11 +113,11 @@ Post.update({ ...@@ -113,11 +113,11 @@ Post.update({
```js ```js
$and: {a: 5} // AND (a = 5) $and: {a: 5} // AND (a = 5)
$or: [{a: 5}, {a: 6}] // (a = 5 OR a = 6) $or: [{a: 5}, {a: 6}] // (a = 5 OR a = 6)
$gt: 6, // id > 6 $gt: 6, // > 6
$gte: 6, // id >= 6 $gte: 6, // >= 6
$lt: 10, // id < 10 $lt: 10, // < 10
$lte: 10, // id <= 10 $lte: 10, // <= 10
$ne: 20, // id != 20 $ne: 20, // != 20
$between: [6, 10], // BETWEEN 6 AND 10 $between: [6, 10], // BETWEEN 6 AND 10
$notBetween: [11, 15], // NOT BETWEEN 11 AND 15 $notBetween: [11, 15], // NOT BETWEEN 11 AND 15
$in: [1, 2], // IN [1, 2] $in: [1, 2], // IN [1, 2]
...@@ -132,6 +132,8 @@ $overlap: [1, 2] // && [1, 2] (PG array overlap operator) ...@@ -132,6 +132,8 @@ $overlap: [1, 2] // && [1, 2] (PG array overlap operator)
$contains: [1, 2] // @> [1, 2] (PG array contains operator) $contains: [1, 2] // @> [1, 2] (PG array contains operator)
$contained: [1, 2] // <@ [1, 2] (PG array contained by operator) $contained: [1, 2] // <@ [1, 2] (PG array contained by operator)
$any: [2,3] // ANY ARRAY[2, 3]::INTEGER (PG only) $any: [2,3] // ANY ARRAY[2, 3]::INTEGER (PG only)
$eq: '$user.organization_id$' // = "user"."organization_id", with dialect specific column identifiers, PG in this example
``` ```
### Combinations ### Combinations
......
...@@ -1621,6 +1621,12 @@ var QueryGenerator = { ...@@ -1621,6 +1621,12 @@ var QueryGenerator = {
joinOn += ' = ' + this.quoteIdentifier(asRight) + '.' + this.quoteIdentifier(fieldRight); joinOn += ' = ' + this.quoteIdentifier(asRight) + '.' + this.quoteIdentifier(fieldRight);
if (include.on) {
joinOn = this.whereItemsQuery(include.on, {
prefix: this.sequelize.literal(this.quoteIdentifier(asRight)),
model: include.model
});
}
if (include.where) { if (include.where) {
joinWhere = this.whereItemsQuery(include.where, { joinWhere = this.whereItemsQuery(include.where, {
...@@ -1628,7 +1634,11 @@ var QueryGenerator = { ...@@ -1628,7 +1634,11 @@ var QueryGenerator = {
model: include.model model: include.model
}); });
if (joinWhere) { if (joinWhere) {
joinOn += ' AND ' + joinWhere; if (include.or) {
joinOn += ' OR ' + joinWhere;
} else {
joinOn += ' AND ' + joinWhere;
}
} }
} }
...@@ -2171,17 +2181,25 @@ var QueryGenerator = { ...@@ -2171,17 +2181,25 @@ var QueryGenerator = {
comparator = 'IS NOT'; comparator = 'IS NOT';
} }
value = this.escape(value, field); if (Utils.isColString(value)) {
value = value.substr(1, value.length - 2).split('.').map(this.quoteIdentifier.bind(this)).join('.');
} else {
value = this.escape(value, field);
}
} }
if (key) { if (key) {
var prefix = true;
if (key._isSequelizeMethod) { if (key._isSequelizeMethod) {
key = this.handleSequelizeMethod(key); key = this.handleSequelizeMethod(key);
} else if (Utils.isColString(key)) {
key = key.substr(1, key.length - 2).split('.').map(this.quoteIdentifier.bind(this)).join('.');
prefix = false;
} else { } else {
key = this.quoteIdentifier(key); key = this.quoteIdentifier(key);
} }
if (options.prefix) { if (options.prefix && prefix) {
if (options.prefix instanceof Utils.literal) { if (options.prefix instanceof Utils.literal) {
key = [this.handleSequelizeMethod(options.prefix), key].join('.'); key = [this.handleSequelizeMethod(options.prefix), key].join('.');
} else { } else {
......
...@@ -1300,6 +1300,8 @@ Model.prototype.all = function(options) { ...@@ -1300,6 +1300,8 @@ Model.prototype.all = function(options) {
* @param {String} [options.include[].as] The alias of the relation, in case the model you want to eagerly load is aliassed. For `hasOne` / `belongsTo`, this should be the singular name, and for `hasMany`, it should be the plural * @param {String} [options.include[].as] The alias of the relation, in case the model you want to eagerly load is aliassed. For `hasOne` / `belongsTo`, this should be the singular name, and for `hasMany`, it should be the plural
* @param {Association} [options.include[].association] The association you want to eagerly load. (This can be used instead of providing a model/as pair) * @param {Association} [options.include[].association] The association you want to eagerly load. (This can be used instead of providing a model/as pair)
* @param {Object} [options.include[].where] Where clauses to apply to the child models. Note that this converts the eager load to an inner join, unless you explicitly set `required: false` * @param {Object} [options.include[].where] Where clauses to apply to the child models. Note that this converts the eager load to an inner join, unless you explicitly set `required: false`
* @param {Boolean} [options.include[].or=false] Whether to bind the ON and WHERE clause together by OR instead of AND.
* @param {Object} [options.include[].on] Supply your own ON condition for the join.
* @param {Array<String>} [options.include[].attributes] A list of attributes to select from the child model * @param {Array<String>} [options.include[].attributes] A list of attributes to select from the child model
* @param {Boolean} [options.include[].required] If true, converts to an inner join, which means that the parent model will only be loaded if it has any matching children. True if `include.where` is set, false otherwise. * @param {Boolean} [options.include[].required] If true, converts to an inner join, which means that the parent model will only be loaded if it has any matching children. True if `include.where` is set, false otherwise.
* @param {Boolean} [options.include[].separate] If true, runs a separate query to fetch the associated instances, only supported for hasMany associations * @param {Boolean} [options.include[].separate] If true, runs a separate query to fetch the associated instances, only supported for hasMany associations
......
...@@ -125,6 +125,7 @@ var Utils = module.exports = { ...@@ -125,6 +125,7 @@ var Utils = module.exports = {
if (attributes) { if (attributes) {
for (attribute in attributes) { for (attribute in attributes) {
rawAttribute = Model.rawAttributes[attribute]; rawAttribute = Model.rawAttributes[attribute];
if (rawAttribute && rawAttribute.field !== rawAttribute.fieldName) { if (rawAttribute && rawAttribute.field !== rawAttribute.fieldName) {
attributes[rawAttribute.field] = attributes[attribute]; attributes[rawAttribute.field] = attributes[attribute];
delete attributes[attribute]; delete attributes[attribute];
...@@ -168,6 +169,11 @@ var Utils = module.exports = { ...@@ -168,6 +169,11 @@ var Utils = module.exports = {
return values; return values;
}, },
isColString: function(value) {
return typeof value === 'string' && value.substr(0, 1) === '$' && value.substr(value.length - 1, 1) === '$';
},
argsArePrimaryKeys: function(args, primaryKeys) { argsArePrimaryKeys: function(args, primaryKeys) {
var result = (args.length === Object.keys(primaryKeys).length); var result = (args.length === Object.keys(primaryKeys).length);
if (result) { if (result) {
......
...@@ -53,6 +53,9 @@ suite(Support.getTestDialectTeaser('SQL'), function() { ...@@ -53,6 +53,9 @@ suite(Support.getTestDialectTeaser('SQL'), function() {
ownerId: { ownerId: {
type: Sequelize.INTEGER, type: Sequelize.INTEGER,
field: 'owner_id' field: 'owner_id'
},
public: {
type: Sequelize.BOOLEAN
} }
}, { }, {
tableName: 'company' tableName: 'company'
...@@ -88,6 +91,19 @@ suite(Support.getTestDialectTeaser('SQL'), function() { ...@@ -88,6 +91,19 @@ suite(Support.getTestDialectTeaser('SQL'), function() {
testsql({ testsql({
model: User, model: User,
subQuery: false,
include: Sequelize.Model.$validateIncludedElements({
model: User,
include: [
{association: User.Company, where: {public: true}, or: true}
]
}).include[0]
}, {
default: "INNER JOIN [company] AS [Company] ON [User].[company_id] = [Company].[id] OR [Company].[public] = true"
});
testsql({
model: User,
subQuery: true, subQuery: true,
include: Sequelize.Model.$validateIncludedElements({ include: Sequelize.Model.$validateIncludedElements({
limit: 3, limit: 3,
...@@ -243,5 +259,36 @@ suite(Support.getTestDialectTeaser('SQL'), function() { ...@@ -243,5 +259,36 @@ suite(Support.getTestDialectTeaser('SQL'), function() {
// The primary key of the main model will be aliased because it's coming from a subquery that the :M join is not a part of // The primary key of the main model will be aliased because it's coming from a subquery that the :M join is not a part of
default: "LEFT OUTER JOIN [task] AS [Tasks] ON [User].[id] = [Tasks].[user_id]" default: "LEFT OUTER JOIN [task] AS [Tasks] ON [User].[id] = [Tasks].[user_id]"
}); });
testsql({
model: User,
subQuery: false,
include: Sequelize.Model.$validateIncludedElements({
model: User,
include: [
{association: User.Tasks, on: {
$or: [
{'$User.id_user$': '$Tasks.user_id$'},
{'$Tasks.user_id$': 2}
]
}}
]
}).include[0]
}, {
default: "LEFT OUTER JOIN [task] AS [Tasks] ON ([User].[id_user] = [Tasks].[user_id] OR [Tasks].[user_id] = 2)"
});
testsql({
model: User,
subQuery: false,
include: Sequelize.Model.$validateIncludedElements({
model: User,
include: [
{association: User.Tasks, on: {'user_id': '$User.alternative_id$'}}
]
}).include[0]
}, {
default: "LEFT OUTER JOIN [task] AS [Tasks] ON [Tasks].[user_id] = [User].[alternative_id]"
});
}); });
}); });
\ No newline at end of file
...@@ -306,12 +306,35 @@ suite(Support.getTestDialectTeaser('SQL'), function() { ...@@ -306,12 +306,35 @@ suite(Support.getTestDialectTeaser('SQL'), function() {
}); });
}); });
suite('$col', function () {
testsql('userId', '$user.id$', {
default: '[userId] = [user].[id]'
});
testsql('$or', [
{'ownerId': '$user.id$'},
{'ownerId': '$organization.id$'}
], {
default: '([ownerId] = [user].[id] OR [ownerId] = [organization].[id])'
});
testsql('$organization.id$', '$user.organizationId$', {
default: '[organization].[id] = [user].[organizationId]'
});
});
suite('$gt', function () { suite('$gt', function () {
testsql('rank', { testsql('rank', {
$gt: 2 $gt: 2
}, { }, {
default: '[rank] > 2' default: '[rank] > 2'
}); });
testsql('created_at', {
$lt: '$updated_at$'
}, {
default: '[created_at] < [updated_at]'
});
}); });
suite('$raw', function () { suite('$raw', function () {
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!