feat: support sort m2m & o2m fields (#768)
Co-authored-by: sealday <sealday@gmail.com> Reviewed-on: daoyoucloud/tachybase#768
This commit is contained in:
parent
c82fe54fe4
commit
b3301e19de
@ -3,6 +3,7 @@ import { Context } from '..';
|
|||||||
import { getRepositoryFromParams, pageArgsToLimitArgs } from '../utils';
|
import { getRepositoryFromParams, pageArgsToLimitArgs } from '../utils';
|
||||||
import { DEFAULT_PAGE, DEFAULT_PER_PAGE } from '../constants';
|
import { DEFAULT_PAGE, DEFAULT_PER_PAGE } from '../constants';
|
||||||
import { Op, QueryTypes } from 'sequelize';
|
import { Op, QueryTypes } from 'sequelize';
|
||||||
|
import qs from 'querystring';
|
||||||
|
|
||||||
function totalPage(total, pageSize): number {
|
function totalPage(total, pageSize): number {
|
||||||
return Math.ceil(total / pageSize);
|
return Math.ceil(total / pageSize);
|
||||||
@ -11,6 +12,32 @@ function totalPage(total, pageSize): number {
|
|||||||
function findArgs(ctx: Context) {
|
function findArgs(ctx: Context) {
|
||||||
const resourceName = ctx.action.resourceName;
|
const resourceName = ctx.action.resourceName;
|
||||||
const params = ctx.action.params;
|
const params = ctx.action.params;
|
||||||
|
// 处理 sort 字段
|
||||||
|
const includeSort = params.sort?.filter((item) => item.split('.').length > 1) ?? [];
|
||||||
|
const sortItems = [];
|
||||||
|
includeSort.forEach((sort) => {
|
||||||
|
const parts = sort[0] === '-' ? sort.slice(1, sort.length).split('.') : sort.split('.');
|
||||||
|
const prefix = parts.slice(0, -1).join('.');
|
||||||
|
const sign = sort[0] === '-' ? '-' : '';
|
||||||
|
const sortItem = sign + parts.slice(-1).join('.');
|
||||||
|
const findIndex = sortItems.findIndex((item) => item.prefix === prefix);
|
||||||
|
if (findIndex !== -1) {
|
||||||
|
sortItems[findIndex].items.push(sortItem);
|
||||||
|
} else {
|
||||||
|
sortItems.push({
|
||||||
|
prefix,
|
||||||
|
sortItems: [sortItem],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
sortItems.forEach((item) => {
|
||||||
|
const i = params.appends?.findIndex((append) => append === item.prefix) ?? -1;
|
||||||
|
if (i !== -1) {
|
||||||
|
params.appends[i] = `${item.prefix}(${qs.stringify({ sort: item.sortItems })})`;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
params.sort = params.sort?.filter((item) => item.split('.').length == 1) ?? [];
|
||||||
|
|
||||||
if (params.tree) {
|
if (params.tree) {
|
||||||
const [collectionName, associationName] = resourceName.split('.');
|
const [collectionName, associationName] = resourceName.split('.');
|
||||||
const collection = ctx.db.getCollection(resourceName);
|
const collection = ctx.db.getCollection(resourceName);
|
||||||
@ -81,10 +108,10 @@ async function listWithPagination(ctx: Context) {
|
|||||||
)
|
)
|
||||||
SELECT DISTINCT *
|
SELECT DISTINCT *
|
||||||
FROM (
|
FROM (
|
||||||
SELECT *
|
SELECT *
|
||||||
FROM tree1
|
FROM tree1
|
||||||
UNION ALL
|
UNION ALL
|
||||||
SELECT *
|
SELECT *
|
||||||
FROM tree2
|
FROM tree2
|
||||||
);`;
|
);`;
|
||||||
const filterTreeDatas = await ctx.db.sequelize.query(query, {
|
const filterTreeDatas = await ctx.db.sequelize.query(query, {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { mockDatabase } from '../index';
|
import { mockDatabase } from '../index';
|
||||||
import Database from '@nocobase/database';
|
import Database from '@nocobase/database';
|
||||||
import { Collection } from '../../collection';
|
import { Collection } from '../../collection';
|
||||||
|
import qs from 'qs';
|
||||||
|
|
||||||
describe('find with associations', () => {
|
describe('find with associations', () => {
|
||||||
let db: Database;
|
let db: Database;
|
||||||
@ -412,6 +413,63 @@ describe('find with associations', () => {
|
|||||||
|
|
||||||
expect(findResult[0].get('name')).toEqual('u1');
|
expect(findResult[0].get('name')).toEqual('u1');
|
||||||
});
|
});
|
||||||
|
it('should find with associations with sort params', async () => {
|
||||||
|
const User = db.collection({
|
||||||
|
name: 'users',
|
||||||
|
fields: [
|
||||||
|
{ type: 'string', name: 'name' },
|
||||||
|
{
|
||||||
|
type: 'hasMany',
|
||||||
|
name: 'posts',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const Post = db.collection({
|
||||||
|
name: 'posts',
|
||||||
|
fields: [
|
||||||
|
{ type: 'string', name: 'title' },
|
||||||
|
{ type: 'belongsTo', name: 'user' },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
await db.sync();
|
||||||
|
|
||||||
|
await User.repository.create({
|
||||||
|
values: [
|
||||||
|
{
|
||||||
|
name: 'u1',
|
||||||
|
posts: [
|
||||||
|
{
|
||||||
|
title: 'u1p1',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'u1p2',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'u2',
|
||||||
|
posts: [
|
||||||
|
{
|
||||||
|
title: 'u2p1',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'u2p2',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const appendArgs = [`posts(${qs.stringify({ sort: ['-id'] })})`];
|
||||||
|
const users = await User.repository.find({
|
||||||
|
appends: appendArgs,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(users[0].get('name')).toEqual('u1');
|
||||||
|
expect(users[0].get('posts')[0].get('title')).toEqual('u1p2');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('repository find', () => {
|
describe('repository find', () => {
|
||||||
|
@ -257,6 +257,20 @@ export class EagerLoadingTree {
|
|||||||
const association = node.association;
|
const association = node.association;
|
||||||
const associationType = association.associationType;
|
const associationType = association.associationType;
|
||||||
|
|
||||||
|
let params: any = {};
|
||||||
|
|
||||||
|
const otherFindOptions = lodash.pick(node.includeOption, ['sort']) || {};
|
||||||
|
|
||||||
|
const collection = this.db.modelCollection.get(node.model);
|
||||||
|
|
||||||
|
if (collection && !lodash.isEmpty(otherFindOptions)) {
|
||||||
|
const parser = new OptionsParser(otherFindOptions, {
|
||||||
|
collection,
|
||||||
|
});
|
||||||
|
|
||||||
|
params = parser.toSequelizeParams();
|
||||||
|
}
|
||||||
|
|
||||||
if (associationType == 'HasOne' || associationType == 'HasMany') {
|
if (associationType == 'HasOne' || associationType == 'HasMany') {
|
||||||
const foreignKey = association.foreignKey;
|
const foreignKey = association.foreignKey;
|
||||||
const foreignKeyValues = node.parent.instances.map((instance) => instance.get(association.sourceKey));
|
const foreignKeyValues = node.parent.instances.map((instance) => instance.get(association.sourceKey));
|
||||||
@ -271,7 +285,7 @@ export class EagerLoadingTree {
|
|||||||
const findOptions = {
|
const findOptions = {
|
||||||
where,
|
where,
|
||||||
attributes: node.attributes,
|
attributes: node.attributes,
|
||||||
order: orderOption(association),
|
order: params.order || orderOption(association),
|
||||||
transaction,
|
transaction,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -358,7 +372,7 @@ export class EagerLoadingTree {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
order: orderOption(association),
|
order: params.order || orderOption(association),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user