feat: filter by target key (#146)
* feat: filter by target key * fix: repository test * change type name * chore: test * change PrimaryKey type to TargetKey * rename filterTargetKey * rename variables * change option parser constructor * add option parser targetKey * change filter parser constructor * fix: custom model Co-authored-by: chenos <chenlinxh@gmail.com>
This commit is contained in:
parent
79ba391aee
commit
2bf09bf9bb
@ -1,5 +1,6 @@
|
|||||||
import { mockDatabase } from './index';
|
import { mockDatabase } from './index';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
|
import { Model } from '..';
|
||||||
|
|
||||||
describe('database', () => {
|
describe('database', () => {
|
||||||
test('import', async () => {
|
test('import', async () => {
|
||||||
@ -165,4 +166,30 @@ describe('database', () => {
|
|||||||
|
|
||||||
expect(listener).toHaveBeenCalled();
|
expect(listener).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('custom model', async () => {
|
||||||
|
class CustomModel extends Model {
|
||||||
|
customMethod() {
|
||||||
|
this.setDataValue('abc', 'abc');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const db = mockDatabase();
|
||||||
|
|
||||||
|
db.registerModels({
|
||||||
|
CustomModel,
|
||||||
|
});
|
||||||
|
|
||||||
|
const Test = db.collection({
|
||||||
|
name: 'tests',
|
||||||
|
model: 'CustomModel',
|
||||||
|
});
|
||||||
|
|
||||||
|
await Test.sync();
|
||||||
|
|
||||||
|
const test = await Test.model.create<any>();
|
||||||
|
test.customMethod();
|
||||||
|
expect(test.get('abc')).toBe('abc');
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -87,7 +87,7 @@ describe('context field', () => {
|
|||||||
});
|
});
|
||||||
expect(t1.get('clientIp')).toBe('11.22.33.44');
|
expect(t1.get('clientIp')).toBe('11.22.33.44');
|
||||||
const [t2] = await Test.repository.update({
|
const [t2] = await Test.repository.update({
|
||||||
filterByPk: t1.get('id') as any,
|
filterByTk: t1.get('id') as any,
|
||||||
values: {},
|
values: {},
|
||||||
context: {
|
context: {
|
||||||
request: {
|
request: {
|
||||||
@ -124,7 +124,7 @@ describe('context field', () => {
|
|||||||
});
|
});
|
||||||
expect(t1.get('clientIp')).toBe('11.22.33.44');
|
expect(t1.get('clientIp')).toBe('11.22.33.44');
|
||||||
const [t2] = await Test.repository.update({
|
const [t2] = await Test.repository.update({
|
||||||
filterByPk: t1.get('id') as any,
|
filterByTk: t1.get('id') as any,
|
||||||
values: {},
|
values: {},
|
||||||
context: {
|
context: {
|
||||||
request: {
|
request: {
|
||||||
|
@ -12,9 +12,14 @@ test('filter item by string', async () => {
|
|||||||
|
|
||||||
await database.sync();
|
await database.sync();
|
||||||
|
|
||||||
const filterParser = new FilterParser(UserCollection.model, UserCollection.context.database, {
|
const filterParser = new FilterParser(
|
||||||
name: 'hello',
|
{
|
||||||
});
|
name: 'hello',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
collection: UserCollection,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
const filterParams = filterParser.toSequelizeParams();
|
const filterParams = filterParser.toSequelizeParams();
|
||||||
|
|
||||||
@ -74,11 +79,9 @@ describe('filter by related', () => {
|
|||||||
'posts.title.$iLike': '%hello%',
|
'posts.title.$iLike': '%hello%',
|
||||||
};
|
};
|
||||||
|
|
||||||
const filterParser = new FilterParser(
|
const filterParser = new FilterParser(filter, {
|
||||||
db.getCollection('users').model,
|
collection: db.getCollection('users'),
|
||||||
db.getCollection('users').context.database,
|
});
|
||||||
filter,
|
|
||||||
);
|
|
||||||
|
|
||||||
const filterParams = filterParser.toSequelizeParams();
|
const filterParams = filterParser.toSequelizeParams();
|
||||||
|
|
||||||
@ -91,11 +94,9 @@ describe('filter by related', () => {
|
|||||||
'posts.comments.content.$iLike': '%hello%',
|
'posts.comments.content.$iLike': '%hello%',
|
||||||
};
|
};
|
||||||
|
|
||||||
const filterParser = new FilterParser(
|
const filterParser = new FilterParser(filter, {
|
||||||
db.getCollection('users').model,
|
collection: db.getCollection('users'),
|
||||||
db.getCollection('users').context.database,
|
});
|
||||||
filter,
|
|
||||||
);
|
|
||||||
|
|
||||||
const filterParams = filterParser.toSequelizeParams();
|
const filterParams = filterParser.toSequelizeParams();
|
||||||
|
|
||||||
|
@ -13,9 +13,7 @@ describe('array field operator', function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
db = mockDatabase({
|
db = mockDatabase({});
|
||||||
logging: console.log,
|
|
||||||
});
|
|
||||||
|
|
||||||
Test = db.collection({
|
Test = db.collection({
|
||||||
name: 'test',
|
name: 'test',
|
||||||
@ -66,7 +64,7 @@ describe('array field operator', function () {
|
|||||||
expect(result.get('id')).toEqual(p1.get('id'));
|
expect(result.get('id')).toEqual(p1.get('id'));
|
||||||
|
|
||||||
await Post.repository.update({
|
await Post.repository.update({
|
||||||
filterByPk: <any>p1.get('id'),
|
filterByTk: <any>p1.get('id'),
|
||||||
values: {
|
values: {
|
||||||
tags: ['t3', 't2'],
|
tags: ['t3', 't2'],
|
||||||
},
|
},
|
||||||
|
@ -60,7 +60,10 @@ describe('option parser', () => {
|
|||||||
fields: ['id', 'name', 'tags.id', 'tags.name'],
|
fields: ['id', 'name', 'tags.id', 'tags.name'],
|
||||||
};
|
};
|
||||||
|
|
||||||
const parser = new OptionsParser(Post.model, Post.context.database, options);
|
const parser = new OptionsParser(options, {
|
||||||
|
collection: Post,
|
||||||
|
});
|
||||||
|
|
||||||
const params = parser.toSequelizeParams();
|
const params = parser.toSequelizeParams();
|
||||||
|
|
||||||
expect(params).toEqual({
|
expect(params).toEqual({
|
||||||
@ -78,7 +81,9 @@ describe('option parser', () => {
|
|||||||
sort: ['id'],
|
sort: ['id'],
|
||||||
};
|
};
|
||||||
|
|
||||||
let parser = new OptionsParser(User.model, User.context.database, options);
|
let parser = new OptionsParser(options, {
|
||||||
|
collection: User,
|
||||||
|
});
|
||||||
let params = parser.toSequelizeParams();
|
let params = parser.toSequelizeParams();
|
||||||
expect(params['order']).toEqual([['id', 'ASC']]);
|
expect(params['order']).toEqual([['id', 'ASC']]);
|
||||||
|
|
||||||
@ -86,7 +91,9 @@ describe('option parser', () => {
|
|||||||
sort: ['id', '-posts.title', 'posts.comments.createdAt'],
|
sort: ['id', '-posts.title', 'posts.comments.createdAt'],
|
||||||
};
|
};
|
||||||
|
|
||||||
parser = new OptionsParser(User.model, User.context.database, options);
|
parser = new OptionsParser(options, {
|
||||||
|
collection: User,
|
||||||
|
});
|
||||||
params = parser.toSequelizeParams();
|
params = parser.toSequelizeParams();
|
||||||
expect(params['order']).toEqual([
|
expect(params['order']).toEqual([
|
||||||
['id', 'ASC'],
|
['id', 'ASC'],
|
||||||
@ -100,7 +107,9 @@ describe('option parser', () => {
|
|||||||
fields: ['id', 'posts'],
|
fields: ['id', 'posts'],
|
||||||
};
|
};
|
||||||
// 转换为 attributes: ['id'], include: [{association: 'posts'}]
|
// 转换为 attributes: ['id'], include: [{association: 'posts'}]
|
||||||
let parser = new OptionsParser(User.model, User.context.database, options);
|
let parser = new OptionsParser(options, {
|
||||||
|
collection: User,
|
||||||
|
});
|
||||||
let params = parser.toSequelizeParams();
|
let params = parser.toSequelizeParams();
|
||||||
|
|
||||||
expect(params['attributes']).toContain('id');
|
expect(params['attributes']).toContain('id');
|
||||||
@ -111,7 +120,9 @@ describe('option parser', () => {
|
|||||||
appends: ['posts'],
|
appends: ['posts'],
|
||||||
};
|
};
|
||||||
|
|
||||||
parser = new OptionsParser(User.model, User.context.database, options);
|
parser = new OptionsParser(options, {
|
||||||
|
collection: User,
|
||||||
|
});
|
||||||
params = parser.toSequelizeParams();
|
params = parser.toSequelizeParams();
|
||||||
expect(params['attributes']['include']).toEqual([]);
|
expect(params['attributes']['include']).toEqual([]);
|
||||||
expect(params['include'][0]['association']).toEqual('posts');
|
expect(params['include'][0]['association']).toEqual('posts');
|
||||||
@ -121,7 +132,9 @@ describe('option parser', () => {
|
|||||||
fields: ['id', 'posts.title'],
|
fields: ['id', 'posts.title'],
|
||||||
};
|
};
|
||||||
|
|
||||||
parser = new OptionsParser(User.model, User.context.database, options);
|
parser = new OptionsParser(options, {
|
||||||
|
collection: User,
|
||||||
|
});
|
||||||
params = parser.toSequelizeParams();
|
params = parser.toSequelizeParams();
|
||||||
expect(params['attributes']).toContain('id');
|
expect(params['attributes']).toContain('id');
|
||||||
expect(params['include'][0]['association']).toEqual('posts');
|
expect(params['include'][0]['association']).toEqual('posts');
|
||||||
@ -132,7 +145,9 @@ describe('option parser', () => {
|
|||||||
fields: ['id', 'posts', 'posts.comments.content'],
|
fields: ['id', 'posts', 'posts.comments.content'],
|
||||||
};
|
};
|
||||||
|
|
||||||
parser = new OptionsParser(User.model, User.context.database, options);
|
parser = new OptionsParser(options, {
|
||||||
|
collection: User,
|
||||||
|
});
|
||||||
params = parser.toSequelizeParams();
|
params = parser.toSequelizeParams();
|
||||||
expect(params['attributes']).toContain('id');
|
expect(params['attributes']).toContain('id');
|
||||||
expect(params['include'][0]['association']).toEqual('posts');
|
expect(params['include'][0]['association']).toEqual('posts');
|
||||||
@ -143,7 +158,9 @@ describe('option parser', () => {
|
|||||||
options = {
|
options = {
|
||||||
except: ['id'],
|
except: ['id'],
|
||||||
};
|
};
|
||||||
parser = new OptionsParser(User.model, User.context.database, options);
|
parser = new OptionsParser(options, {
|
||||||
|
collection: User,
|
||||||
|
});
|
||||||
params = parser.toSequelizeParams();
|
params = parser.toSequelizeParams();
|
||||||
expect(params['attributes']['exclude']).toContain('id');
|
expect(params['attributes']['exclude']).toContain('id');
|
||||||
|
|
||||||
@ -153,7 +170,9 @@ describe('option parser', () => {
|
|||||||
except: ['posts.id'],
|
except: ['posts.id'],
|
||||||
};
|
};
|
||||||
|
|
||||||
parser = new OptionsParser(User.model, User.context.database, options);
|
parser = new OptionsParser(options, {
|
||||||
|
collection: User,
|
||||||
|
});
|
||||||
params = parser.toSequelizeParams();
|
params = parser.toSequelizeParams();
|
||||||
|
|
||||||
expect(params['include'][0]['attributes']['exclude']).toContain('id');
|
expect(params['include'][0]['attributes']['exclude']).toContain('id');
|
||||||
|
@ -1,6 +1,113 @@
|
|||||||
import { mockDatabase } from '../index';
|
import { mockDatabase } from '../index';
|
||||||
import { BelongsToManyRepository } from '../../relation-repository/belongs-to-many-repository';
|
import { BelongsToManyRepository } from '../../relation-repository/belongs-to-many-repository';
|
||||||
import Database from '../../database';
|
import Database from '../../database';
|
||||||
|
import { Collection, HasManyRepository } from '@nocobase/database';
|
||||||
|
|
||||||
|
describe('belongs to many with target key', function () {
|
||||||
|
let db: Database;
|
||||||
|
let Tag: Collection;
|
||||||
|
let Post: Collection;
|
||||||
|
beforeEach(async () => {
|
||||||
|
db = mockDatabase();
|
||||||
|
|
||||||
|
Post = db.collection({
|
||||||
|
name: 'posts',
|
||||||
|
filterTargetKey: 'title',
|
||||||
|
autoGenId: false,
|
||||||
|
fields: [
|
||||||
|
{ type: 'string', name: 'title', primaryKey: true },
|
||||||
|
{
|
||||||
|
type: 'belongsToMany',
|
||||||
|
name: 'tags',
|
||||||
|
sourceKey: 'title',
|
||||||
|
foreignKey: 'postTitle',
|
||||||
|
targetKey: 'name',
|
||||||
|
otherKey: 'tagName',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
Tag = db.collection({
|
||||||
|
name: 'tags',
|
||||||
|
filterTargetKey: 'name',
|
||||||
|
autoGenId: false,
|
||||||
|
fields: [
|
||||||
|
{ type: 'string', name: 'name', primaryKey: true },
|
||||||
|
{ type: 'string', name: 'status' },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
await db.sync({ force: true });
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(async () => {
|
||||||
|
await db.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('destroy by target key', async () => {
|
||||||
|
const t1 = await Tag.repository.create({
|
||||||
|
values: {
|
||||||
|
name: 't1',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const t2 = await Tag.repository.create({
|
||||||
|
values: {
|
||||||
|
name: 't2',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const p1 = await Post.repository.create({
|
||||||
|
values: { title: 'p1' },
|
||||||
|
});
|
||||||
|
|
||||||
|
const PostTagRepository = new BelongsToManyRepository(Post, 'tags', p1.get('title') as string);
|
||||||
|
|
||||||
|
await PostTagRepository.set([t1.get('name') as string, t2.get('name')]);
|
||||||
|
|
||||||
|
await PostTagRepository.destroy();
|
||||||
|
|
||||||
|
const [_, count] = await PostTagRepository.findAndCount();
|
||||||
|
expect(count).toEqual(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('destroy with target key and filter', async () => {
|
||||||
|
let t1 = await Tag.repository.create({
|
||||||
|
values: {
|
||||||
|
name: 't1',
|
||||||
|
status: 'published',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const t2 = await Tag.repository.create({
|
||||||
|
values: {
|
||||||
|
name: 't2',
|
||||||
|
status: 'draft',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const p1 = await Post.repository.create({
|
||||||
|
values: { title: 'p1' },
|
||||||
|
});
|
||||||
|
|
||||||
|
const PostTagRepository = new BelongsToManyRepository(Post, 'tags', p1.get('title') as string);
|
||||||
|
|
||||||
|
await PostTagRepository.set([t1.get('name') as string, t2.get('name') as string]);
|
||||||
|
|
||||||
|
let [_, count] = await PostTagRepository.findAndCount();
|
||||||
|
expect(count).toEqual(2);
|
||||||
|
|
||||||
|
await PostTagRepository.destroy({
|
||||||
|
filterByTk: t1.get('name') as string,
|
||||||
|
filter: {
|
||||||
|
status: 'draft',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
[_, count] = await PostTagRepository.findAndCount();
|
||||||
|
expect(count).toEqual(2);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('belongs to many', () => {
|
describe('belongs to many', () => {
|
||||||
let db: Database;
|
let db: Database;
|
||||||
@ -51,6 +158,7 @@ describe('belongs to many', () => {
|
|||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await db.sequelize.getQueryInterface().dropAllTables();
|
||||||
await db.sync({ force: true });
|
await db.sync({ force: true });
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -384,7 +492,7 @@ describe('belongs to many', () => {
|
|||||||
await PostTagRepository.set([t1.id, t2.id]);
|
await PostTagRepository.set([t1.id, t2.id]);
|
||||||
|
|
||||||
const findByPkResult = await PostTagRepository.findOne({
|
const findByPkResult = await PostTagRepository.findOne({
|
||||||
filterByPk: t2.id,
|
filterByTk: t2.id,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(findByPkResult.name).toEqual('t2');
|
expect(findByPkResult.name).toEqual('t2');
|
||||||
@ -480,7 +588,7 @@ describe('belongs to many', () => {
|
|||||||
expect(count).toEqual(2);
|
expect(count).toEqual(2);
|
||||||
|
|
||||||
await PostTagRepository.destroy({
|
await PostTagRepository.destroy({
|
||||||
filterByPk: t1.get('id') as number,
|
filterByTk: t1.get('id') as number,
|
||||||
filter: {
|
filter: {
|
||||||
status: 'draft',
|
status: 'draft',
|
||||||
},
|
},
|
||||||
@ -511,10 +619,11 @@ describe('belongs to many', () => {
|
|||||||
|
|
||||||
await PostTagRepository.set([t1.id, t2.id]);
|
await PostTagRepository.set([t1.id, t2.id]);
|
||||||
|
|
||||||
|
expect(await PostTagRepository.count()).toEqual(2);
|
||||||
|
|
||||||
await PostTagRepository.destroy(t2.id);
|
await PostTagRepository.destroy(t2.id);
|
||||||
|
|
||||||
const result = await PostTagRepository.findAndCount();
|
expect(await PostTagRepository.count()).toEqual(1);
|
||||||
expect(result[1]).toEqual(1);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('transaction', async () => {
|
test('transaction', async () => {
|
||||||
|
@ -2,6 +2,116 @@ import { mockDatabase } from '../index';
|
|||||||
import { HasManyRepository } from '../../relation-repository/hasmany-repository';
|
import { HasManyRepository } from '../../relation-repository/hasmany-repository';
|
||||||
import { BelongsToManyRepository } from '../../relation-repository/belongs-to-many-repository';
|
import { BelongsToManyRepository } from '../../relation-repository/belongs-to-many-repository';
|
||||||
|
|
||||||
|
describe('has many with target key', function () {
|
||||||
|
test('target key with filterTargetKey', async () => {
|
||||||
|
const db = mockDatabase();
|
||||||
|
const User = db.collection<{ id: number; name: string }, { name: string }>({
|
||||||
|
name: 'users',
|
||||||
|
filterTargetKey: 'name',
|
||||||
|
autoGenId: false,
|
||||||
|
fields: [
|
||||||
|
{ type: 'string', name: 'name', unique: true },
|
||||||
|
{ type: 'integer', name: 'age' },
|
||||||
|
{ type: 'hasMany', name: 'posts', sourceKey: 'name', foreignKey: 'userName', targetKey: 'title' },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const Post = db.collection({
|
||||||
|
name: 'posts',
|
||||||
|
filterTargetKey: 'title',
|
||||||
|
autoGenId: false,
|
||||||
|
fields: [
|
||||||
|
{ type: 'string', name: 'title', unique: true },
|
||||||
|
{ type: 'string', name: 'status' },
|
||||||
|
{
|
||||||
|
type: 'belongsTo',
|
||||||
|
name: 'user',
|
||||||
|
targetKey: 'name',
|
||||||
|
foreignKey: 'userName',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('destroy by target key and filter', async () => {
|
||||||
|
const db = mockDatabase();
|
||||||
|
const User = db.collection<{ id: number; name: string }, { name: string }>({
|
||||||
|
name: 'users',
|
||||||
|
filterTargetKey: 'name',
|
||||||
|
autoGenId: false,
|
||||||
|
fields: [
|
||||||
|
{ type: 'string', name: 'name', unique: true },
|
||||||
|
{ type: 'integer', name: 'age' },
|
||||||
|
{ type: 'hasMany', name: 'posts', sourceKey: 'name', foreignKey: 'userName', targetKey: 'title' },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const Post = db.collection({
|
||||||
|
name: 'posts',
|
||||||
|
filterTargetKey: 'title',
|
||||||
|
autoGenId: false,
|
||||||
|
fields: [
|
||||||
|
{ type: 'string', name: 'title', unique: true },
|
||||||
|
{ type: 'string', name: 'status' },
|
||||||
|
{
|
||||||
|
type: 'belongsTo',
|
||||||
|
name: 'user',
|
||||||
|
targetKey: 'name',
|
||||||
|
foreignKey: 'userName',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
await db.sync({ force: true });
|
||||||
|
|
||||||
|
const u1 = await User.repository.create({
|
||||||
|
values: { name: 'u1' },
|
||||||
|
});
|
||||||
|
|
||||||
|
const UserPostRepository = new HasManyRepository(User, 'posts', u1.get('name') as string);
|
||||||
|
|
||||||
|
const p1 = await UserPostRepository.create({
|
||||||
|
values: {
|
||||||
|
title: 't1',
|
||||||
|
status: 'published',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const p2 = await UserPostRepository.create({
|
||||||
|
values: {
|
||||||
|
title: 't2',
|
||||||
|
status: 'draft',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await UserPostRepository.destroy({
|
||||||
|
filterByTk: p1.title,
|
||||||
|
filter: { status: 'draft' },
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(await UserPostRepository.count()).toEqual(2);
|
||||||
|
|
||||||
|
await UserPostRepository.destroy({
|
||||||
|
filterByTk: p1.title,
|
||||||
|
filter: {
|
||||||
|
status: 'published',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(
|
||||||
|
await UserPostRepository.findOne({
|
||||||
|
filterByTk: p1.title,
|
||||||
|
}),
|
||||||
|
).toBeNull();
|
||||||
|
|
||||||
|
expect(
|
||||||
|
await UserPostRepository.findOne({
|
||||||
|
filterByTk: p2.id,
|
||||||
|
}),
|
||||||
|
).not.toBeNull();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('has many repository', () => {
|
describe('has many repository', () => {
|
||||||
let db;
|
let db;
|
||||||
let User;
|
let User;
|
||||||
@ -190,7 +300,7 @@ describe('has many repository', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
await UserPostRepository.destroy({
|
await UserPostRepository.destroy({
|
||||||
filterByPk: p1.id,
|
filterByTk: p1.id,
|
||||||
filter: {
|
filter: {
|
||||||
status: 'draft',
|
status: 'draft',
|
||||||
},
|
},
|
||||||
@ -199,7 +309,7 @@ describe('has many repository', () => {
|
|||||||
expect(await UserPostRepository.count()).toEqual(2);
|
expect(await UserPostRepository.count()).toEqual(2);
|
||||||
|
|
||||||
await UserPostRepository.destroy({
|
await UserPostRepository.destroy({
|
||||||
filterByPk: p1.id,
|
filterByTk: p1.id,
|
||||||
filter: {
|
filter: {
|
||||||
status: 'published',
|
status: 'published',
|
||||||
},
|
},
|
||||||
@ -207,13 +317,13 @@ describe('has many repository', () => {
|
|||||||
|
|
||||||
expect(
|
expect(
|
||||||
await UserPostRepository.findOne({
|
await UserPostRepository.findOne({
|
||||||
filterByPk: p1.id,
|
filterByTk: p1.id,
|
||||||
}),
|
}),
|
||||||
).toBeNull();
|
).toBeNull();
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
await UserPostRepository.findOne({
|
await UserPostRepository.findOne({
|
||||||
filterByPk: p2.id,
|
filterByTk: p2.id,
|
||||||
}),
|
}),
|
||||||
).not.toBeNull();
|
).not.toBeNull();
|
||||||
});
|
});
|
||||||
|
@ -2,6 +2,45 @@ import { Collection } from '../collection';
|
|||||||
import { Database } from '../database';
|
import { Database } from '../database';
|
||||||
import { mockDatabase } from './';
|
import { mockDatabase } from './';
|
||||||
|
|
||||||
|
describe('find by targetKey', function () {
|
||||||
|
it('can filter by target key', async () => {
|
||||||
|
const db = mockDatabase({});
|
||||||
|
|
||||||
|
const User = db.collection({
|
||||||
|
name: 'users',
|
||||||
|
filterTargetKey: 'name',
|
||||||
|
autoGenId: false,
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
type: 'string',
|
||||||
|
name: 'name',
|
||||||
|
unique: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
await db.sync();
|
||||||
|
|
||||||
|
await User.repository.create({
|
||||||
|
values: {
|
||||||
|
name: 'user1',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await User.repository.create({
|
||||||
|
values: {
|
||||||
|
name: 'user2',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const user2 = await User.repository.findOne({
|
||||||
|
filterByTk: 'user2',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(user2.get('name')).toEqual('user2');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('repository.find', () => {
|
describe('repository.find', () => {
|
||||||
let db: Database;
|
let db: Database;
|
||||||
let User: Collection;
|
let User: Collection;
|
||||||
@ -116,7 +155,7 @@ describe('repository.find', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const result = await Test.repository.findOne({
|
const result = await Test.repository.findOne({
|
||||||
filterByPk: <number>t1.get('id'),
|
filterByTk: <number>t1.get('id'),
|
||||||
filter: {
|
filter: {
|
||||||
status: 'published',
|
status: 'published',
|
||||||
},
|
},
|
||||||
@ -236,7 +275,7 @@ describe('repository.update', () => {
|
|||||||
name: 'user1',
|
name: 'user1',
|
||||||
});
|
});
|
||||||
await User.repository.update({
|
await User.repository.update({
|
||||||
filterByPk: user.id,
|
filterByTk: user.id,
|
||||||
values: {
|
values: {
|
||||||
name: 'user11',
|
name: 'user11',
|
||||||
posts: [{ name: 'post1' }],
|
posts: [{ name: 'post1' }],
|
||||||
@ -263,28 +302,25 @@ describe('repository.update', () => {
|
|||||||
it('update2', async () => {
|
it('update2', async () => {
|
||||||
const user = await User.model.create<any>({
|
const user = await User.model.create<any>({
|
||||||
name: 'user1',
|
name: 'user1',
|
||||||
posts: [{ name: 'post1' }],
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const user2 = await User.model.create<any>({
|
||||||
|
name: 'user2',
|
||||||
|
});
|
||||||
|
|
||||||
await User.repository.update({
|
await User.repository.update({
|
||||||
filterByPk: user.id,
|
filterByTk: user.id,
|
||||||
values: {
|
values: {
|
||||||
name: 'user11',
|
name: 'user11',
|
||||||
posts: [{ name: 'post1' }],
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const updated = await User.model.findByPk(user.id);
|
const updated = await User.model.findByPk(user.id);
|
||||||
expect(updated).toMatchObject({
|
|
||||||
name: 'user11',
|
expect(updated.get('name')).toEqual('user11');
|
||||||
});
|
|
||||||
const post = await Post.model.findOne({
|
const u2 = await User.model.findByPk(user2.id);
|
||||||
where: {
|
expect(u2.get('name')).toEqual('user2');
|
||||||
name: 'post1',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
expect(post).toMatchObject({
|
|
||||||
name: 'post1',
|
|
||||||
userId: user.id,
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,5 +1,80 @@
|
|||||||
import { mockDatabase } from '../index';
|
import { mockDatabase } from '../index';
|
||||||
import { Collection } from '../../collection';
|
import { Collection } from '../../collection';
|
||||||
|
import { Database } from '@nocobase/database';
|
||||||
|
|
||||||
|
describe('destroy with targetKey', function () {
|
||||||
|
let db: Database;
|
||||||
|
let User: Collection;
|
||||||
|
let u1;
|
||||||
|
let u2;
|
||||||
|
afterEach(async () => {
|
||||||
|
await db.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
db = mockDatabase();
|
||||||
|
|
||||||
|
User = db.collection({
|
||||||
|
name: 'users',
|
||||||
|
autoGenId: false,
|
||||||
|
fields: [
|
||||||
|
{ type: 'string', name: 'name', primaryKey: true },
|
||||||
|
{ type: 'string', name: 'status' },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
await db.sync({
|
||||||
|
force: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
u1 = await User.repository.create({
|
||||||
|
values: {
|
||||||
|
name: 'u1',
|
||||||
|
status: 'published',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
u2 = await User.repository.create({
|
||||||
|
values: {
|
||||||
|
name: 'u2',
|
||||||
|
status: 'draft',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should destroy all', async () => {
|
||||||
|
expect(await User.repository.count()).toEqual(2);
|
||||||
|
await User.repository.destroy({ truncate: true });
|
||||||
|
expect(await User.repository.count()).toEqual(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should destroy by target key', async () => {
|
||||||
|
await User.repository.destroy({
|
||||||
|
filterByTk: 'u2',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(await User.repository.count()).toEqual(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should destroy by target key and filter', async () => {
|
||||||
|
await User.repository.destroy({
|
||||||
|
filterByTk: 'u1',
|
||||||
|
filter: {
|
||||||
|
status: 'draft',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(await User.repository.count()).toEqual(2);
|
||||||
|
|
||||||
|
await User.repository.destroy({
|
||||||
|
filterByTk: 'u2',
|
||||||
|
filter: {
|
||||||
|
status: 'draft',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
expect(await User.repository.count()).toEqual(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('destroy', () => {
|
describe('destroy', () => {
|
||||||
let db;
|
let db;
|
||||||
@ -36,7 +111,7 @@ describe('destroy', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
await Post.repository.destroy({
|
await Post.repository.destroy({
|
||||||
filterByPk: p1.get('id') as number,
|
filterByTk: p1.get('id') as number,
|
||||||
filter: {
|
filter: {
|
||||||
status: 'draft',
|
status: 'draft',
|
||||||
},
|
},
|
||||||
|
@ -15,6 +15,7 @@ export type RepositoryType = typeof Repository;
|
|||||||
export interface CollectionOptions extends Omit<ModelOptions, 'name'> {
|
export interface CollectionOptions extends Omit<ModelOptions, 'name'> {
|
||||||
name: string;
|
name: string;
|
||||||
tableName?: string;
|
tableName?: string;
|
||||||
|
filterTargetKey?: string;
|
||||||
fields?: FieldOptions[];
|
fields?: FieldOptions[];
|
||||||
model?: string | ModelCtor<Model>;
|
model?: string | ModelCtor<Model>;
|
||||||
repository?: string | RepositoryType;
|
repository?: string | RepositoryType;
|
||||||
@ -37,6 +38,10 @@ export class Collection<
|
|||||||
model: ModelCtor<Model<TModelAttributes, TCreationAttributes>>;
|
model: ModelCtor<Model<TModelAttributes, TCreationAttributes>>;
|
||||||
repository: Repository<TModelAttributes, TCreationAttributes>;
|
repository: Repository<TModelAttributes, TCreationAttributes>;
|
||||||
|
|
||||||
|
get filterTargetKey() {
|
||||||
|
return lodash.get(this.options, 'filterTargetKey', this.model.primaryKeyAttribute);
|
||||||
|
}
|
||||||
|
|
||||||
get name() {
|
get name() {
|
||||||
return this.options.name;
|
return this.options.name;
|
||||||
}
|
}
|
||||||
@ -54,7 +59,7 @@ export class Collection<
|
|||||||
private sequelizeModelOptions() {
|
private sequelizeModelOptions() {
|
||||||
const { name, tableName } = this.options;
|
const { name, tableName } = this.options;
|
||||||
return {
|
return {
|
||||||
..._.omit(this.options, ['name', 'fields']),
|
..._.omit(this.options, ['name', 'fields', 'model']),
|
||||||
modelName: name,
|
modelName: name,
|
||||||
sequelize: this.context.database.sequelize,
|
sequelize: this.context.database.sequelize,
|
||||||
tableName: tableName || name,
|
tableName: tableName || name,
|
||||||
|
@ -19,6 +19,10 @@ export abstract class RelationField extends Field {
|
|||||||
return this.options.sourceKey;
|
return this.options.sourceKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get targetKey() {
|
||||||
|
return this.options.targetKey || this.TargetModel.primaryKeyAttribute;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get target model from database by it's name
|
* get target model from database by it's name
|
||||||
* @constructor
|
* @constructor
|
||||||
|
@ -3,20 +3,30 @@ import _ from 'lodash';
|
|||||||
import { flatten, unflatten } from 'flat';
|
import { flatten, unflatten } from 'flat';
|
||||||
import { Database } from './database';
|
import { Database } from './database';
|
||||||
import lodash from 'lodash';
|
import lodash from 'lodash';
|
||||||
|
import { Collection } from './collection';
|
||||||
|
|
||||||
const debug = require('debug')('noco-database');
|
const debug = require('debug')('noco-database');
|
||||||
|
|
||||||
type FilterType = any;
|
type FilterType = any;
|
||||||
|
|
||||||
|
interface FilterParserContext {
|
||||||
|
collection: Collection;
|
||||||
|
}
|
||||||
|
|
||||||
export default class FilterParser {
|
export default class FilterParser {
|
||||||
|
collection: Collection;
|
||||||
database: Database;
|
database: Database;
|
||||||
model: ModelCtor<Model>;
|
model: ModelCtor<Model>;
|
||||||
filter: FilterType;
|
filter: FilterType;
|
||||||
|
context: FilterParserContext;
|
||||||
|
|
||||||
constructor(model: ModelCtor<any>, database: Database, filter: FilterType) {
|
constructor(filter: FilterType, context: FilterParserContext) {
|
||||||
this.model = model;
|
const { collection } = context;
|
||||||
|
this.collection = collection;
|
||||||
|
this.context = context;
|
||||||
|
this.model = collection.model;
|
||||||
this.filter = this.prepareFilter(filter);
|
this.filter = this.prepareFilter(filter);
|
||||||
this.database = database;
|
this.database = collection.context.database;
|
||||||
}
|
}
|
||||||
|
|
||||||
prepareFilter(filter: FilterType) {
|
prepareFilter(filter: FilterType) {
|
||||||
|
@ -2,20 +2,32 @@ import { Appends, Except, FindOptions } from './repository';
|
|||||||
import FilterParser from './filter-parser';
|
import FilterParser from './filter-parser';
|
||||||
import { FindAttributeOptions, ModelCtor, Op } from 'sequelize';
|
import { FindAttributeOptions, ModelCtor, Op } from 'sequelize';
|
||||||
import { Database } from './database';
|
import { Database } from './database';
|
||||||
|
import { Collection } from './collection';
|
||||||
|
|
||||||
const debug = require('debug')('noco-database');
|
const debug = require('debug')('noco-database');
|
||||||
|
|
||||||
|
interface OptionsParserContext {
|
||||||
|
collection: Collection;
|
||||||
|
targetKey?: string;
|
||||||
|
}
|
||||||
|
|
||||||
export class OptionsParser {
|
export class OptionsParser {
|
||||||
options: FindOptions;
|
options: FindOptions;
|
||||||
database: Database;
|
database: Database;
|
||||||
|
collection: Collection;
|
||||||
model: ModelCtor<any>;
|
model: ModelCtor<any>;
|
||||||
filterParser: FilterParser;
|
filterParser: FilterParser;
|
||||||
|
context: OptionsParserContext;
|
||||||
|
|
||||||
constructor(model: ModelCtor<any>, database: Database, options: FindOptions) {
|
constructor(options: FindOptions, context: OptionsParserContext) {
|
||||||
this.model = model;
|
const { collection } = context;
|
||||||
|
|
||||||
|
this.collection = collection;
|
||||||
|
this.model = collection.model;
|
||||||
this.options = options;
|
this.options = options;
|
||||||
this.database = database;
|
this.database = collection.context.database;
|
||||||
this.filterParser = new FilterParser(model, this.database, options?.filter);
|
this.filterParser = new FilterParser(options?.filter, { collection });
|
||||||
|
this.context = context;
|
||||||
}
|
}
|
||||||
|
|
||||||
isAssociation(key: string) {
|
isAssociation(key: string) {
|
||||||
@ -26,27 +38,15 @@ export class OptionsParser {
|
|||||||
return this.isAssociation(path.split('.')[0]);
|
return this.isAssociation(path.split('.')[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
parseFilterByPk() {
|
|
||||||
if (this.options?.filterByPk) {
|
|
||||||
return {
|
|
||||||
where: {
|
|
||||||
[this.model.primaryKeyAttribute]: this.options.filterByPk,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
toSequelizeParams() {
|
toSequelizeParams() {
|
||||||
const queryParams = this.filterParser.toSequelizeParams();
|
const queryParams = this.filterParser.toSequelizeParams();
|
||||||
|
|
||||||
if (this.options?.filterByPk) {
|
if (this.options?.filterByTk) {
|
||||||
queryParams.where = {
|
queryParams.where = {
|
||||||
[Op.and]: [
|
[Op.and]: [
|
||||||
queryParams.where,
|
queryParams.where,
|
||||||
{
|
{
|
||||||
[this.model.primaryKeyAttribute]: this.options.filterByPk,
|
[this.context.targetKey || this.collection.filterTargetKey]: this.options.filterByTk,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { BelongsToMany, Model, Op, Transaction } from 'sequelize';
|
import { BelongsToMany, Model, Op, Transaction } from 'sequelize';
|
||||||
import { updateThroughTableValue } from '../update-associations';
|
import { updateThroughTableValue } from '../update-associations';
|
||||||
import { FindAndCountOptions, FindOneOptions, MultipleRelationRepository } from './multiple-relation-repository';
|
import { FindAndCountOptions, FindOneOptions, MultipleRelationRepository } from './multiple-relation-repository';
|
||||||
import { CreateOptions, DestroyOptions, FindOptions, PrimaryKey, UpdateOptions } from '../repository';
|
import { CreateOptions, DestroyOptions, FindOptions, TargetKey, UpdateOptions } from '../repository';
|
||||||
import { AssociatedOptions, PrimaryKeyWithThroughValues } from './types';
|
import { AssociatedOptions, PrimaryKeyWithThroughValues } from './types';
|
||||||
import lodash from 'lodash';
|
import lodash from 'lodash';
|
||||||
import { transaction } from './relation-repository';
|
import { transaction } from './relation-repository';
|
||||||
@ -19,12 +19,12 @@ interface IBelongsToManyRepository<M extends Model> {
|
|||||||
// 删除
|
// 删除
|
||||||
destroy(options?: number | string | number[] | string[] | DestroyOptions): Promise<Boolean>;
|
destroy(options?: number | string | number[] | string[] | DestroyOptions): Promise<Boolean>;
|
||||||
// 建立关联
|
// 建立关联
|
||||||
set(options: PrimaryKey | PrimaryKey[] | AssociatedOptions): Promise<void>;
|
set(options: TargetKey | TargetKey[] | AssociatedOptions): Promise<void>;
|
||||||
// 附加关联,存在中间表数据
|
// 附加关联,存在中间表数据
|
||||||
add(options: PrimaryKey | PrimaryKey[] | AssociatedOptions): Promise<void>;
|
add(options: TargetKey | TargetKey[] | AssociatedOptions): Promise<void>;
|
||||||
// 移除关联
|
// 移除关联
|
||||||
remove(options: PrimaryKey | PrimaryKey[] | AssociatedOptions): Promise<void>;
|
remove(options: TargetKey | TargetKey[] | AssociatedOptions): Promise<void>;
|
||||||
toggle(options: PrimaryKey | { pk?: PrimaryKey; transaction?: Transaction }): Promise<void>;
|
toggle(options: TargetKey | { pk?: TargetKey; transaction?: Transaction }): Promise<void>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class BelongsToManyRepository extends MultipleRelationRepository implements IBelongsToManyRepository<any> {
|
export class BelongsToManyRepository extends MultipleRelationRepository implements IBelongsToManyRepository<any> {
|
||||||
@ -48,22 +48,22 @@ export class BelongsToManyRepository extends MultipleRelationRepository implemen
|
|||||||
|
|
||||||
@transaction((args, transaction) => {
|
@transaction((args, transaction) => {
|
||||||
return {
|
return {
|
||||||
filterByPk: args[0],
|
filterByTk: args[0],
|
||||||
transaction,
|
transaction,
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
async destroy(options?: PrimaryKey | PrimaryKey[] | DestroyOptions): Promise<Boolean> {
|
async destroy(options?: TargetKey | TargetKey[] | DestroyOptions): Promise<Boolean> {
|
||||||
const transaction = await this.getTransaction(options);
|
const transaction = await this.getTransaction(options);
|
||||||
const association = <BelongsToMany>this.association;
|
const association = <BelongsToMany>this.association;
|
||||||
|
|
||||||
const instancesToIds = (instances) => {
|
const instancesToIds = (instances) => {
|
||||||
return instances.map((instance) => instance.get(this.target.primaryKeyAttribute));
|
return instances.map((instance) => instance.get(this.targetKey()));
|
||||||
};
|
};
|
||||||
|
|
||||||
// Through Table
|
// Through Table
|
||||||
const throughTableWhere: Array<any> = [
|
const throughTableWhere: Array<any> = [
|
||||||
{
|
{
|
||||||
[association.foreignKey]: this.sourceId,
|
[association.foreignKey]: this.sourceKeyValue,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -78,12 +78,12 @@ export class BelongsToManyRepository extends MultipleRelationRepository implemen
|
|||||||
ids = instancesToIds(instances);
|
ids = instancesToIds(instances);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options && options['filterByPk']) {
|
if (options && options['filterByTk']) {
|
||||||
const instances = (<any>this.association).toInstanceArray(options['filterByPk']);
|
const instances = (<any>this.association).toInstanceArray(options['filterByTk']);
|
||||||
ids = ids ? lodash.intersection(ids, instancesToIds(instances)) : instancesToIds(instances);
|
ids = ids ? lodash.intersection(ids, instancesToIds(instances)) : instancesToIds(instances);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options && !options['filterByPk'] && !options['filter']) {
|
if (options && !options['filterByTk'] && !options['filter']) {
|
||||||
const sourceModel = await this.getSourceModel(transaction);
|
const sourceModel = await this.getSourceModel(transaction);
|
||||||
|
|
||||||
const instances = await sourceModel[this.accessors().get]({
|
const instances = await sourceModel[this.accessors().get]({
|
||||||
@ -105,9 +105,9 @@ export class BelongsToManyRepository extends MultipleRelationRepository implemen
|
|||||||
transaction,
|
transaction,
|
||||||
});
|
});
|
||||||
|
|
||||||
await this.target.destroy({
|
await this.targetModel.destroy({
|
||||||
where: {
|
where: {
|
||||||
[this.target.primaryKeyAttribute]: {
|
[this.targetKey()]: {
|
||||||
[Op.in]: ids,
|
[Op.in]: ids,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -119,14 +119,9 @@ export class BelongsToManyRepository extends MultipleRelationRepository implemen
|
|||||||
|
|
||||||
protected async setTargets(
|
protected async setTargets(
|
||||||
call: 'add' | 'set',
|
call: 'add' | 'set',
|
||||||
options:
|
options: TargetKey | TargetKey[] | PrimaryKeyWithThroughValues | PrimaryKeyWithThroughValues[] | AssociatedOptions,
|
||||||
| PrimaryKey
|
|
||||||
| PrimaryKey[]
|
|
||||||
| PrimaryKeyWithThroughValues
|
|
||||||
| PrimaryKeyWithThroughValues[]
|
|
||||||
| AssociatedOptions,
|
|
||||||
) {
|
) {
|
||||||
let handleKeys: PrimaryKey[] | PrimaryKeyWithThroughValues[];
|
let handleKeys: TargetKey[] | PrimaryKeyWithThroughValues[];
|
||||||
|
|
||||||
const transaction = await this.getTransaction(options, false);
|
const transaction = await this.getTransaction(options, false);
|
||||||
|
|
||||||
@ -135,12 +130,12 @@ export class BelongsToManyRepository extends MultipleRelationRepository implemen
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (lodash.isString(options) || lodash.isNumber(options)) {
|
if (lodash.isString(options) || lodash.isNumber(options)) {
|
||||||
handleKeys = [<PrimaryKey>options];
|
handleKeys = [<TargetKey>options];
|
||||||
} // if it is type primaryKeyWithThroughValues
|
} // if it is type primaryKeyWithThroughValues
|
||||||
else if (lodash.isArray(options) && options.length == 2 && lodash.isPlainObject(options[0][1])) {
|
else if (lodash.isArray(options) && options.length == 2 && lodash.isPlainObject(options[0][1])) {
|
||||||
handleKeys = [<PrimaryKeyWithThroughValues>options];
|
handleKeys = [<PrimaryKeyWithThroughValues>options];
|
||||||
} else {
|
} else {
|
||||||
handleKeys = <PrimaryKey[] | PrimaryKeyWithThroughValues[]>options;
|
handleKeys = <TargetKey[] | PrimaryKeyWithThroughValues[]>options;
|
||||||
}
|
}
|
||||||
|
|
||||||
const sourceModel = await this.getSourceModel(transaction);
|
const sourceModel = await this.getSourceModel(transaction);
|
||||||
@ -160,7 +155,7 @@ export class BelongsToManyRepository extends MultipleRelationRepository implemen
|
|||||||
|
|
||||||
for (const [id, throughValues] of Object.entries(setObj)) {
|
for (const [id, throughValues] of Object.entries(setObj)) {
|
||||||
if (typeof throughValues === 'object') {
|
if (typeof throughValues === 'object') {
|
||||||
const instance = await this.target.findByPk(id, {
|
const instance = await this.targetModel.findByPk(id, {
|
||||||
transaction,
|
transaction,
|
||||||
});
|
});
|
||||||
await updateThroughTableValue(instance, this.throughName(), throughValues, sourceModel, transaction);
|
await updateThroughTableValue(instance, this.throughName(), throughValues, sourceModel, transaction);
|
||||||
@ -175,12 +170,7 @@ export class BelongsToManyRepository extends MultipleRelationRepository implemen
|
|||||||
};
|
};
|
||||||
})
|
})
|
||||||
async add(
|
async add(
|
||||||
options:
|
options: TargetKey | TargetKey[] | PrimaryKeyWithThroughValues | PrimaryKeyWithThroughValues[] | AssociatedOptions,
|
||||||
| PrimaryKey
|
|
||||||
| PrimaryKey[]
|
|
||||||
| PrimaryKeyWithThroughValues
|
|
||||||
| PrimaryKeyWithThroughValues[]
|
|
||||||
| AssociatedOptions,
|
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
await this.setTargets('add', options);
|
await this.setTargets('add', options);
|
||||||
}
|
}
|
||||||
@ -192,12 +182,7 @@ export class BelongsToManyRepository extends MultipleRelationRepository implemen
|
|||||||
};
|
};
|
||||||
})
|
})
|
||||||
async set(
|
async set(
|
||||||
options:
|
options: TargetKey | TargetKey[] | PrimaryKeyWithThroughValues | PrimaryKeyWithThroughValues[] | AssociatedOptions,
|
||||||
| PrimaryKey
|
|
||||||
| PrimaryKey[]
|
|
||||||
| PrimaryKeyWithThroughValues
|
|
||||||
| PrimaryKeyWithThroughValues[]
|
|
||||||
| AssociatedOptions,
|
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
await this.setTargets('set', options);
|
await this.setTargets('set', options);
|
||||||
}
|
}
|
||||||
@ -208,7 +193,7 @@ export class BelongsToManyRepository extends MultipleRelationRepository implemen
|
|||||||
transaction,
|
transaction,
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
async toggle(options: PrimaryKey | { pk?: PrimaryKey; transaction?: Transaction }): Promise<void> {
|
async toggle(options: TargetKey | { pk?: TargetKey; transaction?: Transaction }): Promise<void> {
|
||||||
const transaction = await this.getTransaction(options);
|
const transaction = await this.getTransaction(options);
|
||||||
const sourceModel = await this.getSourceModel(transaction);
|
const sourceModel = await this.getSourceModel(transaction);
|
||||||
const has = await sourceModel[this.accessors().hasSingle](options['pk'], {
|
const has = await sourceModel[this.accessors().hasSingle](options['pk'], {
|
||||||
|
@ -6,8 +6,9 @@ import {
|
|||||||
FindOneOptions,
|
FindOneOptions,
|
||||||
MultipleRelationRepository,
|
MultipleRelationRepository,
|
||||||
} from './multiple-relation-repository';
|
} from './multiple-relation-repository';
|
||||||
import { CreateOptions, DestroyOptions, FindOptions, PK, PrimaryKey, UpdateOptions } from '../repository';
|
import { CreateOptions, DestroyOptions, FindOptions, TK, TargetKey, UpdateOptions } from '../repository';
|
||||||
import { transaction } from './relation-repository';
|
import { transaction } from './relation-repository';
|
||||||
|
import lodash from 'lodash';
|
||||||
|
|
||||||
interface IHasManyRepository<M extends Model> {
|
interface IHasManyRepository<M extends Model> {
|
||||||
find(options?: FindOptions): Promise<M>;
|
find(options?: FindOptions): Promise<M>;
|
||||||
@ -18,30 +19,30 @@ interface IHasManyRepository<M extends Model> {
|
|||||||
// 更新
|
// 更新
|
||||||
update(options?: UpdateOptions): Promise<M>;
|
update(options?: UpdateOptions): Promise<M>;
|
||||||
// 删除
|
// 删除
|
||||||
destroy(options?: PK | DestroyOptions): Promise<Boolean>;
|
destroy(options?: TK | DestroyOptions): Promise<Boolean>;
|
||||||
// 建立关联
|
// 建立关联
|
||||||
set(options: PrimaryKey | PrimaryKey[] | AssociatedOptions): Promise<void>;
|
set(options: TargetKey | TargetKey[] | AssociatedOptions): Promise<void>;
|
||||||
// 附加关联
|
// 附加关联
|
||||||
add(options: PrimaryKey | PrimaryKey[] | AssociatedOptions): Promise<void>;
|
add(options: TargetKey | TargetKey[] | AssociatedOptions): Promise<void>;
|
||||||
// 移除关联
|
// 移除关联
|
||||||
remove(options: PrimaryKey | PrimaryKey[] | AssociatedOptions): Promise<void>;
|
remove(options: TargetKey | TargetKey[] | AssociatedOptions): Promise<void>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class HasManyRepository extends MultipleRelationRepository implements IHasManyRepository<any> {
|
export class HasManyRepository extends MultipleRelationRepository implements IHasManyRepository<any> {
|
||||||
@transaction((args, transaction) => {
|
@transaction((args, transaction) => {
|
||||||
return {
|
return {
|
||||||
filterByPk: args[0],
|
filterByTk: args[0],
|
||||||
transaction,
|
transaction,
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
async destroy(options?: PK | DestroyOptions): Promise<Boolean> {
|
async destroy(options?: TK | DestroyOptions): Promise<Boolean> {
|
||||||
const transaction = await this.getTransaction(options);
|
const transaction = await this.getTransaction(options);
|
||||||
|
|
||||||
const sourceModel = await this.getSourceModel(transaction);
|
const sourceModel = await this.getSourceModel(transaction);
|
||||||
|
|
||||||
const where = [
|
const where = [
|
||||||
{
|
{
|
||||||
[this.association.foreignKey]: sourceModel.get(this.source.model.primaryKeyAttribute),
|
[this.association.foreignKey]: sourceModel.get((this.association as any).sourceKey),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -54,21 +55,18 @@ export class HasManyRepository extends MultipleRelationRepository implements IHa
|
|||||||
|
|
||||||
where.push(filterResult.where);
|
where.push(filterResult.where);
|
||||||
}
|
}
|
||||||
if (options && options['filterByPk']) {
|
|
||||||
if (typeof options === 'object' && options['filterByPk']) {
|
if (options && options['filterByTk']) {
|
||||||
options = options['filterByPk'];
|
if (typeof options === 'object' && options['filterByTk']) {
|
||||||
|
options = options['filterByTk'];
|
||||||
}
|
}
|
||||||
|
|
||||||
const targetInstances = (<any>this.association).toInstanceArray(options);
|
|
||||||
|
|
||||||
where.push({
|
where.push({
|
||||||
[this.target.primaryKeyAttribute]: targetInstances.map((targetInstance) =>
|
[this.targetKey()]: options,
|
||||||
targetInstance.get(this.target.primaryKeyAttribute),
|
|
||||||
),
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.target.destroy({
|
await this.targetModel.destroy({
|
||||||
where: {
|
where: {
|
||||||
[Op.and]: where,
|
[Op.and]: where,
|
||||||
},
|
},
|
||||||
@ -95,7 +93,7 @@ export class HasManyRepository extends MultipleRelationRepository implements IHa
|
|||||||
transaction,
|
transaction,
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
async set(options: PrimaryKey | PrimaryKey[] | AssociatedOptions): Promise<void> {
|
async set(options: TargetKey | TargetKey[] | AssociatedOptions): Promise<void> {
|
||||||
const transaction = await this.getTransaction(options);
|
const transaction = await this.getTransaction(options);
|
||||||
|
|
||||||
const sourceModel = await this.getSourceModel(transaction);
|
const sourceModel = await this.getSourceModel(transaction);
|
||||||
@ -111,7 +109,7 @@ export class HasManyRepository extends MultipleRelationRepository implements IHa
|
|||||||
transaction,
|
transaction,
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
async add(options: PrimaryKey | PrimaryKey[] | AssociatedOptions): Promise<void> {
|
async add(options: TargetKey | TargetKey[] | AssociatedOptions): Promise<void> {
|
||||||
const transaction = await this.getTransaction(options);
|
const transaction = await this.getTransaction(options);
|
||||||
|
|
||||||
const sourceModel = await this.getSourceModel(transaction);
|
const sourceModel = await this.getSourceModel(transaction);
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { RelationRepository, transaction } from './relation-repository';
|
import { RelationRepository, transaction } from './relation-repository';
|
||||||
import { omit } from 'lodash';
|
import lodash, { omit } from 'lodash';
|
||||||
import { MultiAssociationAccessors, Op, Sequelize, Transaction } from 'sequelize';
|
import { MultiAssociationAccessors, Op, Sequelize, Transaction } from 'sequelize';
|
||||||
import { UpdateGuard } from '../update-guard';
|
import { UpdateGuard } from '../update-guard';
|
||||||
import { updateModelByValues } from '../update-associations';
|
import { updateModelByValues } from '../update-associations';
|
||||||
@ -8,20 +8,20 @@ import {
|
|||||||
CountOptions,
|
CountOptions,
|
||||||
DestroyOptions,
|
DestroyOptions,
|
||||||
Filter,
|
Filter,
|
||||||
FilterByPK,
|
FilterByTk,
|
||||||
FindOptions,
|
FindOptions,
|
||||||
PK,
|
TK,
|
||||||
PrimaryKey,
|
TargetKey,
|
||||||
TransactionAble,
|
TransactionAble,
|
||||||
UpdateOptions,
|
UpdateOptions,
|
||||||
} from '../repository';
|
} from '../repository';
|
||||||
|
|
||||||
export interface FindAndCountOptions extends CommonFindOptions {}
|
export interface FindAndCountOptions extends CommonFindOptions {}
|
||||||
|
|
||||||
export interface FindOneOptions extends CommonFindOptions, FilterByPK {}
|
export interface FindOneOptions extends CommonFindOptions, FilterByTk {}
|
||||||
|
|
||||||
export interface AssociatedOptions extends TransactionAble {
|
export interface AssociatedOptions extends TransactionAble {
|
||||||
pk?: PK;
|
tk?: TK;
|
||||||
}
|
}
|
||||||
|
|
||||||
export abstract class MultipleRelationRepository extends RelationRepository {
|
export abstract class MultipleRelationRepository extends RelationRepository {
|
||||||
@ -46,16 +46,16 @@ export abstract class MultipleRelationRepository extends RelationRepository {
|
|||||||
await sourceModel[getAccessor]({
|
await sourceModel[getAccessor]({
|
||||||
...findOptions,
|
...findOptions,
|
||||||
includeIgnoreAttributes: false,
|
includeIgnoreAttributes: false,
|
||||||
attributes: [this.target.primaryKeyAttribute],
|
attributes: [this.targetKey()],
|
||||||
group: `${this.target.name}.${this.target.primaryKeyAttribute}`,
|
group: `${this.targetModel.name}.${this.targetKey()}`,
|
||||||
transaction,
|
transaction,
|
||||||
})
|
})
|
||||||
).map((row) => row.get(this.target.primaryKeyAttribute));
|
).map((row) => row.get(this.targetKey()));
|
||||||
|
|
||||||
return await sourceModel[getAccessor]({
|
return await sourceModel[getAccessor]({
|
||||||
...omit(findOptions, ['limit', 'offset']),
|
...omit(findOptions, ['limit', 'offset']),
|
||||||
where: {
|
where: {
|
||||||
[this.target.primaryKeyAttribute]: {
|
[this.targetKey()]: {
|
||||||
[Op.in]: ids,
|
[Op.in]: ids,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -97,7 +97,7 @@ export abstract class MultipleRelationRepository extends RelationRepository {
|
|||||||
[
|
[
|
||||||
Sequelize.fn(
|
Sequelize.fn(
|
||||||
'COUNT',
|
'COUNT',
|
||||||
Sequelize.fn('DISTINCT', Sequelize.col(`${this.target.name}.${this.target.primaryKeyAttribute}`)),
|
Sequelize.fn('DISTINCT', Sequelize.col(`${this.targetModel.name}.${this.targetKey()}`)),
|
||||||
),
|
),
|
||||||
'count',
|
'count',
|
||||||
],
|
],
|
||||||
@ -122,7 +122,7 @@ export abstract class MultipleRelationRepository extends RelationRepository {
|
|||||||
transaction,
|
transaction,
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
async remove(options: PrimaryKey | PrimaryKey[] | AssociatedOptions): Promise<void> {
|
async remove(options: TargetKey | TargetKey[] | AssociatedOptions): Promise<void> {
|
||||||
const transaction = await this.getTransaction(options);
|
const transaction = await this.getTransaction(options);
|
||||||
let handleKeys = options['pk'];
|
let handleKeys = options['pk'];
|
||||||
|
|
||||||
@ -141,7 +141,7 @@ export abstract class MultipleRelationRepository extends RelationRepository {
|
|||||||
async update(options?: UpdateOptions): Promise<any> {
|
async update(options?: UpdateOptions): Promise<any> {
|
||||||
const transaction = await this.getTransaction(options);
|
const transaction = await this.getTransaction(options);
|
||||||
|
|
||||||
const guard = UpdateGuard.fromOptions(this.target, options);
|
const guard = UpdateGuard.fromOptions(this.targetModel, options);
|
||||||
|
|
||||||
const values = guard.sanitize(options.values);
|
const values = guard.sanitize(options.values);
|
||||||
|
|
||||||
@ -153,7 +153,7 @@ export abstract class MultipleRelationRepository extends RelationRepository {
|
|||||||
await updateModelByValues(instance, values, {
|
await updateModelByValues(instance, values, {
|
||||||
...options,
|
...options,
|
||||||
sanitized: true,
|
sanitized: true,
|
||||||
sourceModel: this.sourceModel,
|
sourceModel: this.sourceInstance,
|
||||||
transaction,
|
transaction,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -161,7 +161,7 @@ export abstract class MultipleRelationRepository extends RelationRepository {
|
|||||||
return instances;
|
return instances;
|
||||||
}
|
}
|
||||||
|
|
||||||
async destroy(options?: PK | DestroyOptions): Promise<Boolean> {
|
async destroy(options?: TK | DestroyOptions): Promise<Boolean> {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -170,8 +170,9 @@ export abstract class MultipleRelationRepository extends RelationRepository {
|
|||||||
filter: filter,
|
filter: filter,
|
||||||
transaction,
|
transaction,
|
||||||
});
|
});
|
||||||
|
|
||||||
return await this.destroy({
|
return await this.destroy({
|
||||||
filterByPk: instances.map((instance) => instance[this.target.primaryKeyAttribute]),
|
filterByTk: instances.map((instance) => instance.get(this.targetCollection.filterTargetKey)),
|
||||||
transaction,
|
transaction,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -7,24 +7,36 @@ import { UpdateGuard } from '../update-guard';
|
|||||||
import { updateAssociations } from '../update-associations';
|
import { updateAssociations } from '../update-associations';
|
||||||
import lodash from 'lodash';
|
import lodash from 'lodash';
|
||||||
import { transactionWrapperBuilder } from '../transaction-decorator';
|
import { transactionWrapperBuilder } from '../transaction-decorator';
|
||||||
|
import { Field, RelationField } from '@nocobase/database';
|
||||||
|
|
||||||
export const transaction = transactionWrapperBuilder(function () {
|
export const transaction = transactionWrapperBuilder(function () {
|
||||||
return this.source.model.sequelize.transaction();
|
return this.sourceCollection.model.sequelize.transaction();
|
||||||
});
|
});
|
||||||
|
|
||||||
export abstract class RelationRepository {
|
export abstract class RelationRepository {
|
||||||
source: Collection;
|
sourceCollection: Collection;
|
||||||
association: Association;
|
association: Association;
|
||||||
target: ModelCtor<any>;
|
targetModel: ModelCtor<any>;
|
||||||
sourceId: string | number;
|
targetCollection: Collection;
|
||||||
sourceModel: Model;
|
associationName: string;
|
||||||
|
associationField: RelationField;
|
||||||
|
sourceKeyValue: string | number;
|
||||||
|
sourceInstance: Model;
|
||||||
|
|
||||||
constructor(source: Collection, association: string, sourceId: string | number) {
|
constructor(sourceCollection: Collection, association: string, sourceKeyValue: string | number) {
|
||||||
this.source = source;
|
this.sourceCollection = sourceCollection;
|
||||||
this.sourceId = sourceId;
|
this.sourceKeyValue = sourceKeyValue;
|
||||||
this.association = this.source.model.associations[association];
|
this.associationName = association;
|
||||||
|
this.association = this.sourceCollection.model.associations[association];
|
||||||
|
|
||||||
this.target = this.association.target;
|
this.associationField = this.sourceCollection.getField(association);
|
||||||
|
|
||||||
|
this.targetModel = this.association.target;
|
||||||
|
this.targetCollection = this.sourceCollection.context.database.modelCollection.get(this.targetModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
targetKey() {
|
||||||
|
return this.associationField.targetKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected accessors() {
|
protected accessors() {
|
||||||
@ -34,7 +46,7 @@ export abstract class RelationRepository {
|
|||||||
async create(options?: CreateOptions): Promise<any> {
|
async create(options?: CreateOptions): Promise<any> {
|
||||||
const createAccessor = this.accessors().create;
|
const createAccessor = this.accessors().create;
|
||||||
|
|
||||||
const guard = UpdateGuard.fromOptions(this.target, options);
|
const guard = UpdateGuard.fromOptions(this.targetModel, options);
|
||||||
const values = options.values;
|
const values = options.values;
|
||||||
|
|
||||||
const sourceModel = await this.getSourceModel();
|
const sourceModel = await this.getSourceModel();
|
||||||
@ -47,23 +59,30 @@ export abstract class RelationRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async getSourceModel(transaction?: any) {
|
async getSourceModel(transaction?: any) {
|
||||||
if (!this.sourceModel) {
|
if (!this.sourceInstance) {
|
||||||
this.sourceModel = await this.source.model.findByPk(this.sourceId, {
|
this.sourceInstance = await this.sourceCollection.model.findOne({
|
||||||
transaction,
|
where: {
|
||||||
|
[this.associationField.sourceKey]: this.sourceKeyValue,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.sourceModel;
|
return this.sourceInstance;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected buildQueryOptions(options: FindOptions) {
|
protected buildQueryOptions(options: FindOptions) {
|
||||||
const parser = new OptionsParser(this.target, this.source.context.database, options);
|
const parser = new OptionsParser(options, {
|
||||||
|
collection: this.targetCollection,
|
||||||
|
targetKey: this.targetKey(),
|
||||||
|
});
|
||||||
const params = parser.toSequelizeParams();
|
const params = parser.toSequelizeParams();
|
||||||
return { ...options, ...params };
|
return { ...options, ...params };
|
||||||
}
|
}
|
||||||
|
|
||||||
protected parseFilter(filter: Filter) {
|
protected parseFilter(filter: Filter) {
|
||||||
const parser = new FilterParser(this.target, this.source.context.database, filter);
|
const parser = new FilterParser(filter, {
|
||||||
|
collection: this.targetCollection,
|
||||||
|
});
|
||||||
return parser.toSequelizeParams();
|
return parser.toSequelizeParams();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,7 +92,7 @@ export abstract class RelationRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (autoGen) {
|
if (autoGen) {
|
||||||
return await this.source.model.sequelize.transaction();
|
return await this.sourceCollection.model.sequelize.transaction();
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
@ -2,7 +2,7 @@ import { RelationRepository, transaction } from './relation-repository';
|
|||||||
import { Model, SingleAssociationAccessors } from 'sequelize';
|
import { Model, SingleAssociationAccessors } from 'sequelize';
|
||||||
import { updateModelByValues } from '../update-associations';
|
import { updateModelByValues } from '../update-associations';
|
||||||
import lodash from 'lodash';
|
import lodash from 'lodash';
|
||||||
import { Appends, Except, Fields, Filter, PrimaryKey, TransactionAble, UpdateOptions } from '../repository';
|
import { Appends, Except, Fields, Filter, TargetKey, TransactionAble, UpdateOptions } from '../repository';
|
||||||
|
|
||||||
export interface SingleRelationFindOption extends TransactionAble {
|
export interface SingleRelationFindOption extends TransactionAble {
|
||||||
fields?: Fields;
|
fields?: Fields;
|
||||||
@ -12,7 +12,7 @@ export interface SingleRelationFindOption extends TransactionAble {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface SetOption extends TransactionAble {
|
interface SetOption extends TransactionAble {
|
||||||
pk?: PrimaryKey;
|
tk?: TargetKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
export abstract class SingleRelationRepository extends RelationRepository {
|
export abstract class SingleRelationRepository extends RelationRepository {
|
||||||
@ -27,13 +27,13 @@ export abstract class SingleRelationRepository extends RelationRepository {
|
|||||||
|
|
||||||
@transaction((args, transaction) => {
|
@transaction((args, transaction) => {
|
||||||
return {
|
return {
|
||||||
pk: args[0],
|
tk: args[0],
|
||||||
transaction,
|
transaction,
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
async set(options: PrimaryKey | SetOption): Promise<void> {
|
async set(options: TargetKey | SetOption): Promise<void> {
|
||||||
const transaction = await this.getTransaction(options);
|
const transaction = await this.getTransaction(options);
|
||||||
let handleKey = lodash.isPlainObject(options) ? (<SetOption>options).pk : options;
|
let handleKey = lodash.isPlainObject(options) ? (<SetOption>options).tk : options;
|
||||||
|
|
||||||
const sourceModel = await this.getSourceModel(transaction);
|
const sourceModel = await this.getSourceModel(transaction);
|
||||||
|
|
||||||
|
@ -43,8 +43,8 @@ export interface FilterAble {
|
|||||||
filter: Filter;
|
filter: Filter;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type PrimaryKey = string | number;
|
export type TargetKey = string | number;
|
||||||
export type PK = PrimaryKey | PrimaryKey[];
|
export type TK = TargetKey | TargetKey[];
|
||||||
|
|
||||||
export type Filter = any;
|
export type Filter = any;
|
||||||
export type Appends = string[];
|
export type Appends = string[];
|
||||||
@ -63,11 +63,11 @@ export interface CountOptions extends Omit<SequelizeCreateOptions, 'distinct' |
|
|||||||
filter?: Filter;
|
filter?: Filter;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface FilterByPK {
|
export interface FilterByTk {
|
||||||
filterByPk?: PrimaryKey;
|
filterByTk?: TargetKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface FindOptions extends SequelizeFindOptions, CommonFindOptions, FilterByPK {}
|
export interface FindOptions extends SequelizeFindOptions, CommonFindOptions, FilterByTk {}
|
||||||
|
|
||||||
export interface CommonFindOptions {
|
export interface CommonFindOptions {
|
||||||
filter?: Filter;
|
filter?: Filter;
|
||||||
@ -81,7 +81,7 @@ interface FindOneOptions extends FindOptions, CommonFindOptions {}
|
|||||||
|
|
||||||
export interface DestroyOptions extends SequelizeDestroyOptions {
|
export interface DestroyOptions extends SequelizeDestroyOptions {
|
||||||
filter?: Filter;
|
filter?: Filter;
|
||||||
filterByPk?: PrimaryKey | PrimaryKey[];
|
filterByTk?: TargetKey | TargetKey[];
|
||||||
truncate?: boolean;
|
truncate?: boolean;
|
||||||
context?: any;
|
context?: any;
|
||||||
}
|
}
|
||||||
@ -110,7 +110,7 @@ export interface CreateOptions extends SequelizeCreateOptions {
|
|||||||
export interface UpdateOptions extends Omit<SequelizeUpdateOptions, 'where'> {
|
export interface UpdateOptions extends Omit<SequelizeUpdateOptions, 'where'> {
|
||||||
values: Values;
|
values: Values;
|
||||||
filter?: Filter;
|
filter?: Filter;
|
||||||
filterByPk?: PrimaryKey;
|
filterByTk?: TargetKey;
|
||||||
whitelist?: WhiteList;
|
whitelist?: WhiteList;
|
||||||
blacklist?: BlackList;
|
blacklist?: BlackList;
|
||||||
updateAssociationValues?: AssociationKeysToBeUpdate;
|
updateAssociationValues?: AssociationKeysToBeUpdate;
|
||||||
@ -261,7 +261,7 @@ export class Repository<TModelAttributes extends {} = any, TCreationAttributes e
|
|||||||
* Find By Id
|
* Find By Id
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
findById(id: PrimaryKey) {
|
findById(id: string | number) {
|
||||||
return this.collection.model.findByPk(id);
|
return this.collection.model.findByPk(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -362,26 +362,28 @@ export class Repository<TModelAttributes extends {} = any, TCreationAttributes e
|
|||||||
|
|
||||||
@transaction((args, transaction) => {
|
@transaction((args, transaction) => {
|
||||||
return {
|
return {
|
||||||
filterByPk: args[0],
|
filterByTk: args[0],
|
||||||
transaction,
|
transaction,
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
async destroy(options?: PrimaryKey | PrimaryKey[] | DestroyOptions) {
|
async destroy(options?: TargetKey | TargetKey[] | DestroyOptions) {
|
||||||
const transaction = await this.getTransaction(options);
|
const transaction = await this.getTransaction(options);
|
||||||
|
|
||||||
|
const modelFilterKey = this.collection.filterTargetKey;
|
||||||
|
|
||||||
options = <DestroyOptions>options;
|
options = <DestroyOptions>options;
|
||||||
|
|
||||||
const filterByPk: PrimaryKey[] | undefined =
|
const filterByTk: TargetKey[] | undefined =
|
||||||
options.filterByPk && !lodash.isArray(options.filterByPk)
|
options.filterByTk && !lodash.isArray(options.filterByTk)
|
||||||
? [options.filterByPk]
|
? [options.filterByTk]
|
||||||
: (options.filterByPk as PrimaryKey[] | undefined);
|
: (options.filterByTk as TargetKey[] | undefined);
|
||||||
|
|
||||||
if (filterByPk && !options.filter) {
|
if (filterByTk && !options.filter) {
|
||||||
return await this.model.destroy({
|
return await this.model.destroy({
|
||||||
...options,
|
...options,
|
||||||
where: {
|
where: {
|
||||||
[this.model.primaryKeyAttribute]: {
|
[modelFilterKey]: {
|
||||||
[Op.in]: filterByPk,
|
[Op.in]: filterByTk,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
transaction,
|
transaction,
|
||||||
@ -394,15 +396,15 @@ export class Repository<TModelAttributes extends {} = any, TCreationAttributes e
|
|||||||
filter: options.filter,
|
filter: options.filter,
|
||||||
transaction,
|
transaction,
|
||||||
})
|
})
|
||||||
).map((instance) => instance[this.model.primaryKeyAttribute]);
|
).map((instance) => instance.get(modelFilterKey) as TargetKey);
|
||||||
|
|
||||||
if (filterByPk) {
|
if (filterByTk) {
|
||||||
pks = lodash.intersection(pks, filterByPk);
|
pks = lodash.intersection(pks, filterByTk);
|
||||||
}
|
}
|
||||||
|
|
||||||
return await this.destroy({
|
return await this.destroy({
|
||||||
context: options.context,
|
context: options.context,
|
||||||
filterByPk: pks,
|
filterByTk: pks,
|
||||||
transaction,
|
transaction,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -424,14 +426,19 @@ export class Repository<TModelAttributes extends {} = any, TCreationAttributes e
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected buildQueryOptions(options: any) {
|
protected buildQueryOptions(options: any) {
|
||||||
const parser = new OptionsParser(this.collection.model, this.collection.context.database, options);
|
const parser = new OptionsParser(options, {
|
||||||
|
collection: this.collection,
|
||||||
|
});
|
||||||
|
|
||||||
const params = parser.toSequelizeParams();
|
const params = parser.toSequelizeParams();
|
||||||
debug('sequelize query params %o', params);
|
debug('sequelize query params %o', params);
|
||||||
return { ...options, ...params };
|
return { ...options, ...params };
|
||||||
}
|
}
|
||||||
|
|
||||||
protected parseFilter(filter: Filter) {
|
protected parseFilter(filter: Filter) {
|
||||||
const parser = new FilterParser(this.collection.model, this.collection.context.database, filter);
|
const parser = new FilterParser(filter, {
|
||||||
|
collection: this.collection,
|
||||||
|
});
|
||||||
return parser.toSequelizeParams();
|
return parser.toSequelizeParams();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,7 +51,7 @@ type UpdateValue = { [key: string]: any };
|
|||||||
|
|
||||||
interface UpdateOptions extends TransactionAble {
|
interface UpdateOptions extends TransactionAble {
|
||||||
filter?: any;
|
filter?: any;
|
||||||
filterByPk?: number | string;
|
filterByTk?: number | string;
|
||||||
// 字段白名单
|
// 字段白名单
|
||||||
whitelist?: string[];
|
whitelist?: string[];
|
||||||
// 字段黑名单
|
// 字段黑名单
|
||||||
|
Loading…
Reference in New Issue
Block a user