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 4f098998
authored
Aug 13, 2019
by
javiertury
Committed by
Sushant
Aug 13, 2019
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: support include option in bulkInsert (#11307)
1 parent
de06ac3f
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
824 additions
and
37 deletions
lib/model.js
test/integration/model/bulk-create.test.js
test/integration/model/bulk-create/include.test.js
types/lib/model.d.ts
lib/model.js
View file @
4f09899
...
@@ -2532,6 +2532,22 @@ class Model {
...
@@ -2532,6 +2532,22 @@ class Model {
return
Promise
.
resolve
([]);
return
Promise
.
resolve
([]);
}
}
const
dialect
=
this
.
sequelize
.
options
.
dialect
;
const
now
=
Utils
.
now
(
this
.
sequelize
.
options
.
dialect
);
options
.
model
=
this
;
if
(
!
options
.
includeValidated
)
{
this
.
_conformIncludes
(
options
,
this
);
if
(
options
.
include
)
{
this
.
_expandIncludeAll
(
options
);
this
.
_validateIncludedElements
(
options
);
}
}
const
instances
=
records
.
map
(
values
=>
this
.
build
(
values
,
{
isNewRecord
:
true
,
include
:
options
.
include
}));
const
recursiveBulkCreate
=
(
instances
,
options
)
=>
{
options
=
Object
.
assign
({
options
=
Object
.
assign
({
validate
:
false
,
validate
:
false
,
hooks
:
true
,
hooks
:
true
,
...
@@ -2539,9 +2555,13 @@ class Model {
...
@@ -2539,9 +2555,13 @@ class Model {
ignoreDuplicates
:
false
ignoreDuplicates
:
false
},
options
);
},
options
);
options
.
fields
=
options
.
fields
||
Object
.
keys
(
this
.
rawAttributes
);
if
(
options
.
returning
===
undefined
)
{
if
(
options
.
association
)
{
const
dialect
=
this
.
sequelize
.
options
.
dialect
;
options
.
returning
=
false
;
}
else
{
options
.
returning
=
true
;
}
}
if
(
options
.
ignoreDuplicates
&&
[
'mssql'
].
includes
(
dialect
))
{
if
(
options
.
ignoreDuplicates
&&
[
'mssql'
].
includes
(
dialect
))
{
return
Promise
.
reject
(
new
Error
(
`
${
dialect
}
does not support the ignoreDuplicates option.`
));
return
Promise
.
reject
(
new
Error
(
`
${
dialect
}
does not support the ignoreDuplicates option.`
));
...
@@ -2550,10 +2570,16 @@ class Model {
...
@@ -2550,10 +2570,16 @@ class Model {
return
Promise
.
reject
(
new
Error
(
`
${
dialect
}
does not support the updateOnDuplicate option.`
));
return
Promise
.
reject
(
new
Error
(
`
${
dialect
}
does not support the updateOnDuplicate option.`
));
}
}
const
model
=
options
.
model
;
options
.
fields
=
options
.
fields
||
Object
.
keys
(
model
.
rawAttributes
);
const
createdAtAttr
=
model
.
_timestampAttributes
.
createdAt
;
const
updatedAtAttr
=
model
.
_timestampAttributes
.
updatedAt
;
if
(
options
.
updateOnDuplicate
!==
undefined
)
{
if
(
options
.
updateOnDuplicate
!==
undefined
)
{
if
(
Array
.
isArray
(
options
.
updateOnDuplicate
)
&&
options
.
updateOnDuplicate
.
length
)
{
if
(
Array
.
isArray
(
options
.
updateOnDuplicate
)
&&
options
.
updateOnDuplicate
.
length
)
{
options
.
updateOnDuplicate
=
_
.
intersection
(
options
.
updateOnDuplicate
=
_
.
intersection
(
_
.
without
(
Object
.
keys
(
this
.
tableAttributes
),
this
.
_timestampAttributes
.
createdAt
),
_
.
without
(
Object
.
keys
(
model
.
tableAttributes
),
createdAtAttr
),
options
.
updateOnDuplicate
options
.
updateOnDuplicate
);
);
}
else
{
}
else
{
...
@@ -2561,18 +2587,10 @@ class Model {
...
@@ -2561,18 +2587,10 @@ class Model {
}
}
}
}
options
.
model
=
this
;
const
createdAtAttr
=
this
.
_timestampAttributes
.
createdAt
;
const
updatedAtAttr
=
this
.
_timestampAttributes
.
updatedAt
;
const
now
=
Utils
.
now
(
this
.
sequelize
.
options
.
dialect
);
let
instances
=
records
.
map
(
values
=>
this
.
build
(
values
,
{
isNewRecord
:
true
}));
return
Promise
.
try
(()
=>
{
return
Promise
.
try
(()
=>
{
// Run before hook
// Run before hook
if
(
options
.
hooks
)
{
if
(
options
.
hooks
)
{
return
this
.
runHooks
(
'beforeBulkCreate'
,
instances
,
options
);
return
model
.
runHooks
(
'beforeBulkCreate'
,
instances
,
options
);
}
}
}).
then
(()
=>
{
}).
then
(()
=>
{
// Validate
// Validate
...
@@ -2604,10 +2622,46 @@ class Model {
...
@@ -2604,10 +2622,46 @@ class Model {
individualOptions
.
hooks
=
true
;
individualOptions
.
hooks
=
true
;
return
instance
.
save
(
individualOptions
);
return
instance
.
save
(
individualOptions
);
}).
then
(
_instances
=>
{
instances
=
_instances
;
});
});
}
}
return
Promise
.
resolve
().
then
(()
=>
{
if
(
!
options
.
include
||
!
options
.
include
.
length
)
return
;
// Nested creation for BelongsTo relations
return
Promise
.
map
(
options
.
include
.
filter
(
include
=>
include
.
association
instanceof
BelongsTo
),
include
=>
{
const
associationInstances
=
[];
const
associationInstanceIndexToInstanceMap
=
[];
for
(
const
instance
of
instances
)
{
const
associationInstance
=
instance
.
get
(
include
.
as
);
if
(
associationInstance
)
{
associationInstances
.
push
(
associationInstance
);
associationInstanceIndexToInstanceMap
.
push
(
instance
);
}
}
if
(
!
associationInstances
.
length
)
{
return
;
}
const
includeOptions
=
_
(
Utils
.
cloneDeep
(
include
))
.
omit
([
'association'
])
.
defaults
({
transaction
:
options
.
transaction
,
logging
:
options
.
logging
}).
value
();
return
recursiveBulkCreate
(
associationInstances
,
includeOptions
).
then
(
associationInstances
=>
{
for
(
const
idx
in
associationInstances
)
{
const
associationInstance
=
associationInstances
[
idx
];
const
instance
=
associationInstanceIndexToInstanceMap
[
idx
];
instance
[
include
.
association
.
accessors
.
set
](
associationInstance
,
{
save
:
false
,
logging
:
options
.
logging
});
}
});
});
}).
then
(()
=>
{
// Create all in one query
// Create all in one query
// Recreate records from instances to represent any changes made in hooks or validation
// Recreate records from instances to represent any changes made in hooks or validation
records
=
instances
.
map
(
instance
=>
{
records
=
instances
.
map
(
instance
=>
{
...
@@ -2627,10 +2681,8 @@ class Model {
...
@@ -2627,10 +2681,8 @@ class Model {
}
}
}
}
instance
.
dataValues
=
Utils
.
mapValueFieldNames
(
values
,
options
.
fields
,
this
);
const
out
=
Object
.
assign
({},
Utils
.
mapValueFieldNames
(
values
,
options
.
fields
,
model
));
for
(
const
key
of
model
.
_virtualAttributes
)
{
const
out
=
Object
.
assign
({},
instance
.
dataValues
);
for
(
const
key
of
this
.
_virtualAttributes
)
{
delete
out
[
key
];
delete
out
[
key
];
}
}
return
out
;
return
out
;
...
@@ -2638,45 +2690,140 @@ class Model {
...
@@ -2638,45 +2690,140 @@ class Model {
// Map attributes to fields for serial identification
// Map attributes to fields for serial identification
const
fieldMappedAttributes
=
{};
const
fieldMappedAttributes
=
{};
for
(
const
attr
in
this
.
tableAttributes
)
{
for
(
const
attr
in
model
.
tableAttributes
)
{
fieldMappedAttributes
[
this
.
rawAttributes
[
attr
].
field
||
attr
]
=
this
.
rawAttributes
[
attr
];
fieldMappedAttributes
[
model
.
rawAttributes
[
attr
].
field
||
attr
]
=
model
.
rawAttributes
[
attr
];
}
}
// Map updateOnDuplicate attributes to fields
// Map updateOnDuplicate attributes to fields
if
(
options
.
updateOnDuplicate
)
{
if
(
options
.
updateOnDuplicate
)
{
options
.
updateOnDuplicate
=
options
.
updateOnDuplicate
.
map
(
attr
=>
this
.
rawAttributes
[
attr
].
field
||
attr
);
options
.
updateOnDuplicate
=
options
.
updateOnDuplicate
.
map
(
attr
=>
model
.
rawAttributes
[
attr
].
field
||
attr
);
// Get primary keys for postgres to enable updateOnDuplicate
// Get primary keys for postgres to enable updateOnDuplicate
options
.
upsertKeys
=
_
.
chain
(
this
.
primaryKeys
).
values
().
map
(
'fieldName'
).
value
();
options
.
upsertKeys
=
_
.
chain
(
model
.
primaryKeys
).
values
().
map
(
'fieldName'
).
value
();
if
(
Object
.
keys
(
this
.
uniqueKeys
).
length
>
0
)
{
if
(
Object
.
keys
(
model
.
uniqueKeys
).
length
>
0
)
{
options
.
upsertKeys
=
_
.
chain
(
this
.
uniqueKeys
).
values
().
filter
(
c
=>
c
.
fields
.
length
===
1
).
map
(
'column'
).
value
();
options
.
upsertKeys
=
_
.
chain
(
model
.
uniqueKeys
).
values
().
filter
(
c
=>
c
.
fields
.
length
===
1
).
map
(
'column'
).
value
();
}
}
}
}
// Map returning attributes to fields
// Map returning attributes to fields
if
(
options
.
returning
&&
Array
.
isArray
(
options
.
returning
))
{
if
(
options
.
returning
&&
Array
.
isArray
(
options
.
returning
))
{
options
.
returning
=
options
.
returning
.
map
(
attr
=>
this
.
rawAttributes
[
attr
].
field
||
attr
);
options
.
returning
=
options
.
returning
.
map
(
attr
=>
model
.
rawAttributes
[
attr
].
field
||
attr
);
}
}
return
this
.
QueryInterface
.
bulkInsert
(
this
.
getTableName
(
options
),
records
,
options
,
fieldMappedAttributes
).
then
(
results
=>
{
return
model
.
QueryInterface
.
bulkInsert
(
model
.
getTableName
(
options
),
records
,
options
,
fieldMappedAttributes
).
then
(
results
=>
{
if
(
Array
.
isArray
(
results
))
{
if
(
Array
.
isArray
(
results
))
{
results
.
forEach
((
result
,
i
)
=>
{
results
.
forEach
((
result
,
i
)
=>
{
if
(
instances
[
i
]
&&
!
instances
[
i
].
get
(
this
.
primaryKeyAttribute
))
{
const
instance
=
instances
[
i
];
instances
[
i
].
dataValues
[
this
.
primaryKeyField
]
=
result
[
this
.
primaryKeyField
];
for
(
const
key
in
result
)
{
if
(
!
instance
||
key
===
model
.
primaryKeyAttribute
&&
instance
.
get
(
model
.
primaryKeyAttribute
)
&&
[
'mysql'
,
'mariadb'
,
'sqlite'
].
includes
(
dialect
))
{
// The query.js for these DBs is blind, it autoincrements the
// primarykey value, even if it was set manually. Also, it can
// return more results than instances, bug?.
continue
;
}
if
(
Object
.
prototype
.
hasOwnProperty
.
call
(
result
,
key
))
{
const
record
=
result
[
key
];
const
attr
=
_
.
find
(
model
.
rawAttributes
,
attribute
=>
attribute
.
fieldName
===
key
||
attribute
.
field
===
key
);
instance
.
dataValues
[
attr
&&
attr
.
fieldName
||
key
]
=
record
;
}
}
}
});
});
}
}
return
results
;
return
results
;
});
});
});
}).
then
(()
=>
{
if
(
!
options
.
include
||
!
options
.
include
.
length
)
return
;
// Nested creation for HasOne/HasMany/BelongsToMany relations
return
Promise
.
map
(
options
.
include
.
filter
(
include
=>
!
(
include
.
association
instanceof
BelongsTo
||
include
.
parent
&&
include
.
parent
.
association
instanceof
BelongsToMany
)),
include
=>
{
const
associationInstances
=
[];
const
associationInstanceIndexToInstanceMap
=
[];
for
(
const
instance
of
instances
)
{
let
associated
=
instance
.
get
(
include
.
as
);
if
(
!
Array
.
isArray
(
associated
))
associated
=
[
associated
];
for
(
const
associationInstance
of
associated
)
{
if
(
associationInstance
)
{
if
(
!
(
include
.
association
instanceof
BelongsToMany
))
{
associationInstance
.
set
(
include
.
association
.
foreignKey
,
instance
.
get
(
include
.
association
.
sourceKey
||
instance
.
constructor
.
primaryKeyAttribute
,
{
raw
:
true
}),
{
raw
:
true
});
Object
.
assign
(
associationInstance
,
include
.
association
.
scope
);
}
associationInstances
.
push
(
associationInstance
);
associationInstanceIndexToInstanceMap
.
push
(
instance
);
}
}
}
if
(
!
associationInstances
.
length
)
{
return
;
}
const
includeOptions
=
_
(
Utils
.
cloneDeep
(
include
))
.
omit
([
'association'
])
.
defaults
({
transaction
:
options
.
transaction
,
logging
:
options
.
logging
}).
value
();
return
recursiveBulkCreate
(
associationInstances
,
includeOptions
).
then
(
associationInstances
=>
{
if
(
include
.
association
instanceof
BelongsToMany
)
{
const
valueSets
=
[];
for
(
const
idx
in
associationInstances
)
{
const
associationInstance
=
associationInstances
[
idx
];
const
instance
=
associationInstanceIndexToInstanceMap
[
idx
];
const
values
=
{};
values
[
include
.
association
.
foreignKey
]
=
instance
.
get
(
instance
.
constructor
.
primaryKeyAttribute
,
{
raw
:
true
});
values
[
include
.
association
.
otherKey
]
=
associationInstance
.
get
(
associationInstance
.
constructor
.
primaryKeyAttribute
,
{
raw
:
true
});
// Include values defined in the association
Object
.
assign
(
values
,
include
.
association
.
through
.
scope
);
if
(
associationInstance
[
include
.
association
.
through
.
model
.
name
])
{
for
(
const
attr
of
Object
.
keys
(
include
.
association
.
through
.
model
.
rawAttributes
))
{
if
(
include
.
association
.
through
.
model
.
rawAttributes
[
attr
].
_autoGenerated
||
attr
===
include
.
association
.
foreignKey
||
attr
===
include
.
association
.
otherKey
||
typeof
associationInstance
[
include
.
association
.
through
.
model
.
name
][
attr
]
===
undefined
)
{
continue
;
}
values
[
attr
]
=
associationInstance
[
include
.
association
.
through
.
model
.
name
][
attr
];
}
}
valueSets
.
push
(
values
);
}
const
throughOptions
=
_
(
Utils
.
cloneDeep
(
include
))
.
omit
([
'association'
,
'attributes'
])
.
defaults
({
transaction
:
options
.
transaction
,
logging
:
options
.
logging
}).
value
();
throughOptions
.
model
=
include
.
association
.
throughModel
;
const
throughInstances
=
include
.
association
.
throughModel
.
bulkBuild
(
valueSets
,
throughOptions
);
return
recursiveBulkCreate
(
throughInstances
,
throughOptions
);
}
});
});
}).
then
(()
=>
{
}).
then
(()
=>
{
// map fields back to attributes
// map fields back to attributes
instances
.
forEach
(
instance
=>
{
instances
.
forEach
(
instance
=>
{
for
(
const
attr
in
this
.
rawAttributes
)
{
for
(
const
attr
in
model
.
rawAttributes
)
{
if
(
this
.
rawAttributes
[
attr
].
field
&&
if
(
model
.
rawAttributes
[
attr
].
field
&&
instance
.
dataValues
[
this
.
rawAttributes
[
attr
].
field
]
!==
undefined
&&
instance
.
dataValues
[
model
.
rawAttributes
[
attr
].
field
]
!==
undefined
&&
this
.
rawAttributes
[
attr
].
field
!==
attr
model
.
rawAttributes
[
attr
].
field
!==
attr
)
{
)
{
instance
.
dataValues
[
attr
]
=
instance
.
dataValues
[
this
.
rawAttributes
[
attr
].
field
];
instance
.
dataValues
[
attr
]
=
instance
.
dataValues
[
model
.
rawAttributes
[
attr
].
field
];
delete
instance
.
dataValues
[
this
.
rawAttributes
[
attr
].
field
];
delete
instance
.
dataValues
[
model
.
rawAttributes
[
attr
].
field
];
}
}
instance
.
_previousDataValues
[
attr
]
=
instance
.
dataValues
[
attr
];
instance
.
_previousDataValues
[
attr
]
=
instance
.
dataValues
[
attr
];
instance
.
changed
(
attr
,
false
);
instance
.
changed
(
attr
,
false
);
...
@@ -2686,9 +2833,12 @@ class Model {
...
@@ -2686,9 +2833,12 @@ class Model {
// Run after hook
// Run after hook
if
(
options
.
hooks
)
{
if
(
options
.
hooks
)
{
return
this
.
runHooks
(
'afterBulkCreate'
,
instances
,
options
);
return
model
.
runHooks
(
'afterBulkCreate'
,
instances
,
options
);
}
}
}).
then
(()
=>
instances
);
}).
then
(()
=>
instances
);
};
return
recursiveBulkCreate
(
instances
,
options
);
}
}
/**
/**
...
...
test/integration/model/bulk-create.test.js
View file @
4f09899
...
@@ -152,7 +152,7 @@ describe(Support.getTestDialectTeaser('Model'), () => {
...
@@ -152,7 +152,7 @@ describe(Support.getTestDialectTeaser('Model'), () => {
if
(
dialect
===
'postgres'
)
{
if
(
dialect
===
'postgres'
)
{
expect
(
sql
).
to
.
include
(
'INSERT INTO "Beers" ("id","style","createdAt","updatedAt") VALUES (DEFAULT'
);
expect
(
sql
).
to
.
include
(
'INSERT INTO "Beers" ("id","style","createdAt","updatedAt") VALUES (DEFAULT'
);
}
else
if
(
dialect
===
'mssql'
)
{
}
else
if
(
dialect
===
'mssql'
)
{
expect
(
sql
).
to
.
include
(
'INSERT INTO [Beers] ([style],[createdAt],[updatedAt])
VALUES
'
);
expect
(
sql
).
to
.
include
(
'INSERT INTO [Beers] ([style],[createdAt],[updatedAt]) '
);
}
else
{
// mysql, sqlite
}
else
{
// mysql, sqlite
expect
(
sql
).
to
.
include
(
'INSERT INTO `Beers` (`id`,`style`,`createdAt`,`updatedAt`) VALUES (NULL'
);
expect
(
sql
).
to
.
include
(
'INSERT INTO `Beers` (`id`,`style`,`createdAt`,`updatedAt`) VALUES (NULL'
);
}
}
...
...
test/integration/model/bulk-create/include.test.js
0 → 100644
View file @
4f09899
'use strict'
;
const
chai
=
require
(
'chai'
),
Sequelize
=
require
(
'../../../../index'
),
expect
=
chai
.
expect
,
Support
=
require
(
'../../support'
),
DataTypes
=
require
(
'../../../../lib/data-types'
);
describe
(
Support
.
getTestDialectTeaser
(
'Model'
),
()
=>
{
describe
(
'bulkCreate'
,
()
=>
{
describe
(
'include'
,
()
=>
{
it
(
'should bulkCreate data for BelongsTo relations'
,
function
()
{
const
Product
=
this
.
sequelize
.
define
(
'Product'
,
{
title
:
Sequelize
.
STRING
},
{
hooks
:
{
afterBulkCreate
(
products
)
{
products
.
forEach
(
product
=>
{
product
.
isIncludeCreatedOnAfterCreate
=
!!
(
product
.
User
&&
product
.
User
.
id
);
});
}
}
});
const
User
=
this
.
sequelize
.
define
(
'User'
,
{
first_name
:
Sequelize
.
STRING
,
last_name
:
Sequelize
.
STRING
},
{
hooks
:
{
beforeBulkCreate
(
users
,
options
)
{
users
.
forEach
(
user
=>
{
user
.
createOptions
=
options
;
});
}
}
});
Product
.
belongsTo
(
User
);
return
this
.
sequelize
.
sync
({
force
:
true
}).
then
(()
=>
{
return
Product
.
bulkCreate
([{
title
:
'Chair'
,
User
:
{
first_name
:
'Mick'
,
last_name
:
'Broadstone'
}
},
{
title
:
'Table'
,
User
:
{
first_name
:
'John'
,
last_name
:
'Johnson'
}
}],
{
include
:
[{
model
:
User
,
myOption
:
'option'
}]
}).
then
(
savedProducts
=>
{
expect
(
savedProducts
[
0
].
isIncludeCreatedOnAfterCreate
).
to
.
be
.
true
;
expect
(
savedProducts
[
0
].
User
.
createOptions
.
myOption
).
to
.
be
.
equal
(
'option'
);
expect
(
savedProducts
[
1
].
isIncludeCreatedOnAfterCreate
).
to
.
be
.
true
;
expect
(
savedProducts
[
1
].
User
.
createOptions
.
myOption
).
to
.
be
.
equal
(
'option'
);
return
Promise
.
all
([
Product
.
findOne
({
where
:
{
id
:
savedProducts
[
0
].
id
},
include
:
[
User
]
}),
Product
.
findOne
({
where
:
{
id
:
savedProducts
[
1
].
id
},
include
:
[
User
]
})
]).
then
(
persistedProducts
=>
{
expect
(
persistedProducts
[
0
].
User
).
to
.
be
.
ok
;
expect
(
persistedProducts
[
0
].
User
.
first_name
).
to
.
be
.
equal
(
'Mick'
);
expect
(
persistedProducts
[
0
].
User
.
last_name
).
to
.
be
.
equal
(
'Broadstone'
);
expect
(
persistedProducts
[
1
].
User
).
to
.
be
.
ok
;
expect
(
persistedProducts
[
1
].
User
.
first_name
).
to
.
be
.
equal
(
'John'
);
expect
(
persistedProducts
[
1
].
User
.
last_name
).
to
.
be
.
equal
(
'Johnson'
);
});
});
});
});
it
(
'should bulkCreate data for BelongsTo relations with no nullable FK'
,
function
()
{
const
Product
=
this
.
sequelize
.
define
(
'Product'
,
{
title
:
Sequelize
.
STRING
});
const
User
=
this
.
sequelize
.
define
(
'User'
,
{
first_name
:
Sequelize
.
STRING
});
Product
.
belongsTo
(
User
,
{
foreignKey
:
{
allowNull
:
false
}
});
return
this
.
sequelize
.
sync
({
force
:
true
}).
then
(()
=>
{
return
Product
.
bulkCreate
([{
title
:
'Chair'
,
User
:
{
first_name
:
'Mick'
}
},
{
title
:
'Table'
,
User
:
{
first_name
:
'John'
}
}],
{
include
:
[{
model
:
User
}]
}).
then
(
savedProducts
=>
{
expect
(
savedProducts
[
0
]).
to
.
exist
;
expect
(
savedProducts
[
0
].
title
).
to
.
be
.
equal
(
'Chair'
);
expect
(
savedProducts
[
0
].
User
).
to
.
exist
;
expect
(
savedProducts
[
0
].
User
.
first_name
).
to
.
be
.
equal
(
'Mick'
);
expect
(
savedProducts
[
1
]).
to
.
exist
;
expect
(
savedProducts
[
1
].
title
).
to
.
be
.
equal
(
'Table'
);
expect
(
savedProducts
[
1
].
User
).
to
.
exist
;
expect
(
savedProducts
[
1
].
User
.
first_name
).
to
.
be
.
equal
(
'John'
);
});
});
});
it
(
'should bulkCreate data for BelongsTo relations with alias'
,
function
()
{
const
Product
=
this
.
sequelize
.
define
(
'Product'
,
{
title
:
Sequelize
.
STRING
});
const
User
=
this
.
sequelize
.
define
(
'User'
,
{
first_name
:
Sequelize
.
STRING
,
last_name
:
Sequelize
.
STRING
});
const
Creator
=
Product
.
belongsTo
(
User
,
{
as
:
'creator'
});
return
this
.
sequelize
.
sync
({
force
:
true
}).
then
(()
=>
{
return
Product
.
bulkCreate
([{
title
:
'Chair'
,
creator
:
{
first_name
:
'Matt'
,
last_name
:
'Hansen'
}
},
{
title
:
'Table'
,
creator
:
{
first_name
:
'John'
,
last_name
:
'Johnson'
}
}],
{
include
:
[
Creator
]
}).
then
(
savedProducts
=>
{
return
Promise
.
all
([
Product
.
findOne
({
where
:
{
id
:
savedProducts
[
0
].
id
},
include
:
[
Creator
]
}),
Product
.
findOne
({
where
:
{
id
:
savedProducts
[
1
].
id
},
include
:
[
Creator
]
})
]).
then
(
persistedProducts
=>
{
expect
(
persistedProducts
[
0
].
creator
).
to
.
be
.
ok
;
expect
(
persistedProducts
[
0
].
creator
.
first_name
).
to
.
be
.
equal
(
'Matt'
);
expect
(
persistedProducts
[
0
].
creator
.
last_name
).
to
.
be
.
equal
(
'Hansen'
);
expect
(
persistedProducts
[
1
].
creator
).
to
.
be
.
ok
;
expect
(
persistedProducts
[
1
].
creator
.
first_name
).
to
.
be
.
equal
(
'John'
);
expect
(
persistedProducts
[
1
].
creator
.
last_name
).
to
.
be
.
equal
(
'Johnson'
);
});
});
});
});
it
(
'should bulkCreate data for HasMany relations'
,
function
()
{
const
Product
=
this
.
sequelize
.
define
(
'Product'
,
{
title
:
Sequelize
.
STRING
},
{
hooks
:
{
afterBulkCreate
(
products
)
{
products
.
forEach
(
product
=>
{
product
.
areIncludesCreatedOnAfterCreate
=
product
.
Tags
&&
product
.
Tags
.
every
(
tag
=>
{
return
!!
tag
.
id
;
});
});
}
}
});
const
Tag
=
this
.
sequelize
.
define
(
'Tag'
,
{
name
:
Sequelize
.
STRING
},
{
hooks
:
{
afterBulkCreate
(
tags
,
options
)
{
tags
.
forEach
(
tag
=>
tag
.
createOptions
=
options
);
}
}
});
Product
.
hasMany
(
Tag
);
return
this
.
sequelize
.
sync
({
force
:
true
}).
then
(()
=>
{
return
Product
.
bulkCreate
([{
id
:
1
,
title
:
'Chair'
,
Tags
:
[
{
id
:
1
,
name
:
'Alpha'
},
{
id
:
2
,
name
:
'Beta'
}
]
},
{
id
:
2
,
title
:
'Table'
,
Tags
:
[
{
id
:
3
,
name
:
'Gamma'
},
{
id
:
4
,
name
:
'Delta'
}
]
}],
{
include
:
[{
model
:
Tag
,
myOption
:
'option'
}]
}).
then
(
savedProducts
=>
{
expect
(
savedProducts
[
0
].
areIncludesCreatedOnAfterCreate
).
to
.
be
.
true
;
expect
(
savedProducts
[
0
].
Tags
[
0
].
createOptions
.
myOption
).
to
.
be
.
equal
(
'option'
);
expect
(
savedProducts
[
0
].
Tags
[
1
].
createOptions
.
myOption
).
to
.
be
.
equal
(
'option'
);
expect
(
savedProducts
[
1
].
areIncludesCreatedOnAfterCreate
).
to
.
be
.
true
;
expect
(
savedProducts
[
1
].
Tags
[
0
].
createOptions
.
myOption
).
to
.
be
.
equal
(
'option'
);
expect
(
savedProducts
[
1
].
Tags
[
1
].
createOptions
.
myOption
).
to
.
be
.
equal
(
'option'
);
return
Promise
.
all
([
Product
.
findOne
({
where
:
{
id
:
savedProducts
[
0
].
id
},
include
:
[
Tag
]
}),
Product
.
findOne
({
where
:
{
id
:
savedProducts
[
1
].
id
},
include
:
[
Tag
]
})
]).
then
(
persistedProducts
=>
{
expect
(
persistedProducts
[
0
].
Tags
).
to
.
be
.
ok
;
expect
(
persistedProducts
[
0
].
Tags
.
length
).
to
.
equal
(
2
);
expect
(
persistedProducts
[
1
].
Tags
).
to
.
be
.
ok
;
expect
(
persistedProducts
[
1
].
Tags
.
length
).
to
.
equal
(
2
);
});
});
});
});
it
(
'should bulkCreate data for HasMany relations with alias'
,
function
()
{
const
Product
=
this
.
sequelize
.
define
(
'Product'
,
{
title
:
Sequelize
.
STRING
});
const
Tag
=
this
.
sequelize
.
define
(
'Tag'
,
{
name
:
Sequelize
.
STRING
});
const
Categories
=
Product
.
hasMany
(
Tag
,
{
as
:
'categories'
});
return
this
.
sequelize
.
sync
({
force
:
true
}).
then
(()
=>
{
return
Product
.
bulkCreate
([{
id
:
1
,
title
:
'Chair'
,
categories
:
[
{
id
:
1
,
name
:
'Alpha'
},
{
id
:
2
,
name
:
'Beta'
}
]
},
{
id
:
2
,
title
:
'Table'
,
categories
:
[
{
id
:
3
,
name
:
'Gamma'
},
{
id
:
4
,
name
:
'Delta'
}
]
}],
{
include
:
[
Categories
]
}).
then
(
savedProducts
=>
{
return
Promise
.
all
([
Product
.
findOne
({
where
:
{
id
:
savedProducts
[
0
].
id
},
include
:
[
Categories
]
}),
Product
.
findOne
({
where
:
{
id
:
savedProducts
[
1
].
id
},
include
:
[
Categories
]
})
]).
then
(
persistedProducts
=>
{
expect
(
persistedProducts
[
0
].
categories
).
to
.
be
.
ok
;
expect
(
persistedProducts
[
0
].
categories
.
length
).
to
.
equal
(
2
);
expect
(
persistedProducts
[
1
].
categories
).
to
.
be
.
ok
;
expect
(
persistedProducts
[
1
].
categories
.
length
).
to
.
equal
(
2
);
});
});
});
});
it
(
'should bulkCreate data for HasOne relations'
,
function
()
{
const
User
=
this
.
sequelize
.
define
(
'User'
,
{
username
:
Sequelize
.
STRING
});
const
Task
=
this
.
sequelize
.
define
(
'Task'
,
{
title
:
Sequelize
.
STRING
});
User
.
hasOne
(
Task
);
return
this
.
sequelize
.
sync
({
force
:
true
}).
then
(()
=>
{
return
User
.
bulkCreate
([{
username
:
'Muzzy'
,
Task
:
{
title
:
'Eat Clocks'
}
},
{
username
:
'Walker'
,
Task
:
{
title
:
'Walk'
}
}],
{
include
:
[
Task
]
}).
then
(
savedUsers
=>
{
return
Promise
.
all
([
User
.
findOne
({
where
:
{
id
:
savedUsers
[
0
].
id
},
include
:
[
Task
]
}),
User
.
findOne
({
where
:
{
id
:
savedUsers
[
1
].
id
},
include
:
[
Task
]
})
]).
then
(
persistedUsers
=>
{
expect
(
persistedUsers
[
0
].
Task
).
to
.
be
.
ok
;
expect
(
persistedUsers
[
1
].
Task
).
to
.
be
.
ok
;
});
});
});
});
it
(
'should bulkCreate data for HasOne relations with alias'
,
function
()
{
const
User
=
this
.
sequelize
.
define
(
'User'
,
{
username
:
Sequelize
.
STRING
});
const
Task
=
this
.
sequelize
.
define
(
'Task'
,
{
title
:
Sequelize
.
STRING
});
const
Job
=
User
.
hasOne
(
Task
,
{
as
:
'job'
});
return
this
.
sequelize
.
sync
({
force
:
true
}).
then
(()
=>
{
return
User
.
bulkCreate
([{
username
:
'Muzzy'
,
job
:
{
title
:
'Eat Clocks'
}
},
{
username
:
'Walker'
,
job
:
{
title
:
'Walk'
}
}],
{
include
:
[
Job
]
}).
then
(
savedUsers
=>
{
return
Promise
.
all
([
User
.
findOne
({
where
:
{
id
:
savedUsers
[
0
].
id
},
include
:
[
Job
]
}),
User
.
findOne
({
where
:
{
id
:
savedUsers
[
1
].
id
},
include
:
[
Job
]
})
]).
then
(
persistedUsers
=>
{
expect
(
persistedUsers
[
0
].
job
).
to
.
be
.
ok
;
expect
(
persistedUsers
[
1
].
job
).
to
.
be
.
ok
;
});
});
});
});
it
(
'should bulkCreate data for BelongsToMany relations'
,
function
()
{
const
User
=
this
.
sequelize
.
define
(
'User'
,
{
username
:
DataTypes
.
STRING
},
{
hooks
:
{
afterBulkCreate
(
users
)
{
users
.
forEach
(
user
=>
{
user
.
areIncludesCreatedOnAfterCreate
=
user
.
Tasks
&&
user
.
Tasks
.
every
(
task
=>
{
return
!!
task
.
id
;
});
});
}
}
});
const
Task
=
this
.
sequelize
.
define
(
'Task'
,
{
title
:
DataTypes
.
STRING
,
active
:
DataTypes
.
BOOLEAN
},
{
hooks
:
{
afterBulkCreate
(
tasks
,
options
)
{
tasks
.
forEach
(
task
=>
{
task
.
createOptions
=
options
;
});
}
}
});
User
.
belongsToMany
(
Task
,
{
through
:
'user_task'
});
Task
.
belongsToMany
(
User
,
{
through
:
'user_task'
});
return
this
.
sequelize
.
sync
({
force
:
true
}).
then
(()
=>
{
return
User
.
bulkCreate
([{
username
:
'John'
,
Tasks
:
[
{
title
:
'Get rich'
,
active
:
true
},
{
title
:
'Die trying'
,
active
:
false
}
]
},
{
username
:
'Jack'
,
Tasks
:
[
{
title
:
'Prepare sandwich'
,
active
:
true
},
{
title
:
'Each sandwich'
,
active
:
false
}
]
}],
{
include
:
[{
model
:
Task
,
myOption
:
'option'
}]
}).
then
(
savedUsers
=>
{
expect
(
savedUsers
[
0
].
areIncludesCreatedOnAfterCreate
).
to
.
be
.
true
;
expect
(
savedUsers
[
0
].
Tasks
[
0
].
createOptions
.
myOption
).
to
.
be
.
equal
(
'option'
);
expect
(
savedUsers
[
0
].
Tasks
[
1
].
createOptions
.
myOption
).
to
.
be
.
equal
(
'option'
);
expect
(
savedUsers
[
1
].
areIncludesCreatedOnAfterCreate
).
to
.
be
.
true
;
expect
(
savedUsers
[
1
].
Tasks
[
0
].
createOptions
.
myOption
).
to
.
be
.
equal
(
'option'
);
expect
(
savedUsers
[
1
].
Tasks
[
1
].
createOptions
.
myOption
).
to
.
be
.
equal
(
'option'
);
return
Promise
.
all
([
User
.
findOne
({
where
:
{
id
:
savedUsers
[
0
].
id
},
include
:
[
Task
]
}),
User
.
findOne
({
where
:
{
id
:
savedUsers
[
1
].
id
},
include
:
[
Task
]
})
]).
then
(
persistedUsers
=>
{
expect
(
persistedUsers
[
0
].
Tasks
).
to
.
be
.
ok
;
expect
(
persistedUsers
[
0
].
Tasks
.
length
).
to
.
equal
(
2
);
expect
(
persistedUsers
[
1
].
Tasks
).
to
.
be
.
ok
;
expect
(
persistedUsers
[
1
].
Tasks
.
length
).
to
.
equal
(
2
);
});
});
});
});
it
(
'should bulkCreate data for polymorphic BelongsToMany relations'
,
function
()
{
const
Post
=
this
.
sequelize
.
define
(
'Post'
,
{
title
:
DataTypes
.
STRING
},
{
tableName
:
'posts'
,
underscored
:
true
});
const
Tag
=
this
.
sequelize
.
define
(
'Tag'
,
{
name
:
DataTypes
.
STRING
},
{
tableName
:
'tags'
,
underscored
:
true
});
const
ItemTag
=
this
.
sequelize
.
define
(
'ItemTag'
,
{
tag_id
:
{
type
:
DataTypes
.
INTEGER
,
references
:
{
model
:
'tags'
,
key
:
'id'
}
},
taggable_id
:
{
type
:
DataTypes
.
INTEGER
,
references
:
null
},
taggable
:
{
type
:
DataTypes
.
STRING
}
},
{
tableName
:
'item_tag'
,
underscored
:
true
});
Post
.
belongsToMany
(
Tag
,
{
as
:
'tags'
,
foreignKey
:
'taggable_id'
,
constraints
:
false
,
through
:
{
model
:
ItemTag
,
scope
:
{
taggable
:
'post'
}
}
});
Tag
.
belongsToMany
(
Post
,
{
as
:
'posts'
,
foreignKey
:
'tag_id'
,
constraints
:
false
,
through
:
{
model
:
ItemTag
,
scope
:
{
taggable
:
'post'
}
}
});
return
this
.
sequelize
.
sync
({
force
:
true
}).
then
(()
=>
{
return
Post
.
bulkCreate
([{
title
:
'Polymorphic Associations'
,
tags
:
[
{
name
:
'polymorphic'
},
{
name
:
'associations'
}
]
},
{
title
:
'Second Polymorphic Associations'
,
tags
:
[
{
name
:
'second polymorphic'
},
{
name
:
'second associations'
}
]
}],
{
include
:
[{
model
:
Tag
,
as
:
'tags'
,
through
:
{
model
:
ItemTag
}
}]
}
);
}).
then
(
savedPosts
=>
{
// The saved post should include the two tags
expect
(
savedPosts
[
0
].
tags
.
length
).
to
.
equal
(
2
);
expect
(
savedPosts
[
1
].
tags
.
length
).
to
.
equal
(
2
);
// The saved post should be able to retrieve the two tags
// using the convenience accessor methods
return
Promise
.
all
([
savedPosts
[
0
].
getTags
(),
savedPosts
[
1
].
getTags
()
]);
}).
then
(
savedTagGroups
=>
{
// All nested tags should be returned
expect
(
savedTagGroups
[
0
].
length
).
to
.
equal
(
2
);
expect
(
savedTagGroups
[
1
].
length
).
to
.
equal
(
2
);
}).
then
(()
=>
{
return
ItemTag
.
findAll
();
}).
then
(
itemTags
=>
{
// Four "through" models should be created
expect
(
itemTags
.
length
).
to
.
equal
(
4
);
// And their polymorphic field should be correctly set to 'post'
expect
(
itemTags
[
0
].
taggable
).
to
.
equal
(
'post'
);
expect
(
itemTags
[
1
].
taggable
).
to
.
equal
(
'post'
);
expect
(
itemTags
[
2
].
taggable
).
to
.
equal
(
'post'
);
expect
(
itemTags
[
3
].
taggable
).
to
.
equal
(
'post'
);
});
});
it
(
'should bulkCreate data for BelongsToMany relations with alias'
,
function
()
{
const
User
=
this
.
sequelize
.
define
(
'User'
,
{
username
:
DataTypes
.
STRING
});
const
Task
=
this
.
sequelize
.
define
(
'Task'
,
{
title
:
DataTypes
.
STRING
,
active
:
DataTypes
.
BOOLEAN
});
const
Jobs
=
User
.
belongsToMany
(
Task
,
{
through
:
'user_job'
,
as
:
'jobs'
});
Task
.
belongsToMany
(
User
,
{
through
:
'user_job'
});
return
this
.
sequelize
.
sync
({
force
:
true
}).
then
(()
=>
{
return
User
.
bulkCreate
([{
username
:
'John'
,
jobs
:
[
{
title
:
'Get rich'
,
active
:
true
},
{
title
:
'Die trying'
,
active
:
false
}
]
},
{
username
:
'Jack'
,
jobs
:
[
{
title
:
'Prepare sandwich'
,
active
:
true
},
{
title
:
'Eat sandwich'
,
active
:
false
}
]
}],
{
include
:
[
Jobs
]
}).
then
(
savedUsers
=>
{
return
Promise
.
all
([
User
.
findOne
({
where
:
{
id
:
savedUsers
[
0
].
id
},
include
:
[
Jobs
]
}),
User
.
findOne
({
where
:
{
id
:
savedUsers
[
1
].
id
},
include
:
[
Jobs
]
})
]).
then
(
persistedUsers
=>
{
expect
(
persistedUsers
[
0
].
jobs
).
to
.
be
.
ok
;
expect
(
persistedUsers
[
0
].
jobs
.
length
).
to
.
equal
(
2
);
expect
(
persistedUsers
[
1
].
jobs
).
to
.
be
.
ok
;
expect
(
persistedUsers
[
1
].
jobs
.
length
).
to
.
equal
(
2
);
});
});
});
});
});
});
});
types/lib/model.d.ts
View file @
4f09899
...
@@ -741,6 +741,11 @@ export interface BulkCreateOptions extends Logging, Transactionable {
...
@@ -741,6 +741,11 @@ export interface BulkCreateOptions extends Logging, Transactionable {
updateOnDuplicate
?:
string
[];
updateOnDuplicate
?:
string
[];
/**
/**
* Include options. See `find` for details
*/
include
?:
Includeable
[];
/**
* Return all columns or only the specified columns for the affected rows (only for postgres)
* Return all columns or only the specified columns for the affected rows (only for postgres)
*/
*/
returning
?:
boolean
|
string
[];
returning
?:
boolean
|
string
[];
...
...
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