diff --git a/packages/core/database/src/relation-repository/relation-repository.ts b/packages/core/database/src/relation-repository/relation-repository.ts index 3f1059521a..cf7dee06f5 100644 --- a/packages/core/database/src/relation-repository/relation-repository.ts +++ b/packages/core/database/src/relation-repository/relation-repository.ts @@ -55,6 +55,44 @@ export abstract class RelationRepository { return this.db.getCollection(this.targetModel.name); } + abstract find(options?: FindOptions): Promise; + + async chunk( + options: FindOptions & { chunkSize: number; callback: (rows: Model[], options: FindOptions) => Promise }, + ) { + const { chunkSize, callback, limit: overallLimit } = options; + const transaction = await this.getTransaction(options); + + let offset = 0; + let totalProcessed = 0; + + // eslint-disable-next-line no-constant-condition + while (true) { + // Calculate the limit for the current chunk + const currentLimit = overallLimit !== undefined ? Math.min(chunkSize, overallLimit - totalProcessed) : chunkSize; + + const rows = await this.find({ + ...options, + limit: currentLimit, + offset, + transaction, + }); + + if (rows.length === 0) { + break; + } + + await callback(rows, options); + + offset += currentLimit; + totalProcessed += rows.length; + + if (overallLimit !== undefined && totalProcessed >= overallLimit) { + break; + } + } + } + convertTk(options: any) { let tk = options; if (typeof options === 'object' && options['tk']) { diff --git a/packages/plugins/@nocobase/plugin-action-export/src/server/__tests__/export-action.test.ts b/packages/plugins/@nocobase/plugin-action-export/src/server/__tests__/export-action.test.ts new file mode 100644 index 0000000000..0501ae1451 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-action-export/src/server/__tests__/export-action.test.ts @@ -0,0 +1,89 @@ +import { createMockServer, MockServer } from '@nocobase/test'; +import fs from 'fs'; +import os from 'os'; +import path from 'path'; +import XLSX from 'xlsx'; + +describe('export action', () => { + let app: MockServer; + + beforeEach(async () => { + app = await createMockServer({ + plugins: ['nocobase'], + acl: false, + }); + }); + + it('should export with association repository', async () => { + await app.db.getRepository('collections').create({ + values: { + name: 'posts', + fields: [ + { + name: 'title', + type: 'string', + }, + { + name: 'comments', + type: 'hasMany', + target: 'comments', + }, + ], + }, + context: {}, + }); + + await app.db.getRepository('collections').create({ + values: { + name: 'comments', + fields: [ + { + name: 'content', + type: 'string', + }, + ], + }, + context: {}, + }); + + await app.db.getRepository('posts').create({ + values: [ + { + title: 'post1', + comments: [ + { + content: 'comment1', + }, + ], + }, + { + title: 'post2', + comments: [ + { + content: 'comment2', + }, + ], + }, + ], + }); + + const res = await app + .agent() + .resource('posts.comments', 1) + .export({ + values: { + columns: [{ dataIndex: ['content'], defaultTitle: 'content' }], + }, + }); + + expect(res.status).toBe(200); + + const buffer = res.body; + + const workbook = XLSX.read(buffer, { type: 'buffer' }); + const sheetName = workbook.SheetNames[0]; + const sheet = workbook.Sheets[sheetName]; + const rows = XLSX.utils.sheet_to_json(sheet); + expect(rows.length).toBe(1); + }); +}); diff --git a/packages/plugins/@nocobase/plugin-action-export/src/server/actions/export-xlsx.ts b/packages/plugins/@nocobase/plugin-action-export/src/server/actions/export-xlsx.ts index f37f94ea8a..69b6eba309 100644 --- a/packages/plugins/@nocobase/plugin-action-export/src/server/actions/export-xlsx.ts +++ b/packages/plugins/@nocobase/plugin-action-export/src/server/actions/export-xlsx.ts @@ -33,6 +33,7 @@ async function exportXlsxAction(ctx: Context, next: Next) { const xlsxExporter = new XlsxExporter({ collectionManager: dataSource.collectionManager, collection, + repository, columns, findOptions: { filter, diff --git a/packages/plugins/@nocobase/plugin-action-export/src/server/xlsx-exporter.ts b/packages/plugins/@nocobase/plugin-action-export/src/server/xlsx-exporter.ts index 9b92b05d39..71201bb952 100644 --- a/packages/plugins/@nocobase/plugin-action-export/src/server/xlsx-exporter.ts +++ b/packages/plugins/@nocobase/plugin-action-export/src/server/xlsx-exporter.ts @@ -29,6 +29,7 @@ type ExportColumn = { type ExportOptions = { collectionManager: ICollectionManager; collection: ICollection; + repository?: any; columns: Array; findOptions?: FindOptions; chunkSize?: number; @@ -51,7 +52,7 @@ class XlsxExporter { constructor(private options: ExportOptions) {} async run(ctx?): Promise { - const { collection, columns, chunkSize } = this.options; + const { collection, columns, chunkSize, repository } = this.options; const workbook = XLSX.utils.book_new(); const worksheet = XLSX.utils.sheet_new(); @@ -63,7 +64,7 @@ class XlsxExporter { let startRowNumber = 2; - await collection.repository.chunk({ + await (repository || collection.repository).chunk({ ...this.getFindOptions(), chunkSize: chunkSize || 200, callback: async (rows, options) => {