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

Node.js中Knex+Objection下,多对多关联表字段过滤实现方法

在Objection.js中对多对多中间表的额外字段添加过滤条件

首先要确保你的模型关系定义正确,必须在多对多关系的through配置中指定中间表的额外字段,否则Objection无法识别并加载这些字段:

// Class模型
class Class extends Model {
  static get tableName() {
    return 'class';
  }

  static get relationMappings() {
    return {
      adventures: {
        relation: Model.ManyToManyRelation,
        modelClass: Adventure,
        join: {
          from: 'class.id',
          through: {
            from: 'class_has_adventure.class_id',
            to: 'class_has_adventure.adventure_id',
            extra: ['date_start', 'date_stop'] // 声明中间表的额外字段
          },
          to: 'adventure.id'
        }
      }
    };
  }
}

// Adventure模型
class Adventure extends Model {
  static get tableName() {
    return 'adventure';
  }

  static get relationMappings() {
    return {
      classes: {
        relation: Model.ManyToManyRelation,
        modelClass: Class,
        join: {
          from: 'adventure.id',
          through: {
            from: 'class_has_adventure.adventure_id',
            to: 'class_has_adventure.class_id',
            extra: ['date_start', 'date_stop']
          },
          to: 'class.id'
        }
      }
    };
  }
}

场景1:查询Class时过滤中间表字段

如果要返回存在符合时间条件的关联adventure的Class,并且仅加载符合条件的adventure数据,可以结合withGraphJoined、modifyGraph和whereExists实现:

const currentDate = new Date();

const targetClasses = await Class.query()
  // 关联adventures并join中间表
  .withGraphJoined('adventures')
  // 过滤关联的adventures,只保留时间范围内的项
  .modifyGraph('adventures', builder => {
    builder
      .where('class_has_adventure.date_start', '<=', currentDate)
      .andWhere('class_has_adventure.date_stop', '>=', currentDate);
  })
  // 确保只返回有符合条件关联的Class
  .whereExists(
    Class.relatedQuery('adventures')
      .where('class_has_adventure.date_start', '<=', currentDate)
      .andWhere('class_has_adventure.date_stop', '>=', currentDate)
  )
  // 去重,避免同一Class被重复返回
  .distinct();

如果仅需过滤Class,不关心返回的adventure列表,可以简化为:

const targetClasses = await Class.query()
  .join('class_has_adventure', 'class.id', 'class_has_adventure.class_id')
  .where('class_has_adventure.date_start', '<=', currentDate)
  .andWhere('class_has_adventure.date_stop', '>=', currentDate)
  .distinct();

场景2:查询Adventure时过滤中间表字段

逻辑和查询Class一致,只需替换模型和关系名:

const currentDate = new Date();

const targetAdventures = await Adventure.query()
  .withGraphJoined('classes')
  .modifyGraph('classes', builder => {
    builder
      .where('class_has_adventure.date_start', '<=', currentDate)
      .andWhere('class_has_adventure.date_stop', '>=', currentDate);
  })
  .whereExists(
    Adventure.relatedQuery('classes')
      .where('class_has_adventure.date_start', '<=', currentDate)
      .andWhere('class_has_adventure.date_stop', '>=', currentDate)
  )
  .distinct();

使用withGraphFetch的注意事项

如果用withGraphFetch(单独查询关联数据),需要手动在关联builder中join中间表才能过滤:

const targetClasses = await Class.query()
  .withGraphFetch('adventures')
  .modifyGraph('adventures', builder => {
    builder
      .join('class_has_adventure', 'adventure.id', 'class_has_adventure.adventure_id')
      .where('class_has_adventure.date_start', '<=', currentDate)
      .andWhere('class_has_adventure.date_stop', '>=', currentDate)
      // 选择中间表的额外字段,否则不会返回
      .select('adventure.*', 'class_has_adventure.date_start', 'class_has_adventure.date_stop');
  })
  .whereExists(
    Class.relatedQuery('adventures')
      .where('class_has_adventure.date_start', '<=', currentDate)
      .andWhere('class_has_adventure.date_stop', '>=', currentDate)
  );

内容的提问来源于stack exchange,提问作者Mirza Umair

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 18:15:58