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

Commit 798b002b by Mick Hansen

Support through models with attributes in find/findAll includes

1 parent 86ba3b36
...@@ -1304,6 +1304,9 @@ module.exports = (function() { ...@@ -1304,6 +1304,9 @@ module.exports = (function() {
include.attributes = Object.keys(include.daoFactory.attributes) include.attributes = Object.keys(include.daoFactory.attributes)
} }
// pseudo include just needed the attribute logic, return
if (include._pseudo) return include
if (include.hasOwnProperty('daoFactory') && (include.hasOwnProperty('as'))) { if (include.hasOwnProperty('daoFactory') && (include.hasOwnProperty('as'))) {
var usesAlias = (include.as !== include.daoFactory.tableName) var usesAlias = (include.as !== include.daoFactory.tableName)
, association = (usesAlias ? parent.getAssociationByAlias(include.as) : parent.getAssociation(include.daoFactory)) , association = (usesAlias ? parent.getAssociationByAlias(include.as) : parent.getAssociation(include.daoFactory))
...@@ -1313,10 +1316,28 @@ module.exports = (function() { ...@@ -1313,10 +1316,28 @@ module.exports = (function() {
include.as = Utils.singularize(include.daoFactory.tableName, include.daoFactory.options.language) include.as = Utils.singularize(include.daoFactory.tableName, include.daoFactory.options.language)
} }
// check if the current daoFactory is actually associated with the passed daoFactory // check if the current daoFactory is actually associated with the passed daoFactory - or it's a pseudo include
if (!!association && (!association.options.as || (association.options.as === include.as))) { if (association && (!association.options.as || (association.options.as === include.as))) {
include.association = association include.association = association
// If through, we create a pseudo child include, to ease our parsing later on
if (Object(include.association.through) === include.association.through) {
if (!include.include) include.include = []
var through = include.association.through
include.through = {
daoFactory: through,
as: Utils.singularize(through.tableName, through.options.language),
association: {
isSingleAssociation: true
},
_pseudo: true
}
include.include.push(include.through)
}
// Validate child includes
if (include.hasOwnProperty('include')) { if (include.hasOwnProperty('include')) {
validateIncludedElements(include) validateIncludedElements(include)
} }
......
...@@ -159,6 +159,7 @@ module.exports = (function() { ...@@ -159,6 +159,7 @@ module.exports = (function() {
this._customSetters[key].call(this, value, key) this._customSetters[key].call(this, value, key)
} else { } else {
// Check if we have included models, and if this key matches the include model names/aliases // Check if we have included models, and if this key matches the include model names/aliases
if (this.options && this.options.include && this.options.includeNames.indexOf(key) !== -1) { if (this.options && this.options.include && this.options.includeNames.indexOf(key) !== -1) {
// Pass it on to the include handler // Pass it on to the include handler
this._setInclude(key, value, options) this._setInclude(key, value, options)
...@@ -238,7 +239,7 @@ module.exports = (function() { ...@@ -238,7 +239,7 @@ module.exports = (function() {
includeValidated: true, includeValidated: true,
raw: options.raw raw: options.raw
}) })
, isEmpty = !Utils.firstValueOfHash(daoInstance.identifiers) , isEmpty = !Utils.firstValueOfHash(daoInstance.identifiers)
if (association.isSingleAssociation) { if (association.isSingleAssociation) {
accessor = Utils.singularize(accessor, self.sequelize.language) accessor = Utils.singularize(accessor, self.sequelize.language)
......
...@@ -489,45 +489,56 @@ module.exports = (function() { ...@@ -489,45 +489,56 @@ module.exports = (function() {
, as = include.as , as = include.as
, joinQueryItem = "" , joinQueryItem = ""
, attributes , attributes
, association = include.association
, through = include.through
if (tableName !== parentTable) as = parentTable+'.'+include.as if (tableName !== parentTable) as = parentTable+'.'+include.as
attributes = include.attributes.map(function(attr) { attributes = include.attributes.map(function(attr) {
return self.quoteIdentifier(as) + "." + self.quoteIdentifier(attr) + " AS " + self.quoteIdentifier(as + "." + attr) return self.quoteIdentifier(as) + "." + self.quoteIdentifier(attr) + " AS " + self.quoteIdentifier(as + "." + attr)
}) })
optAttributes = optAttributes.concat(attributes) optAttributes = optAttributes.concat(attributes)
if (Object(include.association.through) === include.association.through) { if (through) {
var primaryKeysSource = Object.keys(include.association.source.primaryKeys) var throughTable = through.daoFactory.tableName
, throughAs = as + "." + through.as
, throughAttributes = through.attributes.map(function(attr) {
return self.quoteIdentifier(throughAs) + "." + self.quoteIdentifier(attr) + " AS " + self.quoteIdentifier(throughAs + "." + attr)
})
optAttributes = optAttributes.concat(throughAttributes)
var primaryKeysSource = Object.keys(association.source.primaryKeys)
, tableSource = parentTable , tableSource = parentTable
, identSource = include.association.identifier , identSource = association.identifier
, attrSource = ((!include.association.source.hasPrimaryKeys || primaryKeysSource.length !== 1) ? 'id' : primaryKeysSource[0]) , attrSource = ((!association.source.hasPrimaryKeys || primaryKeysSource.length !== 1) ? 'id' : primaryKeysSource[0])
var primaryKeysTarget = Object.keys(include.association.target.primaryKeys) var primaryKeysTarget = Object.keys(association.target.primaryKeys)
, tableTarget = as , tableTarget = as
, identTarget = include.association.foreignIdentifier , identTarget = association.foreignIdentifier
, attrTarget = ((!include.association.target.hasPrimaryKeys || primaryKeysTarget.length !== 1) ? 'id' : primaryKeysTarget[0]) , attrTarget = ((!include.association.target.hasPrimaryKeys || primaryKeysTarget.length !== 1) ? 'id' : primaryKeysTarget[0])
var tableJunction = include.association.through.tableName joinQueryItem += " LEFT OUTER JOIN " + self.quoteIdentifier(throughTable) + " AS " + self.quoteIdentifier(throughAs) + " ON "
joinQueryItem += " LEFT OUTER JOIN " + self.quoteIdentifier(tableJunction) + " ON "
joinQueryItem += self.quoteIdentifier(tableSource) + "." + self.quoteIdentifier(attrSource) + " = " joinQueryItem += self.quoteIdentifier(tableSource) + "." + self.quoteIdentifier(attrSource) + " = "
joinQueryItem += self.quoteIdentifier(tableJunction) + "." + self.quoteIdentifier(identSource) joinQueryItem += self.quoteIdentifier(throughAs) + "." + self.quoteIdentifier(identSource)
joinQueryItem += " LEFT OUTER JOIN " + self.quoteIdentifier(table) + " AS " + self.quoteIdentifier(as) + " ON " joinQueryItem += " LEFT OUTER JOIN " + self.quoteIdentifier(table) + " AS " + self.quoteIdentifier(as) + " ON "
joinQueryItem += self.quoteIdentifier(tableTarget) + "." + self.quoteIdentifier(attrTarget) + " = " joinQueryItem += self.quoteIdentifier(tableTarget) + "." + self.quoteIdentifier(attrTarget) + " = "
joinQueryItem += self.quoteIdentifier(tableJunction) + "." + self.quoteIdentifier(identTarget) joinQueryItem += self.quoteIdentifier(throughAs) + "." + self.quoteIdentifier(identTarget)
} else { } else {
var primaryKeysLeft = ((include.association.associationType === 'BelongsTo') ? Object.keys(include.association.target.primaryKeys) : Object.keys(include.association.source.primaryKeys)) var primaryKeysLeft = ((association.associationType === 'BelongsTo') ? Object.keys(association.target.primaryKeys) : Object.keys(include.association.source.primaryKeys))
, tableLeft = ((include.association.associationType === 'BelongsTo') ? as : parentTable) , tableLeft = ((association.associationType === 'BelongsTo') ? as : parentTable)
, attrLeft = ((primaryKeysLeft.length !== 1) ? 'id' : primaryKeysLeft[0]) , attrLeft = ((primaryKeysLeft.length !== 1) ? 'id' : primaryKeysLeft[0])
, tableRight = ((include.association.associationType === 'BelongsTo') ? parentTable : as) , tableRight = ((association.associationType === 'BelongsTo') ? parentTable : as)
, attrRight = include.association.identifier , attrRight = association.identifier
joinQueryItem += " LEFT OUTER JOIN " + self.quoteIdentifier(table) + " AS " + self.quoteIdentifier(as) + " ON " + self.quoteIdentifier(tableLeft) + "." + self.quoteIdentifier(attrLeft) + " = " + self.quoteIdentifier(tableRight) + "." + self.quoteIdentifier(attrRight) joinQueryItem += " LEFT OUTER JOIN " + self.quoteIdentifier(table) + " AS " + self.quoteIdentifier(as) + " ON " + self.quoteIdentifier(tableLeft) + "." + self.quoteIdentifier(attrLeft) + " = " + self.quoteIdentifier(tableRight) + "." + self.quoteIdentifier(attrRight)
} }
if (include.include) { if (include.include) {
include.include.forEach(function(childInclude) { include.include.forEach(function(childInclude) {
if (childInclude._pseudo) return
joinQueryItem += generateJoinQuery(childInclude, as) joinQueryItem += generateJoinQuery(childInclude, as)
}.bind(this)) }.bind(this))
} }
......
...@@ -322,14 +322,19 @@ module.exports = (function() { ...@@ -322,14 +322,19 @@ module.exports = (function() {
, existingResult , existingResult
, calleeData , calleeData
, child , child
, calleeDataIgnore = ['__children']
if (options.includeNames) {
calleeDataIgnore = calleeDataIgnore.concat(options.includeNames)
}
data.forEach(function (row) { data.forEach(function (row) {
row = Dot.transform(row) row = Dot.transform(row)
calleeData = _.omit(row, options.includeNames) calleeData = _.omit(row, calleeDataIgnore)
existingResult = _.find(results, function (result) { existingResult = _.find(results, function (result) {
if (options.includeNames) { if (calleeDataIgnore) {
return Utils._.isEqual(_.omit(result, options.includeNames.concat(['__children'])), calleeData) return Utils._.isEqual(_.omit(result, calleeDataIgnore), calleeData)
} }
return Utils._.isEqual(result, calleeData) return Utils._.isEqual(result, calleeData)
}) })
...@@ -340,7 +345,9 @@ module.exports = (function() { ...@@ -340,7 +345,9 @@ module.exports = (function() {
for (var attrName in row) { for (var attrName in row) {
if (row.hasOwnProperty(attrName)) { if (row.hasOwnProperty(attrName)) {
// Child if object, and is an child include
child = Object(row[attrName]) === row[attrName] && options.includeMap && options.includeMap[attrName] child = Object(row[attrName]) === row[attrName] && options.includeMap && options.includeMap[attrName]
if (child) { if (child) {
if (!existingResult.__children) existingResult.__children = {} if (!existingResult.__children) existingResult.__children = {}
if (!existingResult.__children[attrName]) existingResult.__children[attrName] = [] if (!existingResult.__children[attrName]) existingResult.__children[attrName] = []
...@@ -350,10 +357,10 @@ module.exports = (function() { ...@@ -350,10 +357,10 @@ module.exports = (function() {
} }
} }
}) })
results.forEach(function (result) { results.forEach(function (result) {
_.each(result.__children, function (children, key) { _.each(result.__children, function (children, key) {
result[key] = groupJoinData(children, options.includeMap[key]) result[key] = groupJoinData(children, (options.includeMap && options.includeMap[key]))
}) })
delete result.__children delete result.__children
}) })
......
...@@ -368,8 +368,6 @@ describe(Support.getTestDialectTeaser("Include"), function () { ...@@ -368,8 +368,6 @@ describe(Support.getTestDialectTeaser("Include"), function () {
{name: 'Member', canInvite: 1, canRemove: 0} {name: 'Member', canInvite: 1, canRemove: 0}
]).done(function () { ]).done(function () {
Rank.findAll().done(callback) Rank.findAll().done(callback)
}).on('sql', function (sql) {
console.log(sql)
}) })
}, },
memberships: ['user', 'groups', 'ranks', function (callback, results) { memberships: ['user', 'groups', 'ranks', function (callback, results) {
...@@ -891,5 +889,82 @@ describe(Support.getTestDialectTeaser("Include"), function () { ...@@ -891,5 +889,82 @@ describe(Support.getTestDialectTeaser("Include"), function () {
}) })
}) })
}) })
it('should support through models with attributes', function (done) {
var Product = this.sequelize.define('Product', {
title: DataTypes.STRING
})
, Tag = this.sequelize.define('Tag', {
name: DataTypes.STRING
})
, ProductTag = this.sequelize.define('ProductTag', {
priority: DataTypes.INTEGER
})
Product.hasMany(Tag, {through: ProductTag})
Tag.hasMany(Product, {through: ProductTag})
this.sequelize.sync({force: true}).done(function () {
async.auto({
products: function (callback) {
Product.bulkCreate([
{title: 'Chair'},
{title: 'Desk'},
{title: 'Dress'}
]).done(function () {
Product.findAll().done(callback)
})
},
tags: function(callback) {
Tag.bulkCreate([
{name: 'A'},
{name: 'B'},
{name: 'C'}
]).done(function () {
Tag.findAll().done(callback)
})
},
productTags: ['products', 'tags', function (callback, results) {
var chainer = new Sequelize.Utils.QueryChainer()
chainer.add(results.products[0].addTag(results.tags[0], {priority: 1}))
chainer.add(results.products[0].addTag(results.tags[1], {priority: 2}))
chainer.add(results.products[1].addTag(results.tags[1], {priority: 1}))
chainer.add(results.products[2].addTag(results.tags[0], {priority: 3}))
chainer.add(results.products[2].addTag(results.tags[1], {priority: 1}))
chainer.add(results.products[2].addTag(results.tags[2], {priority: 2}))
chainer.run().done(callback)
}]
}, function (err, results) {
expect(err).not.to.be.ok
Product.findAll({
include: [
{model: Tag}
],
order: [
['id', 'ASC'],
['Tags.id', 'ASC']
]
}).done(function (err, products) {
expect(err).not.to.be.ok
expect(products[0].tags[0].productTag.priority).to.equal(1)
expect(products[0].tags[1].productTag.priority).to.equal(2)
expect(products[1].tags[0].productTag.priority).to.equal(1)
expect(products[2].tags[0].productTag.priority).to.equal(3)
expect(products[2].tags[1].productTag.priority).to.equal(1)
expect(products[2].tags[2].productTag.priority).to.equal(2)
done()
})
})
})
})
}) })
}) })
\ No newline at end of file
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!