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:
ChengLei Shao 2024-05-06 21:03:30 +08:00 committed by GitHub
parent 3651c6ccca
commit 7545d05a3d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 70 additions and 9 deletions

View File

@ -80,7 +80,7 @@ describe('option parser', () => {
const params = parser.toSequelizeParams();
expect(params).toEqual({
expect(params).toMatchObject({
attributes: ['id', 'name'],
include: [
{

View File

@ -24,7 +24,46 @@ describe('find with associations', () => {
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 () => {
const Org = db.collection({
name: 'organizations',

View File

@ -108,18 +108,29 @@ export class OptionsParser {
*/
protected parseSort(filterParams) {
let sort = this.options?.sort || [];
if (typeof sort === 'string') {
sort = sort.split(',');
}
const primaryKeyField = this.model.primaryKeyAttribute;
if (primaryKeyField && !this.options?.group) {
if (!sort.includes(primaryKeyField)) {
sort.push(primaryKeyField);
}
}
const orderParams = [];
for (const sortKey of sort) {
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')) {
direction = `${direction} NULLS LAST`;
}
// handle sort by association
if (sortField.length > 1) {
let associationModel = this.model;

View File

@ -339,7 +339,10 @@ export class Repository<TModelAttributes extends {} = any, TCreationAttributes e
});
options.optionsTransformer?.(queryOptions);
const hasAssociationFilter = () => {
delete queryOptions.order;
const hasAssociationFilter = (() => {
if (queryOptions.include && queryOptions.include.length > 0) {
const filterInclude = queryOptions.include.filter((include) => {
return (
@ -350,9 +353,9 @@ export class Repository<TModelAttributes extends {} = any, TCreationAttributes e
return filterInclude.length > 0;
}
return false;
};
})();
if (hasAssociationFilter()) {
if (hasAssociationFilter) {
const primaryKeyField = this.model.primaryKeyAttribute;
const queryInterface = this.database.sequelize.getQueryInterface();

View File

@ -7,17 +7,18 @@
* 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 { vi } from 'vitest';
import {
cacheMiddleware,
checkPermission,
parseBuilder,
parseFieldAndAssociations,
checkPermission,
postProcess,
parseVariables,
postProcess,
} from '../actions/query';
const formatter = await import('../actions/formatter');
describe('query', () => {
@ -25,6 +26,9 @@ describe('query', () => {
const sequelize = {
fn: vi.fn().mockImplementation((fn: string, field: string) => [fn, field]),
col: vi.fn().mockImplementation((field: string) => field),
getDialect() {
return false;
},
};
let ctx: any;
let app: MockServer;
@ -307,15 +311,19 @@ describe('query', () => {
ctx.body = value;
await next();
});
class MockCache {
map: Map<string, any> = new Map();
get(key: string) {
return this.map.get(key);
}
set(key: string, value: any) {
this.map.set(key, value);
}
}
let ctx: any;
beforeEach(() => {
const cache = new MockCache();