From 7545d05a3db41383eba9e5fabfb7d2102cc73a99 Mon Sep 17 00:00:00 2001 From: ChengLei Shao Date: Mon, 6 May 2024 21:03:30 +0800 Subject: [PATCH] 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 --- .../src/__tests__/option-parser.test.ts | 2 +- .../src/__tests__/repository/find.test.ts | 41 ++++++++++++++++++- packages/core/database/src/options-parser.ts | 13 +++++- packages/core/database/src/repository.ts | 9 ++-- .../src/server/__tests__/query.test.ts | 14 +++++-- 5 files changed, 70 insertions(+), 9 deletions(-) diff --git a/packages/core/database/src/__tests__/option-parser.test.ts b/packages/core/database/src/__tests__/option-parser.test.ts index a38fd7ee88..165415580a 100644 --- a/packages/core/database/src/__tests__/option-parser.test.ts +++ b/packages/core/database/src/__tests__/option-parser.test.ts @@ -80,7 +80,7 @@ describe('option parser', () => { const params = parser.toSequelizeParams(); - expect(params).toEqual({ + expect(params).toMatchObject({ attributes: ['id', 'name'], include: [ { diff --git a/packages/core/database/src/__tests__/repository/find.test.ts b/packages/core/database/src/__tests__/repository/find.test.ts index 29843aa363..fb8981ae8a 100644 --- a/packages/core/database/src/__tests__/repository/find.test.ts +++ b/packages/core/database/src/__tests__/repository/find.test.ts @@ -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', diff --git a/packages/core/database/src/options-parser.ts b/packages/core/database/src/options-parser.ts index 78ed99a44b..df3603e20d 100644 --- a/packages/core/database/src/options-parser.ts +++ b/packages/core/database/src/options-parser.ts @@ -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 = sortKey.replace('-', '').split('.'); + const sortField: Array = 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; diff --git a/packages/core/database/src/repository.ts b/packages/core/database/src/repository.ts index 4338430dca..463bb5e138 100644 --- a/packages/core/database/src/repository.ts +++ b/packages/core/database/src/repository.ts @@ -339,7 +339,10 @@ export class Repository { + + 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 0; } return false; - }; + })(); - if (hasAssociationFilter()) { + if (hasAssociationFilter) { const primaryKeyField = this.model.primaryKeyAttribute; const queryInterface = this.database.sequelize.getQueryInterface(); diff --git a/packages/plugins/@nocobase/plugin-data-visualization/src/server/__tests__/query.test.ts b/packages/plugins/@nocobase/plugin-data-visualization/src/server/__tests__/query.test.ts index 44919a942d..13e4524940 100644 --- a/packages/plugins/@nocobase/plugin-data-visualization/src/server/__tests__/query.test.ts +++ b/packages/plugins/@nocobase/plugin-data-visualization/src/server/__tests__/query.test.ts @@ -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 = 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();