Class TreeModel<T>Abstract

Model implementation with helpers for querying tree-structured data.

This works by using a modified pre-order traversal to number the tree nodes with a left- and right-side numbers. For example:

                    (1) A (14)
                        |
      (2) B (9)    (10) C (11)    (12) D (14)
          |
(3) E (6)   (7) G (8)
    |
(4) F (5)

These numbers are stored, by default, in left_num and right_num columns. The subtree() method returns a HasSubtree relation which loads the subtree of a model and recursively nests the nodes.

You can use the children() helper method to get a collection of the immediate children of this node, which also have the subtree set.

To query the model without loading the entire subtree, use the without() method on the ModelBuilder. For example:

MyModel.query<MyModel>().without('subtree')

Type Parameters

Hierarchy

Constructors

  • Type Parameters

    Parameters

    • Optional values: {
          [key: string]: any;
      }

      Pre-fill the model's properties from the given values. Calls boot() under the hood.

      • [key: string]: any

    Returns TreeModel<T>

Properties

awareOfContainerLifecycle: true = ...
bus: Bus<Event>
dirtySourceRow?: QueryRow

Database fields that should be run on the next save, even if the fields are not mapped to members on the model.

logging: Logging
originalSourceRow?: QueryRow

The original row fetched from the database.

relationCache: Collection<{
    accessor: string | symbol;
    relation: Relation<T, any, any>;
}> = ...

Cache of relation instances by property accessor. This is used by the @Relation() decorator to cache Relation instances.

removedChildren: Collection<TreeModel<T>> = ...
scopes: Collection<{
    accessor: string | Instantiable<Scope>;
    scope: ScopeClosure;
}> = ...
subscribers: Collection<BusSubscriber<ModelEvent<T>>> = ...

Local listeners subscribed to events on this bus.

subscriptions: Collection<BusInternalSubscription> = ...
uuid: string = ...
with: (keyof T)[] = ...

to eager-load the subtree by default

CREATED_AT: null | string = 'created_at'

Optionally, the timestamp field set on creation.

UPDATED_AT: null | string = 'updated_at'

Optionally, the timestamp field set op update.

appends: string[] = []

Array of additional fields on the class that should be included in the object serializations.

connection: string = 'default'

The name of the connection this model should run through.

key: string

The name of the column that uniquely identifies this model.

leftTreeField: "left_num" = 'left_num'

The table column where the left tree number is stored.

masks: string[] = []

Array of fields on the class that should be excluded from the object serializations.

parentIdField: "parent_id" = 'parent_id'
populateKeyOnInsert: boolean = false

If false (default), the primary key will be excluded from INSERTs.

rightTreeField: "right_num" = 'right_num'

The table column where the right tree number is stored.

table: string

The name of the table this model is stored in.

timestamps: boolean = true

If true, the CREATED_AT and UPDATED_AT columns will be automatically set.

Accessors

  • get isDirtyCheck(): ((field: ModelField) => boolean)
  • Protected

    Get a wrapped function that compares whether the given model field on the current instance differs from the originally fetched value.

    Used to filter for dirty fields.

    Returns ((field: ModelField) => boolean)

      • (field: ModelField): boolean
      • Protected

        Get a wrapped function that compares whether the given model field on the current instance differs from the originally fetched value.

        Used to filter for dirty fields.

        Parameters

        Returns boolean

Methods

  • Append the other node as a child of this node. Returns the common ancestor from which point the tree was renumbered. You should save the subtree after this.

    Example

    await nodeA.appendChild(nodeB).saveSubtree()
    

    Parameters

    • other: T
    • before: Maybe<T> = undefined

      if provided, other will be inserted before the before child of this node. Otherwise, it will be added as the last child.

    Returns TreeModel<T>

  • Similar to assumeFromSource, but instead of mapping database fields to model properties, this function assumes the object contains a mapping of model properties to the values of those properties.

    Only properties with @Field() annotations will be set.

    Parameters

    • object: {
          [key: string]: any;
      }
      • [key: string]: any

    Returns Promise<TreeModel<T>>

  • Given a row from the database, set the properties on this model that correspond to fields on that database.

    The row maps database fields to values, and the values are set for the properties that they correspond to based on the model's @Field() annotations.

    Parameters

    Returns Promise<TreeModel<T>>

  • Create the inverse of a one-to-many relation. Should be called from a method on the model:

    Example

    class MyModel extends Model<MyModel> {
    @Related()
    public otherModels() {
    return this.hasMany(MyOtherModel)
    }
    }

    class MyOtherModel extends Model<MyOtherModel> {
    @Related()
    public myModels() {
    return this.belongsToMany(MyModel, 'otherModels')
    }
    }

    Type Parameters

    • T2 extends Model<T2, T2>

    Parameters

    Returns HasMany<T, T2>

  • Create the inverse of a one-to-one relation. Should be called from a method on the model:

    Example

    class MyModel extends Model<MyModel> {
    @Related()
    public otherModel() {
    return this.hasOne(MyOtherModel)
    }
    }

    class MyOtherModel extends Model<MyOtherModel> {
    @Related()
    public myModel() {
    return this.belongsToOne(MyModel, 'otherModel')
    }
    }

    Type Parameters

    • T2 extends Model<T2, T2>

    Parameters

    Returns HasOne<T, T2>

  • Initialize the model's properties from the given values and do any other initial setup.

    values can optionally be an object mapping model properties to the values of those properties. Only properties with @Field() annotations will be set.

    Parameters

    • Optional values: {
          [key: string]: unknown;
      }
      • [key: string]: unknown

    Returns void

  • Protected

    Call all local listeners for the given event. Returns true if the propagation of the event should be halted.

    Parameters

    • event: ModelEvent<T>

    Returns Promise<boolean>

  • Get a query row mapping database columns to values for properties on this model that (1) have @Field() annotations and (2) have been modified since the record was fetched from the database or created.

    Returns QueryRow

  • Fetch a fresh instance of this record from the database.

    This returns a NEW instance of the SAME record by matching on the primary key. It does NOT change the current instance of the record.

    Returns Promise<undefined | Model<T>>

  • Get the method with the given name from this class, bound to this class.

    Returns

    function

    Parameters

    • methodName: string

    Returns ((...args: any[]) => any)

      • (...args: any[]): any
      • Get the method with the given name from this class, bound to this class.

        Returns

        function

        Parameters

        • Rest ...args: any[]

        Returns any

  • Returns an array of MODEL fields that have been modified since this record was fetched from the database or created.

    Returns string[]

  • Create a new one-to-one relation instance. Should be called from a method on the model:

    Example

    class MyModel extends Model<MyModel> {
    @Related()
    public otherModels() {
    return this.hasMany(MyOtherModel)
    }
    }

    Type Parameters

    • T2 extends Model<T2, T2>

    Parameters

    • related: Instantiable<T2>
    • Optional foreignKeyOverride: keyof T & string
    • Optional localKeyOverride: keyof T2 & string

    Returns HasMany<T, T2>

  • Create a new one-to-one relation instance. Should be called from a method on the model:

    Example

    class MyModel extends Model<MyModel> {
    @Related()
    public otherModel() {
    return this.hasOne(MyOtherModel)
    }
    }

    Type Parameters

    • T2 extends Model<T2, T2>

    Parameters

    • related: Instantiable<T2>
    • Optional foreignKeyOverride: keyof T & string
    • Optional localKeyOverride: keyof T2 & string

    Returns HasOne<T, T2>

  • Returns true if the other model refers to the same database record as this instance.

    This is done by comparing the qualified primary keys.

    Parameters

    Returns boolean

  • Returns true if none of the fields on this model have been modified since they were fetched from the database (and all exist in the database).

    Only fields with @Field() annotations are checked.

    Returns boolean

  • Returns true if any of the fields on this model have been modified since they were fetched from the database (or ones that were never saved to the database).

    Only fields with @Field() annotations are checked.

    Returns boolean

  • Get the value of the primary key of this model, if it exists.

    Returns string | number

  • Call the make() method on the global container.

    Type Parameters

    • T

    Parameters

    • target: any
    • Rest ...parameters: any[]

    Returns T

  • Return an object of only the given properties on this model.

    Example

    Assume a is an instance of some model A with the given fields.

    const a = new A({ field1: 'field1 value', field2: 'field2 value', id: 123 })

    a.only('field1', 'id) // => {field1: 'field1 value', id: 123}

    Parameters

    • Rest ...fields: string[]

    Returns QueryRow

  • Populates an instance of the model with the same database fields that are set on this model, with the exclusion of the primary key.

    Useful for inserting copies of records.

    Example

    Assume a record, a, is an instance of some model A with the given fields.

    const a = A.find(123)  // => A{id: 123, name: 'some_name', other_field: 'a value'}

    const b = a.populate(new A) // => A{name: 'some_name', other_field: 'a value'}

    Parameters

    • model: T

    Returns Promise<T>

  • Given the name of a column, return the qualified name of the column as it could appear in a query.

    Example

    modelInstance.qualify('id')  // => 'model_table_name.id'
    

    Parameters

    • column: string

    Returns string

  • Return the qualified name of the column corresponding to the model's primary key.

    Example

    class A extends Model<A> {
    protected static table = 'table_a'
    protected static key = 'a_id'
    }

    const a = new A()
    a.qualifyKey() // => 'table_a.a_id'

    Returns string

  • Re-load the currently-loaded database fields from the table.

    Overwrites any un-persisted changes in the current instance.

    Returns Promise<void>

  • Remove this node from the tree structure. If the node existed in the tree structure and was removed, this method returns the tree node that was renumbered. You should save the subtree after this.

    Example

    await nodeA.removeFromTree().saveSubtree()
    

    Returns Maybe<TreeModel<T>>

  • Update the preorder traversal numbering for this node and its subtree. After renumbering, you probably also want to save the subtree nodes to persist the tree into the database.

    Example

    await model.renumber().saveSubtree()
    

    Returns TreeModel<T>

  • Persist the model into the database. If the model already exists, perform an update on its fields. Otherwise, insert a new row with its fields.

    Passing the withoutTimestamps will prevent the configured CREATED_AT/UPDATED_AT timestamps from being updated.

    Parameters

    • withoutTimestamps: {
          withoutTimestamps: undefined | boolean;
      } = {}
      • withoutTimestamps: undefined | boolean

    Returns Promise<Model<T>>

  • Call the save() method on all nodes in this node & its subtree.

    Returns Promise<void>

  • Sets a value in the override row and (if applicable) associated class property for the given database column.

    Parameters

    • key: string
    • value: unknown

    Returns TreeModel<T>

  • Protected

    Sets a property on this to the value of a given property in object.

    Parameters

    • thisFieldName: string | symbol
    • objectFieldName: string
    • object: QueryRow

    Returns void

  • Returns true if the given event should be pushed to connected event busses.

    Parameters

    • event: ModelEvent<T>

    Returns Promise<boolean>

  • Get normalized values of the configured CREATED_AT/UPDATED_AT fields for this model.

    Example

    user.timestamps()  // => {updated: Date, created: Date}
    

    Returns {
        created?: Date;
        updated?: Date;
    }

    • Optional created?: Date
    • Optional updated?: Date
  • Cast the model to the base QueryRow object. The resultant object maps DATABASE fields to values, NOT MODEL fields to values.

    Only fields with @Field() annotations will be included.

    Returns QueryRow

  • Returns true if the given field has changed since this model was fetched from the database, or if the given field never existed in the database.

    Parameters

    • field: string

    Returns boolean

  • Find the first instance of this model where the primary key matches key.

    Example

    const user = await UserModel.findByKey(45)
    

    Type Parameters

    • T2 extends Model<T2, T2>

    Parameters

    Returns Promise<undefined | T2>

  • Given the name of a property on the model with a @Field() annotation, return the unqualified name of the database column it corresponds to.

    Parameters

    • modelKey: string

    Returns string

  • Given the name of a column, return the qualified name of the column as it could appear in a query.

    Example

    SomeModel.qualify('col_name')  // => 'model_table_name.col_name'
    

    Parameters

    • column: string

    Returns string

  • Return the qualified name of the column corresponding to the model's primary key.

    Example

    class A extends Model<A> {
    protected static table = 'table_a'
    protected static key = 'a_id'
    }

    A.qualifyKey() // => 'table_a.a_id'

    Returns string

  • Get the QuerySource object for this model as it should be applied to query builders.

    This sets the alias for the model table equal to the table name itself, so it can be referenced explicitly in queries if necessary.

    Returns QuerySource

  • Sets the fully- and un-qualified canonical resolver strings. Intended for use by the Canonical unit.

    Parameters

    • fullyQualifiedResolver: string
    • unqualifiedResolver: string

    Returns void