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 }}
cache: 'yarn'
- run: yarn install
# - run: yarn build
- name: Test with postgres
run: yarn nocobase install -f && yarn test
env:

View File

@ -43,12 +43,20 @@ async function listWithPagination(ctx: Context) {
const repository = getRepositoryFromParams(ctx);
const [rows, count] = await repository.findAndCount({
const options = {
context: ctx,
...findArgs(ctx),
...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 = {
count,
rows,

View File

@ -1,7 +1,101 @@
import { mockDatabase } from '../index';
import Database from '@nocobase/database';
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', () => {
let db: Database;

View File

@ -351,10 +351,9 @@ export class Repository<TModelAttributes extends {} = any, TCreationAttributes e
* @param options
*/
async findAndCount(options?: FindAndCountOptions): Promise<[Model[], number]> {
const transaction = await this.getTransaction(options);
options = {
...options,
transaction,
transaction: await this.getTransaction(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) => {
return r.toJSON();
});
const childInstances = await super.find(findChildrenOptions);
const nodeMap = {};
@ -64,10 +62,10 @@ export class AdjacencyListRepository extends Repository {
return [];
}
return children.map((child) => ({
...child,
[childrenKey]: buildTree(child.id),
}));
return children.map((child) => {
child.setDataValue(childrenKey, buildTree(child.id));
return child;
});
}
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');
});
});