mirror of
https://gitee.com/nocobase/nocobase.git
synced 2025-05-05 21:49:25 +08:00
feat(database): append default sort options into find (#4231)
* chore: upgrade vitest * feat(database): append default sort options into find * chore: test * chore: test * fix: test * fix: test * fix: test * fix: test * fix: test
This commit is contained in:
parent
3651c6ccca
commit
7545d05a3d
@ -80,7 +80,7 @@ describe('option parser', () => {
|
|||||||
|
|
||||||
const params = parser.toSequelizeParams();
|
const params = parser.toSequelizeParams();
|
||||||
|
|
||||||
expect(params).toEqual({
|
expect(params).toMatchObject({
|
||||||
attributes: ['id', 'name'],
|
attributes: ['id', 'name'],
|
||||||
include: [
|
include: [
|
||||||
{
|
{
|
||||||
|
@ -24,7 +24,46 @@ describe('find with associations', () => {
|
|||||||
await db.close();
|
await db.close();
|
||||||
});
|
});
|
||||||
|
|
||||||
// 关系数据分页测试
|
it('should append primary key to sort option', async () => {
|
||||||
|
const User = db.collection({
|
||||||
|
name: 'users',
|
||||||
|
fields: [
|
||||||
|
{ name: 'name', type: 'string' },
|
||||||
|
{
|
||||||
|
name: 'age',
|
||||||
|
type: 'integer',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
await db.sync();
|
||||||
|
|
||||||
|
await User.repository.create({
|
||||||
|
values: [
|
||||||
|
{
|
||||||
|
name: 'user1',
|
||||||
|
age: '10',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'user2',
|
||||||
|
age: '10',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'user3',
|
||||||
|
age: '10',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const records = await User.repository.find({
|
||||||
|
sort: ['age'],
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(records[0].get('name')).toBe('user1');
|
||||||
|
expect(records[1].get('name')).toBe('user2');
|
||||||
|
expect(records[2].get('name')).toBe('user3');
|
||||||
|
});
|
||||||
|
|
||||||
it('should filter with associations by pagination', async () => {
|
it('should filter with associations by pagination', async () => {
|
||||||
const Org = db.collection({
|
const Org = db.collection({
|
||||||
name: 'organizations',
|
name: 'organizations',
|
||||||
|
@ -108,18 +108,29 @@ export class OptionsParser {
|
|||||||
*/
|
*/
|
||||||
protected parseSort(filterParams) {
|
protected parseSort(filterParams) {
|
||||||
let sort = this.options?.sort || [];
|
let sort = this.options?.sort || [];
|
||||||
|
|
||||||
if (typeof sort === 'string') {
|
if (typeof sort === 'string') {
|
||||||
sort = sort.split(',');
|
sort = sort.split(',');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const primaryKeyField = this.model.primaryKeyAttribute;
|
||||||
|
|
||||||
|
if (primaryKeyField && !this.options?.group) {
|
||||||
|
if (!sort.includes(primaryKeyField)) {
|
||||||
|
sort.push(primaryKeyField);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const orderParams = [];
|
const orderParams = [];
|
||||||
|
|
||||||
for (const sortKey of sort) {
|
for (const sortKey of sort) {
|
||||||
let direction = sortKey.startsWith('-') ? 'DESC' : 'ASC';
|
let direction = sortKey.startsWith('-') ? 'DESC' : 'ASC';
|
||||||
const sortField: Array<any> = sortKey.replace('-', '').split('.');
|
const sortField: Array<any> = sortKey.startsWith('-') ? sortKey.replace('-', '').split('.') : sortKey.split('.');
|
||||||
|
|
||||||
if (this.database.inDialect('postgres', 'sqlite')) {
|
if (this.database.inDialect('postgres', 'sqlite')) {
|
||||||
direction = `${direction} NULLS LAST`;
|
direction = `${direction} NULLS LAST`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// handle sort by association
|
// handle sort by association
|
||||||
if (sortField.length > 1) {
|
if (sortField.length > 1) {
|
||||||
let associationModel = this.model;
|
let associationModel = this.model;
|
||||||
|
@ -339,7 +339,10 @@ export class Repository<TModelAttributes extends {} = any, TCreationAttributes e
|
|||||||
});
|
});
|
||||||
|
|
||||||
options.optionsTransformer?.(queryOptions);
|
options.optionsTransformer?.(queryOptions);
|
||||||
const hasAssociationFilter = () => {
|
|
||||||
|
delete queryOptions.order;
|
||||||
|
|
||||||
|
const hasAssociationFilter = (() => {
|
||||||
if (queryOptions.include && queryOptions.include.length > 0) {
|
if (queryOptions.include && queryOptions.include.length > 0) {
|
||||||
const filterInclude = queryOptions.include.filter((include) => {
|
const filterInclude = queryOptions.include.filter((include) => {
|
||||||
return (
|
return (
|
||||||
@ -350,9 +353,9 @@ export class Repository<TModelAttributes extends {} = any, TCreationAttributes e
|
|||||||
return filterInclude.length > 0;
|
return filterInclude.length > 0;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
};
|
})();
|
||||||
|
|
||||||
if (hasAssociationFilter()) {
|
if (hasAssociationFilter) {
|
||||||
const primaryKeyField = this.model.primaryKeyAttribute;
|
const primaryKeyField = this.model.primaryKeyAttribute;
|
||||||
const queryInterface = this.database.sequelize.getQueryInterface();
|
const queryInterface = this.database.sequelize.getQueryInterface();
|
||||||
|
|
||||||
|
@ -7,17 +7,18 @@
|
|||||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { MockServer, createMockServer } from '@nocobase/test';
|
import { createMockServer, MockServer } from '@nocobase/test';
|
||||||
import compose from 'koa-compose';
|
import compose from 'koa-compose';
|
||||||
import { vi } from 'vitest';
|
import { vi } from 'vitest';
|
||||||
import {
|
import {
|
||||||
cacheMiddleware,
|
cacheMiddleware,
|
||||||
|
checkPermission,
|
||||||
parseBuilder,
|
parseBuilder,
|
||||||
parseFieldAndAssociations,
|
parseFieldAndAssociations,
|
||||||
checkPermission,
|
|
||||||
postProcess,
|
|
||||||
parseVariables,
|
parseVariables,
|
||||||
|
postProcess,
|
||||||
} from '../actions/query';
|
} from '../actions/query';
|
||||||
|
|
||||||
const formatter = await import('../actions/formatter');
|
const formatter = await import('../actions/formatter');
|
||||||
|
|
||||||
describe('query', () => {
|
describe('query', () => {
|
||||||
@ -25,6 +26,9 @@ describe('query', () => {
|
|||||||
const sequelize = {
|
const sequelize = {
|
||||||
fn: vi.fn().mockImplementation((fn: string, field: string) => [fn, field]),
|
fn: vi.fn().mockImplementation((fn: string, field: string) => [fn, field]),
|
||||||
col: vi.fn().mockImplementation((field: string) => field),
|
col: vi.fn().mockImplementation((field: string) => field),
|
||||||
|
getDialect() {
|
||||||
|
return false;
|
||||||
|
},
|
||||||
};
|
};
|
||||||
let ctx: any;
|
let ctx: any;
|
||||||
let app: MockServer;
|
let app: MockServer;
|
||||||
@ -307,15 +311,19 @@ describe('query', () => {
|
|||||||
ctx.body = value;
|
ctx.body = value;
|
||||||
await next();
|
await next();
|
||||||
});
|
});
|
||||||
|
|
||||||
class MockCache {
|
class MockCache {
|
||||||
map: Map<string, any> = new Map();
|
map: Map<string, any> = new Map();
|
||||||
|
|
||||||
get(key: string) {
|
get(key: string) {
|
||||||
return this.map.get(key);
|
return this.map.get(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
set(key: string, value: any) {
|
set(key: string, value: any) {
|
||||||
this.map.set(key, value);
|
this.map.set(key, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let ctx: any;
|
let ctx: any;
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
const cache = new MockCache();
|
const cache = new MockCache();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user