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()

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: typeorm#3909
  • Loading branch information
tiagojsag committed Nov 3, 2021
1 parent 25271d7 commit 85ea143
Show file tree
Hide file tree
Showing 7 changed files with 1,210 additions and 16 deletions.
5 changes: 5 additions & 0 deletions docs/tree-entities.md
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,9 @@ const treeCategories = await repository.findTrees();

const treeCategoriesWithRelations = await repository.findTrees({ relations: ["sites"] });
// automatically joins the sites relation

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 @@ -228,6 +231,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
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 @@ -6,6 +6,7 @@ import { TypeORMError } from "../error/TypeORMError";
import { FindTreeOptions } from "../find-options/FindTreeOptions";
import { FindRelationsNotFoundError } from "../error";
import { FindOptionsUtils } from "../find-options/FindOptionsUtils";
import { FindTreesOptions } from "./FindTreesOptions";

/**
* Repository with additional functions to work with trees.
Expand Down Expand Up @@ -89,7 +90,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 @@ -287,14 +292,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

0 comments on commit 85ea143

Please sign in to comment.