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 { DEFAULT_PAGE, DEFAULT_PER_PAGE } from '../constants';
|
||||
import { Op, QueryTypes } from 'sequelize';
|
||||
import qs from 'querystring';
|
||||
|
||||
function totalPage(total, pageSize): number {
|
||||
return Math.ceil(total / pageSize);
|
||||
@ -11,6 +12,32 @@ function totalPage(total, pageSize): number {
|
||||
function findArgs(ctx: Context) {
|
||||
const resourceName = ctx.action.resourceName;
|
||||
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) {
|
||||
const [collectionName, associationName] = resourceName.split('.');
|
||||
const collection = ctx.db.getCollection(resourceName);
|
||||
@ -81,10 +108,10 @@ async function listWithPagination(ctx: Context) {
|
||||
)
|
||||
SELECT DISTINCT *
|
||||
FROM (
|
||||
SELECT *
|
||||
SELECT *
|
||||
FROM tree1
|
||||
UNION ALL
|
||||
SELECT *
|
||||
SELECT *
|
||||
FROM tree2
|
||||
);`;
|
||||
const filterTreeDatas = await ctx.db.sequelize.query(query, {
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { mockDatabase } from '../index';
|
||||
import Database from '@nocobase/database';
|
||||
import { Collection } from '../../collection';
|
||||
import qs from 'qs';
|
||||
|
||||
describe('find with associations', () => {
|
||||
let db: Database;
|
||||
@ -412,6 +413,63 @@ describe('find with associations', () => {
|
||||
|
||||
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', () => {
|
||||
|
@ -257,6 +257,20 @@ export class EagerLoadingTree {
|
||||
const association = node.association;
|
||||
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') {
|
||||
const foreignKey = association.foreignKey;
|
||||
const foreignKeyValues = node.parent.instances.map((instance) => instance.get(association.sourceKey));
|
||||
@ -271,7 +285,7 @@ export class EagerLoadingTree {
|
||||
const findOptions = {
|
||||
where,
|
||||
attributes: node.attributes,
|
||||
order: orderOption(association),
|
||||
order: params.order || orderOption(association),
|
||||
transaction,
|
||||
};
|
||||
|
||||
@ -358,7 +372,7 @@ export class EagerLoadingTree {
|
||||
},
|
||||
},
|
||||
],
|
||||
order: orderOption(association),
|
||||
order: params.order || orderOption(association),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user