Skip to content

Commit

Permalink
feat: add depth limiter optional parameter when loading nested trees …
Browse files Browse the repository at this point in the history
…using TreeRepository's findTrees() and findDescendantsTree() (#7926)

Modified TreeRepository's findTrees() and findDescendantsTree() public methods to now accept an optional object with config options.
For now, said object contains a depth parameter that, if set, will limit how far down the tree those methods will crawl and retrieve nested entities.

BREAKING CHANGE: TreeRepository's protected method buildChildrenEntityTree() now requires a 4th argument. Anyone affected by this break should also review and update their implementation, otherwise this feature will not work.

Closes: #3909
  • Loading branch information
tiagojsag authored Nov 11, 2021
1 parent e80f97d commit 0c44629
Show file tree
Hide file tree
Showing 7 changed files with 1,212 additions and 18 deletions.
9 changes: 7 additions & 2 deletions docs/tree-entities.md
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,9 @@ There are other special methods to work with tree entities through `TreeReposito
```typescript
const treeCategories = await repository.findTrees();
// returns root categories with sub categories inside

const treeCategoriesWithLimitedDepth = await repository.findTrees({ depth: 2 });
// returns root categories with sub categories inside, up to depth 2
```

* `findRoots` - Roots are entities that have no ancestors. Finds them all.
Expand All @@ -225,6 +228,8 @@ const children = await repository.findDescendants(parentCategory);
```typescript
const childrenTree = await repository.findDescendantsTree(parentCategory);
// returns all direct subcategories (with its nested categories) of a parentCategory
const childrenTreeWithLimitedDepth = await repository.findDescendantsTree(parentCategory, { depth: 2 });
// returns all direct subcategories (with its nested categories) of a parentCategory, up to depth 2
```

* `createDescendantsQueryBuilder` - Creates a query builder used to get descendants of the entities in a tree.
Expand Down Expand Up @@ -279,10 +284,10 @@ For the following methods, options can be passed:
* findAncestors
* findAncestorsTree

The following options are available:
The following options are available:
* `relations` - Indicates what relations of entity should be loaded (simplified left join form).

Examples:
Examples:
```typescript
const treeCategoriesWithRelations = await repository.findTrees({ relations: ["sites"] });
// automatically joins the sites relation
Expand Down
13 changes: 9 additions & 4 deletions src/find-options/FindTreeOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,14 @@
*/
export interface FindTreeOptions {

/**
* Indicates what relations of entity should be loaded (simplified left join form).
*/
relations?: string[];
/**
* Indicates what relations of entity should be loaded (simplified left join form).
*/
relations?: string[];

/**
* When loading a tree from a TreeRepository, limits the depth of the descendents loaded
*/
depth?: number;

}
11 changes: 11 additions & 0 deletions src/repository/FindTreesOptions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/**
* Special options passed to TreeRepository#findTrees
*/
export interface FindTreesOptions {

/**
* When loading a tree from a TreeRepository, limits the depth of the descendents loaded
*/
depth?: number;

}
15 changes: 12 additions & 3 deletions src/repository/TreeRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {AbstractSqliteDriver} from "../driver/sqlite-abstract/AbstractSqliteDriv
import { TypeORMError } from "../error/TypeORMError";
import { FindTreeOptions } from "../find-options/FindTreeOptions";
import { FindOptionsUtils } from "../find-options/FindOptionsUtils";
import { FindTreesOptions } from "./FindTreesOptions";

/**
* Repository with additional functions to work with trees.
Expand Down Expand Up @@ -64,7 +65,11 @@ export class TreeRepository<Entity> extends Repository<Entity> {

const entities = await qb.getRawAndEntities();
const relationMaps = this.createRelationMaps("treeEntity", entities.raw);
this.buildChildrenEntityTree(entity, entities.entities, relationMaps);
this.buildChildrenEntityTree(entity, entities.entities, relationMaps, {
depth: -1,
...options

});

return entity;
}
Expand Down Expand Up @@ -261,14 +266,18 @@ export class TreeRepository<Entity> extends Repository<Entity> {
});
}

protected buildChildrenEntityTree(entity: any, entities: any[], relationMaps: { id: any, parentId: any }[]): void {
protected buildChildrenEntityTree(entity: any, entities: any[], relationMaps: { id: any, parentId: any }[], options: (FindTreesOptions & { depth: number })): void {
const childProperty = this.metadata.treeChildrenRelation!.propertyName;
if (options.depth === 0) {
entity[childProperty] = [];
return;
}
const parentEntityId = this.metadata.primaryColumns[0].getEntityValue(entity);
const childRelationMaps = relationMaps.filter(relationMap => relationMap.parentId === parentEntityId);
const childIds = new Set(childRelationMaps.map(relationMap => relationMap.id));
entity[childProperty] = entities.filter(entity => childIds.has(this.metadata.primaryColumns[0].getEntityValue(entity)));
entity[childProperty].forEach((childEntity: any) => {
this.buildChildrenEntityTree(childEntity, entities, relationMaps);
this.buildChildrenEntityTree(childEntity, entities, relationMaps, { ...options, depth: options.depth - 1 });
});
}

Expand Down
Loading

0 comments on commit 0c44629

Please sign in to comment.