Skip to content
Toggle navigation
Projects
Groups
Snippets
Help
public
/
sequelize
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
0
Merge Requests
0
Pipelines
Wiki
Snippets
Settings
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
不要怂,就是干,撸起袖子干!
Commit 3afdc41e
authored
Dec 27, 2013
by
Mick Hansen
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Lots of refactoring - primarily that DAO now handles the construction of included models
1 parent
d987f037
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
152 additions
and
210 deletions
lib/dao-factory.js
lib/dao.js
lib/dialects/abstract/query.js
lib/emitters/custom-event-emitter.js
lib/query-interface.js
test/dao-factory/find.test.js
test/dao.test.js
lib/dao-factory.js
View file @
3afdc41
...
...
@@ -168,7 +168,10 @@ module.exports = (function() {
})
this
.
DAO
.
prototype
.
__factory
=
this
this
.
DAO
.
prototype
.
daoFactory
=
this
this
.
DAO
.
prototype
.
Model
=
this
this
.
DAO
.
prototype
.
hasDefaultValues
=
!
Utils
.
_
.
isEmpty
(
this
.
DAO
.
prototype
.
defaultValues
)
this
.
DAO
.
prototype
.
daoFactoryName
=
this
.
name
return
this
}
...
...
@@ -555,13 +558,22 @@ module.exports = (function() {
DAOFactory
.
prototype
.
build
=
function
(
values
,
options
)
{
options
=
options
||
{
isNewRecord
:
true
,
isDirty
:
true
}
var
self
=
this
,
instance
=
new
this
.
DAO
(
values
,
this
.
options
,
options
.
isNewRecord
)
if
(
options
.
hasOwnProperty
(
'include'
)
&&
(
!
options
.
includeValidated
||
!
options
.
includeNames
))
{
options
.
includeNames
=
[]
options
.
include
=
options
.
include
.
map
(
function
(
include
)
{
options
.
includeNames
.
push
(
include
.
as
)
return
validateIncludedElement
.
call
(
this
,
include
)
}.
bind
(
this
))
}
instance
.
isNewRecord
=
options
.
isNewRecord
instance
.
daoFactoryName
=
this
.
name
instance
.
daoFactory
=
this
instance
.
isDirty
=
options
.
isDirty
if
(
options
.
includeNames
)
{
options
.
includeNames
=
options
.
includeNames
.
concat
(
options
.
includeNames
.
map
(
function
(
key
)
{
return
key
.
slice
(
0
,
1
).
toLowerCase
()
+
key
.
slice
(
1
)
}))
}
var
self
=
this
,
instance
=
new
this
.
DAO
(
values
,
options
)
return
instance
}
...
...
lib/dao.js
View file @
3afdc41
...
...
@@ -3,16 +3,22 @@ var Utils = require("./utils")
,
DaoValidator
=
require
(
"./dao-validator"
)
,
DataTypes
=
require
(
"./data-types"
)
,
hstore
=
require
(
'./dialects/postgres/hstore'
)
,
_
=
require
(
'lodash'
)
module
.
exports
=
(
function
()
{
var
DAO
=
function
(
values
,
options
,
isNewRecord
)
{
var
DAO
=
function
(
values
,
options
)
{
this
.
dataValues
=
{}
this
.
__options
=
options
this
.
hasPrimaryKeys
=
options
.
hasPrimaryKeys
this
.
selectedValues
=
values
this
.
__options
=
this
.
__factory
.
options
this
.
options
=
options
this
.
hasPrimaryKeys
=
this
.
__factory
.
options
.
hasPrimaryKeys
// What is selected values even used for?
this
.
selectedValues
=
options
.
include
?
_
.
omit
(
values
,
options
.
includeNames
)
:
values
this
.
__eagerlyLoadedAssociations
=
[]
this
.
isNewRecord
=
options
.
isNewRecord
initAttributes
.
call
(
this
,
values
,
isNewRecord
)
initAttributes
.
call
(
this
,
values
,
options
)
this
.
isDirty
=
options
.
isDirty
}
Utils
.
_
.
extend
(
DAO
.
prototype
,
Mixin
.
prototype
)
...
...
@@ -36,17 +42,7 @@ module.exports = (function() {
Object
.
defineProperty
(
DAO
.
prototype
,
'values'
,
{
get
:
function
()
{
var
result
=
{}
,
self
=
this
this
.
attributes
.
concat
(
this
.
__eagerlyLoadedAssociations
).
forEach
(
function
(
attr
)
{
result
[
attr
]
=
self
.
dataValues
.
hasOwnProperty
(
attr
)
?
self
.
dataValues
[
attr
]
:
self
[
attr
]
;
})
return
result
return
this
.
dataValues
}
})
...
...
@@ -104,7 +100,7 @@ module.exports = (function() {
options
=
Utils
.
_
.
extend
({},
options
,
fieldsOrOptions
)
var
self
=
this
,
values
=
options
.
fields
?
{}
:
this
.
dataValues
,
values
=
options
.
fields
?
{}
:
(
this
.
options
.
includeNames
?
_
.
omit
(
this
.
dataValues
,
this
.
options
.
includeNames
)
:
this
.
dataValues
)
,
updatedAtAttr
=
Utils
.
_
.
underscoredIf
(
this
.
__options
.
updatedAt
,
this
.
__options
.
underscored
)
,
createdAtAttr
=
Utils
.
_
.
underscoredIf
(
this
.
__options
.
createdAt
,
this
.
__options
.
underscored
)
...
...
@@ -214,7 +210,7 @@ module.exports = (function() {
return
emitter
.
emit
(
'error'
,
err
)
}
result
.
dataValues
=
newValues
result
.
dataValues
=
_
.
extend
(
result
.
dataValues
,
newValues
)
emitter
.
emit
(
'success'
,
result
)
})
})
...
...
@@ -240,7 +236,7 @@ module.exports = (function() {
this
.
__factory
.
find
({
where
:
where
,
limit
:
1
,
include
:
this
.
__eagerlyLoadedOptions
||
[]
include
:
this
.
options
.
include
||
[]
},
options
)
.
on
(
'sql'
,
function
(
sql
)
{
emitter
.
emit
(
'sql'
,
sql
)
})
.
on
(
'error'
,
function
(
error
)
{
emitter
.
emit
(
'error'
,
error
)
})
...
...
@@ -474,12 +470,12 @@ module.exports = (function() {
}
DAO
.
prototype
.
toJSON
=
function
()
{
return
this
.
v
alues
;
return
this
.
dataV
alues
;
}
// private
var
initAttributes
=
function
(
values
,
isNewRecord
)
{
var
initAttributes
=
function
(
values
,
options
)
{
// set id to null if not passed as value, a newly created dao has no id
var
defaults
=
this
.
hasPrimaryKeys
?
{}
:
{
id
:
null
},
attrs
=
{},
...
...
@@ -487,7 +483,7 @@ module.exports = (function() {
// add all passed values to the dao and store the attribute names in this.attributes
if
(
isNewRecord
)
{
if
(
options
.
isNewRecord
)
{
if
(
this
.
hasDefaultValues
)
{
Utils
.
_
.
each
(
this
.
defaultValues
,
function
(
valueFn
,
key
)
{
if
(
!
defaults
.
hasOwnProperty
(
key
))
{
...
...
@@ -532,7 +528,40 @@ module.exports = (function() {
}
for
(
key
in
attrs
)
{
this
.
addAttribute
(
key
,
attrs
[
key
])
if
(
options
.
include
&&
options
.
includeNames
.
indexOf
(
key
)
!==
-
1
)
{
var
include
=
_
.
find
(
options
.
include
,
function
(
include
)
{
return
include
.
as
===
key
})
var
association
=
include
.
association
,
self
=
this
var
accessor
=
Utils
.
_
.
camelize
(
key
)
// downcase the first char
accessor
=
accessor
.
slice
(
0
,
1
).
toLowerCase
()
+
accessor
.
slice
(
1
)
attrs
[
key
].
forEach
(
function
(
data
)
{
var
daoInstance
=
include
.
daoFactory
.
build
(
data
,
{
isNewRecord
:
false
,
isDirty
:
false
})
,
isEmpty
=
!
Utils
.
firstValueOfHash
(
daoInstance
.
identifiers
)
if
([
'BelongsTo'
,
'HasOne'
].
indexOf
(
association
.
associationType
)
>
-
1
)
{
accessor
=
Utils
.
singularize
(
accessor
,
self
.
sequelize
.
language
)
self
.
dataValues
[
accessor
]
=
isEmpty
?
null
:
daoInstance
self
[
accessor
]
=
self
.
dataValues
[
accessor
]
}
else
{
if
(
!
self
.
dataValues
[
accessor
])
{
self
.
dataValues
[
accessor
]
=
[]
self
[
accessor
]
=
self
.
dataValues
[
accessor
]
}
if
(
!
isEmpty
)
{
self
.
dataValues
[
accessor
].
push
(
daoInstance
)
}
}
}.
bind
(
this
))
}
else
{
this
.
addAttribute
(
key
,
attrs
[
key
])
}
}
// this.addAttributes COMPLETELY destroys the structure of our DAO due to __defineGetter__ resetting the object
...
...
lib/dialects/abstract/query.js
View file @
3afdc41
var
Utils
=
require
(
'../../utils'
)
,
CustomEventEmitter
=
require
(
"../../emitters/custom-event-emitter"
)
,
Dot
=
require
(
'dottie'
)
,
_
=
require
(
'lodash'
)
module
.
exports
=
(
function
()
{
var
AbstractQuery
=
function
(
database
,
sequelize
,
callee
,
options
)
{}
...
...
@@ -100,39 +101,6 @@ module.exports = (function() {
}
/**
Shortcut methods (success, ok) for listening for success events.
Params:
- fct: A function that gets executed once the *success* event was triggered.
Result:
The function returns the instance of the query.
*/
AbstractQuery
.
prototype
.
success
=
AbstractQuery
.
prototype
.
ok
=
function
(
fct
)
{
this
.
on
(
'success'
,
fct
)
return
this
}
/**
Shortcut methods (failure, fail, error) for listening for error events.
Params:
- fct: A function that gets executed once the *error* event was triggered.
Result:
The function returns the instance of the query.
*/
AbstractQuery
.
prototype
.
failure
=
AbstractQuery
.
prototype
.
fail
=
AbstractQuery
.
prototype
.
error
=
function
(
fct
)
{
this
.
on
(
'error'
,
fct
)
return
this
}
/**
* This function is a wrapper for private methods.
*
* @param {String} fctName The name of the private method.
...
...
@@ -167,10 +135,13 @@ module.exports = (function() {
if
(
!
this
.
options
.
include
)
{
return
null
}
if
(
!
this
.
options
.
includeNames
)
{
this
.
options
.
includeNames
=
this
.
options
.
include
.
map
(
function
(
include
)
{
return
include
.
as
})
}
var
tableNames
=
this
.
options
.
include
.
map
(
function
(
include
)
{
return
include
.
as
}).
filter
(
function
(
include
)
{
var
tableNames
=
this
.
options
.
includeNames
.
filter
(
function
(
include
)
{
return
attribute
.
indexOf
(
include
+
'.'
)
===
0
})
...
...
@@ -181,20 +152,6 @@ module.exports = (function() {
}
}
var
queryResultHasJoin
=
function
(
results
)
{
if
(
!!
results
[
0
])
{
var
keys
=
Object
.
keys
(
results
[
0
])
for
(
var
i
=
0
;
i
<
keys
.
length
;
i
++
)
{
if
(
!!
findTableNameInAttribute
.
call
(
this
,
keys
[
i
]))
{
return
true
}
}
}
return
false
}
var
isInsertQuery
=
function
(
results
,
metaData
)
{
var
result
=
true
...
...
@@ -244,6 +201,7 @@ module.exports = (function() {
var
handleSelectQuery
=
function
(
results
)
{
var
result
=
null
// Raw queries
if
(
this
.
options
.
raw
)
{
result
=
results
.
map
(
function
(
result
)
{
var
o
=
{}
...
...
@@ -258,8 +216,22 @@ module.exports = (function() {
})
result
=
result
.
map
(
Dot
.
transform
)
// Queries with include
}
else
if
(
this
.
options
.
hasJoin
===
true
)
{
result
=
transformRowsWithEagerLoadingIntoDaos
.
call
(
this
,
results
)
this
.
options
.
includeNames
=
this
.
options
.
include
.
map
(
function
(
include
)
{
return
include
.
as
})
results
=
groupJoinData
.
call
(
this
,
results
)
result
=
results
.
map
(
function
(
result
)
{
return
this
.
callee
.
build
(
result
,
{
isNewRecord
:
false
,
isDirty
:
false
,
include
:
this
.
options
.
include
,
includeNames
:
this
.
options
.
includeNames
,
includeValidated
:
true
})
}.
bind
(
this
))
}
else
if
(
this
.
options
.
hasJoinTableModel
===
true
)
{
result
=
results
.
map
(
function
(
result
)
{
result
=
Dot
.
transform
(
result
)
...
...
@@ -275,6 +247,8 @@ module.exports = (function() {
return
mainDao
}.
bind
(
this
))
// Regular queries
}
else
{
result
=
results
.
map
(
function
(
result
)
{
return
this
.
callee
.
build
(
result
,
{
isNewRecord
:
false
,
isDirty
:
false
})
...
...
@@ -289,75 +263,6 @@ module.exports = (function() {
return
result
}
var
transformRowsWithEagerLoadingIntoDaos
=
function
(
results
)
{
var
result
=
[]
result
=
prepareJoinData
.
call
(
this
,
results
)
result
=
groupDataByCalleeFactory
.
call
(
this
,
result
).
map
(
function
(
result
)
{
return
transformRowWithEagerLoadingIntoDao
.
call
(
this
,
result
)
}.
bind
(
this
))
return
result
}
var
transformRowWithEagerLoadingIntoDao
=
function
(
result
,
dao
)
{
// let's build the actual dao instance first...
dao
=
dao
||
this
.
callee
.
build
(
result
[
this
.
callee
.
tableName
],
{
isNewRecord
:
false
,
isDirty
:
false
})
// ... and afterwards the prefetched associations
for
(
var
tableName
in
result
)
{
if
(
result
.
hasOwnProperty
(
tableName
)
&&
(
tableName
!==
this
.
callee
.
tableName
))
{
buildAssociatedDaoInstances
.
call
(
this
,
tableName
,
result
[
tableName
],
dao
)
}
}
return
dao
}
var
buildAssociatedDaoInstances
=
function
(
tableName
,
associationData
,
dao
)
{
var
associatedDaoFactory
=
this
.
sequelize
.
daoFactoryManager
.
getDAO
(
tableName
,
{
attribute
:
'tableName'
})
,
association
=
null
,
self
=
this
if
(
!!
associatedDaoFactory
)
{
association
=
this
.
callee
.
getAssociation
(
associatedDaoFactory
)
}
else
{
associatedDaoFactory
=
this
.
sequelize
.
daoFactoryManager
.
getDAO
(
Utils
.
pluralize
(
tableName
,
this
.
sequelize
.
language
),
{
attribute
:
'tableName'
})
if
(
!!
associatedDaoFactory
)
{
association
=
this
.
callee
.
getAssociation
(
associatedDaoFactory
)
}
else
{
association
=
this
.
callee
.
getAssociationByAlias
(
tableName
)
associatedDaoFactory
=
association
.
target
}
}
var
accessor
=
Utils
.
_
.
camelize
(
tableName
)
// downcase the first char
accessor
=
accessor
.
slice
(
0
,
1
).
toLowerCase
()
+
accessor
.
slice
(
1
)
associationData
.
forEach
(
function
(
data
)
{
var
daoInstance
=
associatedDaoFactory
.
build
(
data
,
{
isNewRecord
:
false
,
isDirty
:
false
})
,
isEmpty
=
!
Utils
.
firstValueOfHash
(
daoInstance
.
identifiers
)
if
([
'BelongsTo'
,
'HasOne'
].
indexOf
(
association
.
associationType
)
>
-
1
)
{
accessor
=
Utils
.
singularize
(
accessor
,
self
.
sequelize
.
language
)
dao
[
accessor
]
=
isEmpty
?
null
:
daoInstance
}
else
{
dao
[
accessor
]
=
dao
[
accessor
]
||
[]
if
(
!
isEmpty
)
{
dao
[
accessor
].
push
(
daoInstance
)
}
}
// add the accessor to the eagerly loaded associations array
dao
.
__eagerlyLoadedAssociations
=
Utils
.
_
.
uniq
(
dao
.
__eagerlyLoadedAssociations
.
concat
([
accessor
]))
dao
.
__eagerlyLoadedOptions
=
(
this
.
options
&&
this
.
options
.
include
)
?
this
.
options
.
include
:
[]
}.
bind
(
this
))
}
var
isShowOrDescribeQuery
=
function
()
{
var
result
=
false
...
...
@@ -381,15 +286,18 @@ module.exports = (function() {
the associated data by the callee.
Example:
group
DataByCalleeFactory
([
group
JoinData
([
{
callee: { some: 'data', id: 1 },
some: 'data',
id: 1,
association: { foo: 'bar', id: 1 }
}, {
callee: { some: 'data', id: 1 },
some: 'data',
id: 1,
association: { foo: 'bar', id: 2 }
}, {
callee: { some: 'data', id: 1 },
some: 'data',
id: 1,
association: { foo: 'bar', id: 3 }
}
])
...
...
@@ -399,7 +307,8 @@ module.exports = (function() {
[
{
callee: { some: 'data', id: 1 },
some: 'data',
id: 1,
association: [
{ foo: 'bar', id: 1 },
{ foo: 'bar', id: 2 },
...
...
@@ -408,67 +317,38 @@ module.exports = (function() {
}
]
*/
var
groupDataByCalleeFactory
=
function
(
data
)
{
var
result
=
[]
,
calleeTableName
=
this
.
callee
.
tableName
data
.
forEach
(
function
(
row
)
{
var
calleeData
=
row
[
calleeTableName
]
,
existingEntry
=
result
.
filter
(
function
(
groupedRow
)
{
return
Utils
.
_
.
isEqual
(
groupedRow
[
calleeTableName
],
calleeData
)
})[
0
]
if
(
!
existingEntry
)
{
existingEntry
=
{}
result
.
push
(
existingEntry
)
existingEntry
[
calleeTableName
]
=
calleeData
var
groupJoinData
=
function
(
data
)
{
var
self
=
this
,
results
=
[]
,
existingResult
,
calleeData
data
.
forEach
(
function
(
row
)
{
row
=
Dot
.
transform
(
row
)
calleeData
=
_
.
omit
(
row
,
self
.
options
.
includeNames
)
existingResult
=
_
.
find
(
results
,
function
(
result
)
{
return
Utils
.
_
.
isEqual
(
_
.
omit
(
result
,
self
.
options
.
includeNames
),
calleeData
)
})
if
(
!
existingResult
)
{
results
.
push
(
existingResult
=
calleeData
)
}
for
(
var
attrName
in
row
)
{
if
(
row
.
hasOwnProperty
(
attrName
)
&&
(
attrName
!==
calleeTableName
)
)
{
existing
Entry
[
attrName
]
=
existingEntry
[
attrName
]
||
[]
var
attrRowExists
=
existing
Entry
[
attrName
].
some
(
function
(
attrRow
)
{
if
(
row
.
hasOwnProperty
(
attrName
)
&&
Object
(
row
[
attrName
])
===
row
[
attrName
]
&&
self
.
options
.
includeNames
.
indexOf
(
attrName
)
!==
-
1
)
{
existing
Result
[
attrName
]
=
existingResult
[
attrName
]
||
[]
var
attrRowExists
=
existing
Result
[
attrName
].
some
(
function
(
attrRow
)
{
return
Utils
.
_
.
isEqual
(
attrRow
,
row
[
attrName
])
})
if
(
!
attrRowExists
)
{
existing
Entry
[
attrName
].
push
(
row
[
attrName
])
existing
Result
[
attrName
].
push
(
row
[
attrName
])
}
}
}
}
})
return
result
}
/**
* This function will prepare the result of select queries with joins.
*
* @param {Array} data This array contains objects.
* @return {Array} The array will have the needed format for groupDataByCalleeFactory.
*/
var
prepareJoinData
=
function
(
data
)
{
var
result
=
data
.
map
(
function
(
row
)
{
var
nestedRow
=
{}
for
(
var
key
in
row
)
{
if
(
row
.
hasOwnProperty
(
key
))
{
var
tableName
=
findTableNameInAttribute
.
call
(
this
,
key
)
if
(
!!
tableName
)
{
nestedRow
[
tableName
]
=
nestedRow
[
tableName
]
||
{}
nestedRow
[
tableName
][
key
.
replace
(
tableName
+
'.'
,
''
)]
=
row
[
key
]
}
else
{
nestedRow
[
this
.
callee
.
tableName
]
=
nestedRow
[
this
.
callee
.
tableName
]
||
{}
nestedRow
[
this
.
callee
.
tableName
][
key
]
=
row
[
key
]
}
}
}
return
nestedRow
}.
bind
(
this
))
return
result
return
results
}
return
AbstractQuery
...
...
lib/emitters/custom-event-emitter.js
View file @
3afdc41
...
...
@@ -28,6 +28,15 @@ module.exports = (function() {
return
this
}
/**
Shortcut methods (success, ok) for listening for success events.
Params:
- fct: A function that gets executed once the *success* event was triggered.
Result:
The function returns the instance of the query.
*/
CustomEventEmitter
.
prototype
.
success
=
CustomEventEmitter
.
prototype
.
ok
=
function
(
fct
)
{
...
...
@@ -35,6 +44,15 @@ module.exports = (function() {
return
this
}
/**
Shortcut methods (failure, fail, error) for listening for error events.
Params:
- fct: A function that gets executed once the *error* event was triggered.
Result:
The function returns the instance of the query.
*/
CustomEventEmitter
.
prototype
.
failure
=
CustomEventEmitter
.
prototype
.
fail
=
CustomEventEmitter
.
prototype
.
error
=
...
...
lib/query-interface.js
View file @
3afdc41
...
...
@@ -660,6 +660,7 @@ module.exports = (function() {
var
sql
=
this
.
QueryGenerator
.
selectQuery
(
tableName
,
options
,
factory
)
queryOptions
=
Utils
.
_
.
extend
({},
queryOptions
,
{
include
:
options
.
include
})
return
queryAndEmit
.
call
(
this
,
[
sql
,
factory
,
queryOptions
],
'select'
)
}
...
...
test/dao-factory/find.test.js
View file @
3afdc41
...
...
@@ -904,9 +904,9 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
}],
createTags
:
function
(
callback
)
{
self
.
Tag
.
bulkCreate
([
{
titl
e
:
'Furniture'
},
{
titl
e
:
'Clothing'
},
{
titl
e
:
'People'
}
{
nam
e
:
'Furniture'
},
{
nam
e
:
'Clothing'
},
{
nam
e
:
'People'
}
]).
done
(
callback
)
},
tags
:
[
'createTags'
,
function
(
callback
)
{
...
...
test/dao.test.js
View file @
3afdc41
...
...
@@ -944,7 +944,9 @@ describe(Support.getTestDialectTeaser("DAO"), function () {
user
.
age
=
user
.
age
+
1
// happy birthday joe
user
.
save
().
success
(
function
()
{
user
.
save
().
done
(
function
(
err
)
{
expect
(
err
).
not
.
to
.
be
.
ok
expect
(
user
.
username
).
to
.
equal
(
'joe'
)
expect
(
user
.
age
).
to
.
equal
(
2
)
expect
(
user
.
projects
).
to
.
exist
...
...
Write
Preview
Markdown
is supported
Attach a file
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to post a comment