test: filter nested association (#1802)

* test: filter nested association

* chore: log

* fix: tree instance to json

* ci: test case

* chore: test

* chore: log

---------

Co-authored-by: chenos <chenlinxh@gmail.com>
This commit is contained in:
ChengLei Shao 2023-05-06 07:55:26 +08:00 committed by GitHub
parent d0edc6ce3f
commit b8c85c91b4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 238 additions and 12 deletions

View File

@ -68,7 +68,6 @@ jobs:
node-version: ${{ matrix.node_version }} node-version: ${{ matrix.node_version }}
cache: 'yarn' cache: 'yarn'
- run: yarn install - run: yarn install
# - run: yarn build
- name: Test with postgres - name: Test with postgres
run: yarn nocobase install -f && yarn test run: yarn nocobase install -f && yarn test
env: env:

View File

@ -43,12 +43,20 @@ async function listWithPagination(ctx: Context) {
const repository = getRepositoryFromParams(ctx); const repository = getRepositoryFromParams(ctx);
const [rows, count] = await repository.findAndCount({ const options = {
context: ctx, context: ctx,
...findArgs(ctx), ...findArgs(ctx),
...pageArgsToLimitArgs(parseInt(String(page)), parseInt(String(pageSize))), ...pageArgsToLimitArgs(parseInt(String(page)), parseInt(String(pageSize))),
};
Object.keys(options).forEach((key) => {
if (options[key] === undefined) {
delete options[key];
}
}); });
const [rows, count] = await repository.findAndCount(options);
ctx.body = { ctx.body = {
count, count,
rows, rows,

View File

@ -1,7 +1,101 @@
import { mockDatabase } from '../index'; import { mockDatabase } from '../index';
import Database from '@nocobase/database'; import Database from '@nocobase/database';
import { Collection } from '../../collection'; import { Collection } from '../../collection';
import { OptionsParser } from '../../options-parser';
describe('find with associations', () => {
let db: Database;
beforeEach(async () => {
db = mockDatabase({
tablePrefix: '',
});
await db.clean({ drop: true });
});
afterEach(async () => {
await db.close();
});
it('should filter by association field', async () => {
const User = db.collection({
name: 'users',
tree: 'adjacency-list',
fields: [
{ type: 'string', name: 'name' },
{ type: 'hasMany', name: 'posts', target: 'posts', foreignKey: 'user_id' },
{
type: 'belongsTo',
name: 'parent',
foreignKey: 'parent_id',
treeParent: true,
},
{
type: 'hasMany',
name: 'children',
foreignKey: 'parent_id',
treeChildren: true,
},
],
});
const Post = db.collection({
name: 'posts',
fields: [
{ type: 'string', name: 'title' },
{ type: 'belongsTo', name: 'user', target: 'users', foreignKey: 'user_id' },
],
});
await db.sync();
expect(User.options.tree).toBeTruthy();
await User.repository.create({
values: [
{
name: 'u1',
posts: [
{
title: 'u1p1',
},
],
children: [
{
name: 'u2',
posts: [
{
title: '标题2',
},
],
},
],
},
],
});
const filter = {
$and: [
{
children: {
posts: {
title: {
$eq: '标题2',
},
},
},
},
],
};
const [findResult, count] = await User.repository.findAndCount({
filter,
offset: 0,
limit: 20,
});
expect(findResult[0].get('name')).toEqual('u1');
});
});
describe('repository find', () => { describe('repository find', () => {
let db: Database; let db: Database;

View File

@ -351,10 +351,9 @@ export class Repository<TModelAttributes extends {} = any, TCreationAttributes e
* @param options * @param options
*/ */
async findAndCount(options?: FindAndCountOptions): Promise<[Model[], number]> { async findAndCount(options?: FindAndCountOptions): Promise<[Model[], number]> {
const transaction = await this.getTransaction(options);
options = { options = {
...options, ...options,
transaction, transaction: await this.getTransaction(options),
}; };
const count = await this.count(options); const count = await this.count(options);

View File

@ -44,9 +44,7 @@ export class AdjacencyListRepository extends Repository {
}); });
} }
const childInstances = (await super.find(findChildrenOptions)).map((r) => { const childInstances = await super.find(findChildrenOptions);
return r.toJSON();
});
const nodeMap = {}; const nodeMap = {};
@ -64,10 +62,10 @@ export class AdjacencyListRepository extends Repository {
return []; return [];
} }
return children.map((child) => ({ return children.map((child) => {
...child, child.setDataValue(childrenKey, buildTree(child.id));
[childrenKey]: buildTree(child.id), return child;
})); });
} }
for (const parent of parentNodes) { for (const parent of parentNodes) {

View File

@ -0,0 +1,128 @@
import { Database } from '@nocobase/database';
import { MockServer } from '@nocobase/test';
import { createApp } from '../index';
describe('find with association', () => {
let app: MockServer;
let agent;
let db: Database;
beforeEach(async () => {
app = await createApp();
agent = app.agent();
db = app.db;
});
afterEach(async () => {
await app.destroy();
});
it('should filter by association field', async () => {
await db.getRepository('collections').create({
values: {
name: 'users',
tree: 'adjacency-list',
fields: [
{ type: 'string', name: 'name' },
{ type: 'hasMany', name: 'posts', target: 'posts', foreignKey: 'user_id' },
{
type: 'belongsTo',
name: 'parent',
foreignKey: 'parent_id',
treeParent: true,
target: 'users',
},
{
type: 'hasMany',
name: 'children',
foreignKey: 'parent_id',
treeChildren: true,
target: 'users',
},
],
},
context: {},
});
await db.getRepository('collections').create({
values: {
name: 'posts',
fields: [
{ type: 'string', name: 'title' },
{ type: 'belongsTo', name: 'user', target: 'users', foreignKey: 'user_id' },
],
},
context: {},
});
const UserCollection = db.getCollection('users');
expect(UserCollection.options.tree).toBeTruthy();
await db.getRepository('users').create({
values: [
{
name: 'u1',
posts: [
{
title: 'u1p1',
},
],
children: [
{
name: 'u2',
posts: [
{
title: '标题2',
},
],
},
],
},
{
name: 'u3',
children: [
{
name: 'u4',
posts: [
{
title: '标题五',
},
],
},
],
},
],
});
const filter = {
$and: [
{
children: {
posts: {
title: {
$eq: '标题五',
},
},
},
},
],
};
const items = await db.getRepository('users').find({
filter,
appends: ['children'],
});
expect(items[0].name).toEqual('u3');
const response2 = await agent.resource('users').list({
filter,
appends: ['children'],
});
expect(response2.statusCode).toEqual(200);
expect(response2.body.data[0].name).toEqual('u3');
});
});