You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Knex.js与PostgreSQL实现三表关联并返回嵌套结构结果

如何用Objection.js查询出嵌套结构的用户关联数据?

问题背景

我是PostgreSQL和Knex.js的新手,现有三张表:user、bank_details、social_media,需要查询出包含用户银行信息和社交媒体信息的嵌套结构结果,同时要按created_at大于指定日期筛选记录。

表结构

user表

id,
phone_number,
created_at,
updated_at

bank_details表

id,
user_id,
bank_name,
acc_api_key,
created_at,
updated_at

social_media表

id,
user_id,
media_channel_name,
channel_api_key,
created_at,
updated_at

期望的查询结果

[{
  id:1, 
  phone_number:'42223424234', 
  bankDetails:[
    {
      user_id:1, 
      bank_name:'abc bank', 
      acc_api_key: 'cQ54B325KB2345434'
    },
    {
      user_id:1, 
      bank_name:'cBD bank', 
      acc_api_key: 'JJJSIDD98ASD0AS'
    },
    {
      user_id:1, 
      bank_name:'EFG bank', 
      acc_api_key: '789FAF8AFFYDF9E'
    }
  ],
  socialMedia:[
    {
      user_id:1, 
      media_channel_name:'whatsapp', 
      channel_api_key: '45n2q35oi5'
    },
    {
      user_id:1, 
      media_channel_name:'facebook', 
      channel_api_key: 'q2j3i355'
    }
  ]
},
{
  id:2, 
  phone_number:'72372373828382', 
  bankDetails:[
    {
      user_id:2, 
      bank_name:'eere bank', 
      acc_api_key: 'erereac'
    },
    {
      user_id:2, 
      bank_name:'iff bank', 
      acc_api_key: '789FAF8AFFYDF9E'
    }
  ],
  socialMedia:[{
      user_id:2, 
      media_channel_name:'instagram', 
      channel_api_key: '09e8q232'
    }
  ]
}]

尝试过的查询(未得到预期效果)

let data = await userModel.query()
        .leftJoin('bank_details', 'user.id', '=', 'bank_details.user_id')
        .leftJoin('social_media', 'user.id', '=', 'social_media.user_id')
        .select("*")

对应的Objection.js模型

userModel

import { Model } from 'objection';

class userModel extends Model {

  public id!: number;
  public name!: string;
  public phone_number!: string;
  public created_at!: string;
  public updated_at!: string;

  static get tableName(): string {
    return 'user';
  }

  static get jsonSchema(): schema {
    return {
      type: 'object',
      properties: {
        id: { type: 'integer' },
        name: { type: 'string' },
        phone_number: { type: 'string' },
        member_id: { type: 'string' },
        created_at: { type: 'string' },
        updated_at: { type: 'string' },
      },
    };
  }
}

export default userModel;

bankDetailsModel

import { Model } from 'objection';

class bankDetailsModel extends Model {

  public id!: number;
  public bank_name!: string;
  public acc_api_key!: string;
  public user_id!: number;
  public created_at!: string;
  public updated_at!: string;

  static get tableName(): string {
    return 'bank_details';
  }

  static get jsonSchema(): schema {
    return {
      type: 'object',
      properties: {
        id: { type: 'integer' },
        bank_name: { type: 'string' },
        acc_api_key: { type: 'string' },
        user_id: { type: 'integer' },
        created_at: { type: 'string' },
        updated_at: { type: 'string' },
      },
    };
  }
}

export default bankDetailsModel;

socialMediaModel

import { Model } from 'objection';

class socialMediaModel extends Model {

  public id!: number;
  public media_channel_name!: string;
  public channel_api_key!: string;
  public user_id!: number;
  public created_at!: string;
  public updated_at!: string;

  static get tableName(): string {
    return 'social_media';
  }

  static get jsonSchema(): schema {
    return {
      type: 'object',
      properties: {
        id: { type: 'integer' },
        media_channel_name: { type: 'string' },
        channel_api_key: { type: 'string' },
        user_id: { type: 'integer' },
        created_at: { type: 'string' },
        updated_at: { type: 'string' },
      },
    };
  }
}

export default socialMediaModel;

解决方案

要实现嵌套结构的查询,需要利用Objection.js的withGraphFetched方法,同时在模型中定义关联关系。

步骤1:在userModel中添加关联关系

修改userModel,添加relationMappings来定义和bankDetails、socialMedia的关联:

import { Model } from 'objection';
import bankDetailsModel from './bankDetailsModel';
import socialMediaModel from './socialMediaModel';

class userModel extends Model {
  // 原有属性和tableName、jsonSchema不变...

  static get relationMappings() {
    return {
      bankDetails: {
        relation: Model.HasManyRelation,
        modelClass: bankDetailsModel,
        join: {
          from: 'user.id',
          to: 'bank_details.user_id'
        }
      },
      socialMedia: {
        relation: Model.HasManyRelation,
        modelClass: socialMediaModel,
        join: {
          from: 'user.id',
          to: 'social_media.user_id'
        }
      }
    };
  }
}

export default userModel;

步骤2:编写正确的查询代码

使用withGraphFetched来获取关联数据,同时添加created_at的筛选条件:

const targetDate = '2023-01-01'; // 替换成你的指定日期

const data = await userModel.query()
  .where('created_at', '>', targetDate)
  .withGraphFetched('[bankDetails, socialMedia]')
  .select('id', 'phone_number'); // 只选择需要的用户字段,避免冗余

说明

  • withGraphFetched会执行单独的查询来获取关联数据,避免SQL JOIN导致的重复用户记录问题,正好符合嵌套结构需求。
  • 如果需要对关联数据也做日期筛选,可以在关联后添加条件:
.withGraphFetched({
  bankDetails: (query) => query.where('created_at', '>', targetDate),
  socialMedia: (query) => query.where('created_at', '>', targetDate)
})

内容的提问来源于stack exchange,提问作者Shanthi B

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 09:30:58