finish TreeModel implementation and updateMany builder method

This commit is contained in:
2022-09-12 12:36:33 -05:00
parent f63891ef99
commit c966904418
13 changed files with 580 additions and 44 deletions

View File

@@ -5,13 +5,19 @@ import {RelationBuilder} from './RelationBuilder'
import {raw} from '../../dialect/SQLDialect'
import {AbstractBuilder} from '../../builder/AbstractBuilder'
import {ModelBuilder} from '../ModelBuilder'
import {Inject, Injectable} from '../../../di'
import {Logging} from '../../../service/Logging'
/**
* A relation that recursively loads the subtree of a model using
* modified preorder traversal.
*/
@Injectable()
export class HasSubtree<T extends TreeModel<T>> extends Relation<T, T, Collection<T>> {
@Inject()
protected readonly logging!: Logging
/**
* When the relation is loaded, the immediate children of the node.
* @protected
@@ -25,12 +31,24 @@ export class HasSubtree<T extends TreeModel<T>> extends Relation<T, T, Collectio
super(model, model)
}
public flatten(): Collection<T> {
const children = this.getValue()
const subtrees = children.reduce((subtree, child) => subtree.concat(child.subtree().flatten()), collect())
return children.concat(subtrees)
}
/** Manually load the subtree. */
public async load(): Promise<void> {
this.setValue(await this.get())
}
protected get parentValue(): any {
return this.model.key()
}
public query(): RelationBuilder<T> {
return this.builder()
.tap(b => this.model.applyScopes(b))
.select(raw('*'))
.orderByAscending(this.leftTreeField)
}
@@ -50,6 +68,9 @@ export class HasSubtree<T extends TreeModel<T>> extends Relation<T, T, Collectio
public buildEagerQuery(parentQuery: ModelBuilder<T>, result: Collection<T>): ModelBuilder<T> {
const query = this.model.query().without('subtree')
this.logging.debug(`Building eager query for parent: ${parentQuery}`)
this.logging.debug(result)
if ( result.isEmpty() ) {
return query.whereMatchNone()
}
@@ -61,16 +82,20 @@ export class HasSubtree<T extends TreeModel<T>> extends Relation<T, T, Collectio
return
}
query.where(where => {
query.orWhere(where => {
where.where(this.leftTreeField, '>', left)
.where(this.leftTreeField, '<', right)
})
})
this.logging.debug(`Built eager query: ${query}`)
return query
}
public matchResults(possiblyRelated: Collection<T>): Collection<T> {
this.logging.debug('Matching possibly related: ' + possiblyRelated.length)
this.logging.verbose(possiblyRelated)
const modelLeft = this.model.leftTreeNum()
const modelRight = this.model.rightTreeNum()
if ( !modelLeft || !modelRight ) {
@@ -83,6 +108,22 @@ export class HasSubtree<T extends TreeModel<T>> extends Relation<T, T, Collectio
})
}
public appendSubtree(idx: number, subtree: T): void {
if ( !this.instances ) {
throw new RelationNotLoadedError()
}
this.instances = this.instances.put(idx, subtree)
}
public removeSubtree(subtree: TreeModel<T>): void {
if ( !this.instances ) {
throw new RelationNotLoadedError()
}
this.instances = this.instances.filter(x => x.isNot(subtree))
}
public setValue(related: Collection<T>): void {
// `related` contains a flat collection of the subtree nodes, ordered by left key ascending
// We will loop through the related nodes and recursively call `setValue` for our immediate
@@ -126,6 +167,9 @@ export class HasSubtree<T extends TreeModel<T>> extends Relation<T, T, Collectio
children.push(finalState.currentChild)
}
// Set the parent relation on the immediate children we identified
children.each(child => child.parentNode().setValue(this.model))
this.instances = children.sortBy(inst => inst.getOriginalValues()?.[this.leftTreeField])
}