Relations(Dynamic) 
In large-scale business systems, we create numerous models with numerous relations between them. It's impossible to declare all relations using static relations. This is especially true when there are numerous business modules with models scattered across them. Declaring all relations using static relations becomes impractical. Without pre-defined static relations, we need to implement a mechanism for using dynamic relations in our code to enable querying, type inference, and DTO inference and generation
The following uses test-vona module as an example to explain how to use dynamic relations
Four kinds of Relations 
Vona ORM provides 4 kinds of dynamic relations:
| Name | Description | 
|---|---|
| hasOne | 1:1 | 
| belongsTo | 1:1/n:1 | 
| hasMany | 1:n. It can realize the functions of main-details and main-details(multi-level) | 
| belongsToMany | n:n | 
hasOne 
Specify dynamic relations directly in CRUD operations using with
class ServicePost {
  async relationHasOne() {
    // insert
    const postCreate = await this.scope.model.post.insert(
      {
        title: 'Post001',
        postContent: {
          content: 'This is a post',
        },
      },
      {
        with: {
          postContent: $relationDynamic.hasOne(() => ModelPostContent, 'postId'),
        },
      },
    );
    // get
    const post = await this.scope.model.post.get(
      {
        id: postCreate.id,
      },
      {
        with: {
          postContent: $relationDynamic.hasOne(() => ModelPostContent, 'postId', {
            columns: ['id', 'content'],
          }),
        },
      },
    );
    // update
    await this.scope.model.post.update(
      {
        id: postCreate.id,
        title: 'Post001-Update',
        postContent: {
          content: 'This is a post-changed',
        },
      },
      {
        with: {
          postContent: $relationDynamic.hasOne(() => ModelPostContent, 'postId'),
        },
      },
    );
    // delete
    await this.scope.model.post.delete(
      {
        id: postCreate.id,
      },
      {
        with: {
          postContent: $relationDynamic.hasOne(() => ModelPostContent, 'postId'),
        },
      },
    );
  }
}| Name | Description | 
|---|---|
| with.postContent | Relation Name | 
| $relationDynamic.hasOne | 1:1 | 
| ModelPostContent | Taget Model | 
| 'postId' | Foreign key | 
| columns | List of fields to query | 
belongsTo 
The belongsTo relation is only used for query operations. Specify dynamic relations directly in the query operation through with
class ServicePost {
  async relationBelongsTo() {
    const postContent = await this.scope.model.postContent.select({
      with: {
        post: $relationDynamic.belongsTo(() => ModelPostContent, () => ModelPost, 'postId', {
          columns: ['id', 'title'],
        }),
      },
    });
    console.log(postContent[0]?.post?.title);
  }
}| Name | Description | 
|---|---|
| with.post | Relation Name | 
| $relationDynamic.belongsTo | 1:1/n:1 | 
| ModelPostContent | Source Model | 
| ModelPost | Target Model | 
| 'postId' | Foreign key | 
| columns | List of fields to query | 
hasMany 
Specify dynamic relations directly in CRUD operations using with
class ServiceOrder {
  async relationHasMany() {
    // insert
    const orderCreate = await this.scope.model.order.insert(
      {
        orderNo: 'Order001',
        products2: [
          { name: 'Apple' },
          { name: 'Pear' },
        ],
      },
      {
        with: {
          products2: $relationDynamic.hasMany(() => ModelProduct, 'orderId'),
        },
      },
    );
    // get
    await this.scope.model.order.get(
      {
        id: orderCreate.id,
      },
      {
        with: {
          products2: $relationDynamic.hasMany(() => ModelProduct, 'orderId', {
            columns: ['id', 'name', 'price', 'quantity', 'amount'],
          }),
        },
      },
    );
    // update
    await this.scope.model.order.update(
      {
        id: orderCreate.id,
        orderNo: 'Order001-Update',
        products2: [
          // create product: Peach
          { name: 'Peach' },
          // update product: Apple
          { id: orderCreate.products?.[0].id, name: 'Apple-Update' },
          // delete product: Pear
          { id: orderCreate.products?.[1].id, deleted: true },
        ],
      },
      {
        with: {
          products2: $relationDynamic.hasMany(() => ModelProduct, 'orderId'),
        },
      },
    );
    // delete
    await this.scope.model.order.delete(
      {
        id: orderCreate.id,
      },
      {
        with: {
          products2: $relationDynamic.hasMany(() => ModelProduct, 'orderId'),
        },
      },
    );
  }
}- When updating main table data, you can also update detail table data simultaneously (including Insert/Update/Delete operations)
 
| Name | Description | 
|---|---|
| with.products2 | Relation Name. Since test-vona module already defines the static relation products which autoload be set true. For demonstration purposes, a different relation name products2 is used | 
| $relationDynamic.hasMany | 1:n | 
| ModelProduct | Target Model | 
| 'orderId' | Foreign key | 
| columns | List of fields to query | 
belongsToMany 
Directly specifying a dynamic relation using with in CRUD operations requires providing the intermediate model RoleUser. It should be emphasized that the CRUD operations here are for the intermediate model, not the target model
class ServiceUser {
  async relationBelongsToMany() {
    // insert: roles
    const roles = await this.scope.model.role.insertBulk([
      { name: 'role-family' },
      { name: 'role-friend' },
    ]);
    const roleIdFamily = roles[0].id;
    const roleIdFriend = roles[1].id;
    // insert: user
    const userCreate = await this.scope.model.user.insert(
      {
        name: 'Tom',
        roles: [{
          id: roleIdFamily,
        }],
      },
      {
        with: {
          roles: $relationDynamic.belongsToMany(() => ModelRoleUser, () => ModelRole, 'userId', 'roleId'),
        },
      },
    );
    // get: user
    await this.scope.model.user.get(
      {
        id: userCreate.id,
      },
      {
        with: {
          roles: $relationDynamic.belongsToMany(() => ModelRoleUser, () => ModelRole, 'userId', 'roleId', {
            columns: ['id', 'name'],
          }),
        },
      },
    );
    // update: user
    await this.scope.model.user.update(
      {
        id: userCreate.id,
        roles: [
          // delete
          { id: roleIdFamily, deleted: true },
          // insert
          { id: roleIdFriend },
        ],
      },
      {
        with: {
          roles: $relationDynamic.belongsToMany(() => ModelRoleUser, () => ModelRole, 'userId', 'roleId', {
            columns: ['id', 'name'],
          }),
        },
      },
    );
    // delete: user
    await this.scope.model.user.delete(
      {
        id: userCreate.id,
      },
      {
        with: {
          roles: $relationDynamic.belongsToMany(() => ModelRoleUser, () => ModelRole, 'userId', 'roleId'),
        },
      },
    );
  }
}| Name | Description | 
|---|---|
| with.roles | Relation Name | 
| $relationDynamic.belongsToMany | n:n | 
| ModelRoleUser | Middle Model | 
| ModelRole | Target Model | 
| 'userId' | Foreign key | 
| 'roleId' | Foreign key | 
| columns | List of fields to query | 
Tree structure 
Since the tree structure references itself, using a static relation with autoload: true is most convenient and concise way to write code
For demonstration purposes, we'll still implement the tree structure using a dynamic relation
class ServiceCategory {
  async categoryTreeDynamic() {
    // create
    const treeCreate = await this.scope.model.category.insert(
      {
        name: 'Category-1',
        children2: [
          {
            name: 'Category-1-1',
            children2: [
              { name: 'Category-1-1-1' },
            ],
          },
          {
            name: 'Category-1-2',
          },
        ],
      },
      {
        with: {
          children2: $relationDynamic.hasMany(() => ModelCategory, 'categoryIdParent', {
            with: {
              children2: $relationDynamic.hasMany(() => ModelCategory, 'categoryIdParent', {
              }),
            },
          }),
        },
      },
    );
    // get
    const tree = await this.scope.model.category.get(
      {
        id: treeCreate.id,
      },
      {
        with: {
          children2: $relationDynamic.hasMany(() => ModelCategory, 'categoryIdParent', {
            columns: ['id', 'name'],
            with: {
              children2: $relationDynamic.hasMany(() => ModelCategory, 'categoryIdParent', {
                columns: ['id', 'name'],
              }),
            },
          }),
        },
      },
    );
    assert.equal(tree?.children2.length, 2);
    assert.equal(tree?.children2[0].children2.length, 1);
    // update
    await this.scope.model.category.update(
      {
        id: treeCreate.id,
        name: 'Category-1-Update',
        children2: [
          // create
          { name: 'Category-1-3' },
          // update
          { id: treeCreate.children2?.[0].id, name: 'Category-1-1-Update' },
          // delete
          { id: treeCreate.children2?.[1].id, deleted: true },
        ],
      },
      {
        with: {
          children2: $relationDynamic.hasMany(() => ModelCategory, 'categoryIdParent'),
        },
      },
    );
    // delete
    await this.scope.model.category.delete(
      {
        id: treeCreate.id,
      },
      {
        with: {
          children2: $relationDynamic.hasMany(() => ModelCategory, 'categoryIdParent', {
            with: {
              children2: $relationDynamic.hasMany(() => ModelCategory, 'categoryIdParent', {
              }),
            },
          }),
        },
      },
    );
  }
}| Name | Description | 
|---|---|
| with.children2 | Relation Name. Since test-vona module already defines the static relation children which autoload be set true. For demonstration purposes, a different relation name children2 is used | 
| $relationDynamic.hasMany | 1:n | 
| ModelCategory | Target Model | 
| 'categoryIdParent' | Foreign key | 
| columns | List of fields to query | 
Relation Options 
1. $relationDynamic.hasOne/$relationDynamic.belongsTo 
| Name | Description | 
|---|---|
| columns | List of fields to query | 
| include | Specifying nested static relations | 
| with | Specifying nested dynamic relations | 
| meta.client | Define the datasource used by the relation, which can realize cross-datasource relation query | 
| meta.table | Define the data table used by the relation | 
2. $relationDynamic.hasMany/$relationDynamic.belongsToMany 
| Name | Description | 
|---|---|
| columns | List of fields to query | 
| include | Specifying nested static relations | 
| with | Specifying nested dynamic relations | 
| meta.client | Define the datasource used by the relation, which can realize cross-datasource relation query | 
| meta.table | Define the data table used by the relation | 
| distinct | Whether to enable distinct | 
| where | Conditional statement | 
| joins | Related tables | 
| orders | Sorting | 
| limit | Can be used for paginated queries | 
| offset | Can be used for paginated queries | 
| aggrs | Aggregate query | 
| groups | Group-by query | 
Parameter: Model 
When defining a relation, you need to provide the following parameters: Source Model, Target Model, and Intermediate Model. The following types are supported:
| Name | Description | 
|---|---|
| ModelPost | Model Class | 
| () => ModelPost | Use a function to delay loading to avoid circular dependency errors | 
| 'test-vona:post' | When using models across modules, typically use the model name directly |