Fix: change strategy from add to set for updateAssociations (#33)

* test: add belongsTo case

* fix: change update strategy from add to set
This commit is contained in:
Junyi 2020-12-06 14:28:23 +08:00 committed by GitHub
parent 0a0d09119b
commit 05f815655f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 83 additions and 45 deletions

View File

@ -237,6 +237,22 @@ describe('model', () => {
const authorizedPost = await Post.findByPk(post.id); const authorizedPost = await Post.findByPk(post.id);
expect(authorizedPost.user_id).toBe(user.id); expect(authorizedPost.user_id).toBe(user.id);
}); });
it('update with exist model', async () => {
const [User, Post] = db.getModels(['users', 'posts']);
const user1 = await User.create();
const user2 = await Post.create();
const post = await Post.create();
await post.updateAssociations({
user: user1.id
});
await post.updateAssociations({
user: user2.id
});
const authorizedPost = await Post.findByPk(post.id);
expect(authorizedPost.user_id).toBe(user2.id);
});
}); });
describe('hasMany', () => { describe('hasMany', () => {
@ -368,6 +384,26 @@ describe('model', () => {
{ id: 4, content: 'content4' } { id: 4, content: 'content4' }
]); ]);
}); });
it('update with different primaryKey/row/object', async () => {
const [Post, Comment] = db.getModels(['posts', 'comments']);
const post = await Post.create();
const comments = await Comment.bulkCreate([{}, {}, {}, {}]);
await post.updateAssociations({
comments
});
await post.updateAssociations({
comments: comments
.filter(({ id }) => Boolean(id % 2))
.concat(...[await Comment.create()])
});
const postComments = await Comment.findAll({
where: { post_id: post.id },
attributes: ['id']
});
expect(postComments.map(({ id }) => id)).toEqual([1,3,5]);
});
}); });
describe('belongsToMany', () => { describe('belongsToMany', () => {
@ -410,6 +446,23 @@ describe('model', () => {
]); ]);
}); });
it('update with exist rows/primaryKeys and new objects', async () => {
const [Post, Tag, PostTag] = db.getModels(['posts', 'tags', 'posts_tags']);
const post = await Post.create();
const tags = await Tag.bulkCreate([{}, {}, {}, {}]);
await post.updateAssociations({
tags: tags.map(item => item.id)
});
await post.updateAssociations({
tags: tags.filter(({ id }) => Boolean(id % 2)).concat(await Tag.create({}))
});
const tagged = await PostTag.findAll({
where: { post_id: post.id },
attributes: ['tag_id']
});
expect(tagged.map(({ tag_id }) => tag_id)).toEqual([1,3,5]);
});
it('update other with exist rows/primaryKeys', async () => { it('update other with exist rows/primaryKeys', async () => {
const [Post, Tag, PostTag] = db.getModels(['posts', 'tags', 'posts_tags']); const [Post, Tag, PostTag] = db.getModels(['posts', 'tags', 'posts_tags']);
const post = await Post.create(); const post = await Post.create();

View File

@ -320,8 +320,8 @@ export abstract class Model extends SequelizeModel {
// 准备设置的关联主键 // 准备设置的关联主键
const toSetPks = new Set(); const toSetPks = new Set();
const toSetUks = new Set(); const toSetUks = new Set();
// 筛选后准备添加的关联主键 // 筛选后准备设置的关联主键
const toAddItems = new Set(); const toSetItems = new Set();
// 准备添加的关联对象 // 准备添加的关联对象
const toUpsertObjects = []; const toUpsertObjects = [];
@ -358,9 +358,10 @@ export abstract class Model extends SequelizeModel {
}); });
/* 仅传关联键处理开始 */ /* 仅传关联键处理开始 */
// 查找已存在的关联数据 // 查找已存在的数据
const byPkExistItems = toSetPks.size ? await this[accessors.get]({ const byPkExistItems = toSetPks.size ? await Target.findAll({
...opts, ...opts,
// @ts-ignore
where: { where: {
[targetPk]: { [targetPk]: {
[Op.in]: Array.from(toSetPks) [Op.in]: Array.from(toSetPks)
@ -368,36 +369,11 @@ export abstract class Model extends SequelizeModel {
}, },
attributes: [targetPk] attributes: [targetPk]
}) : []; }) : [];
const pkExistItems = new Map();
byPkExistItems.forEach(item => { byPkExistItems.forEach(item => {
pkExistItems.set(item[targetPk], item); toSetItems.add(item);
}); });
for (const key of toSetPks) {
if (!pkExistItems.has(key)) {
toAddItems.add(key);
}
}
const byUkExistItems = await this[accessors.get]({ const byUkExistItems = toSetUks.size ? await Target.findAll({
...opts,
where: {
[targetKey]: {
[Op.in]: Array.from(toSetUks)
}
},
attributes: [targetPk, targetKey],
transaction
});
const ukExistItems = new Map();
byUkExistItems.forEach(item => {
ukExistItems.set(item[targetKey], item);
});
for (const key of toSetUks) {
if (ukExistItems.has(key)) {
toSetUks.delete(key);
}
}
const byUkItems = toSetUks.size ? await Target.findAll({
...opts, ...opts,
// @ts-ignore // @ts-ignore
where: { where: {
@ -407,11 +383,12 @@ export abstract class Model extends SequelizeModel {
}, },
attributes: [targetPk, targetKey] attributes: [targetPk, targetKey]
}) : []; }) : [];
byUkItems.forEach(item => { byUkExistItems.forEach(item => {
toAddItems.add(item); toSetItems.add(item);
}); });
/* 仅传关联键处理结束 */ /* 仅传关联键处理结束 */
const belongsToManyList = [];
/* 值为对象处理开始 */ /* 值为对象处理开始 */
for (const item of toUpsertObjects) { for (const item of toUpsertObjects) {
let target; let target;
@ -431,10 +408,26 @@ export abstract class Model extends SequelizeModel {
} }
if (association instanceof BelongsToMany) { if (association instanceof BelongsToMany) {
// TODO(optimize): 这里暂时未能批量执行 belongsToManyList.push({
await this[accessors.add](target, opts); item,
const ThroughModel = association.getThroughModel(); target
const throughName = association.getThroughName(); });
}
toSetItems.add(target);
await target.updateAssociations(item, opts);
}
/* 值为对象处理结束 */
// 添加所有计算后的关联
await this[accessors.set](Array.from(toSetItems), opts);
// 后处理 belongsToMany 的更新内容
if (belongsToManyList.length) {
const ThroughModel = (association as BelongsToMany).getThroughModel();
const throughName = (association as BelongsToMany).getThroughName();
for (const { item, target } of belongsToManyList) {
const throughValues = item[throughName]; const throughValues = item[throughName];
if (typeof throughValues === 'object') { if (typeof throughValues === 'object') {
const { foreignKey, sourceKey, otherKey } = association.options; const { foreignKey, sourceKey, otherKey } = association.options;
@ -448,16 +441,8 @@ export abstract class Model extends SequelizeModel {
await through.update(throughValues, opts); await through.update(throughValues, opts);
await through.updateAssociations(throughValues, opts); await through.updateAssociations(throughValues, opts);
} }
} else {
toAddItems.add(target);
} }
await target.updateAssociations(item, opts);
} }
/* 值为对象处理结束 */
// 添加所有计算后的关联
await this[accessors.add](Array.from(toAddItems), opts);
if (!options.transaction) { if (!options.transaction) {
await transaction.commit(); await transaction.commit();