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:
parent
d0edc6ce3f
commit
b8c85c91b4
1
.github/workflows/nocobase-test-lite.yml
vendored
1
.github/workflows/nocobase-test-lite.yml
vendored
@ -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:
|
||||||
|
@ -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,
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
|
@ -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) {
|
||||||
|
@ -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');
|
||||||
|
});
|
||||||
|
});
|
Loading…
Reference in New Issue
Block a user