在Sequelize中按关联项筛选时获取全部关联项的方法
问题
我正在使用Sequelize.js,拥有media和tags两张表,二者为多对多关联(通过tags.belongsToMany(media)和media.belongsToMany(tags)配置)。
我需要查询所有关联了tagName为指定值(比如movie)的媒体项,但当前使用的写法:
media.findAll({ include: { model: 'tags', where: { tagName: 'movie' } } })
以及类似写法:
media.findAll({ include: 'tags', where: { '$tags.tagName$': 'movie' } })
都会导致返回的媒体项的tags字段仅包含匹配该tagName的标签,这不符合需求。我希望返回所有关联了目标标签的媒体项,同时显示这些媒体项的全部标签。
快速演示:
执行代码:
media.findOne({ include: { model: 'tags', where: { tagName: 'Pins' } } })
实际返回结果:
{ "mediaId": 3555, "checksum": "xyz", "fileType": "image", "extension": "jpg", "title": null, "description": null, "downloaded": true, "downloadType": null, "dateDownloaded": null, "createdAt": "2023-12-02T19:51:50.000Z", "updatedAt": "2023-12-08T01:46:09.000Z", "tags": [ { "tagId": 26, "tagName": "Pins", "parentTagId": null, "createdAt": "2023-12-06T23:32:53.000Z", "updatedAt": "2023-12-08T01:30:14.000Z", "mediaTagMapping": { "mappingId": 16919, "mediaId": 3555, "tagId": 26, "createdAt": "2023-12-06T23:32:54.000Z", "updatedAt": "2023-12-08T02:15:02.000Z" } } ] }
期望返回结果:
{ "mediaId": 3555, "checksum": "xyz", "fileType": "image", "extension": "jpg", "title": null, "description": null, "downloaded": true, "downloadType": null, "dateDownloaded": null, "createdAt": "2023-12-02T19:51:50.000Z", "updatedAt": "2023-12-08T01:46:09.000Z", "tags": [ { "tagId": 26, "tagName": "Pins", "parentTagId": null, "createdAt": "2023-12-06T23:32:53.000Z", "updatedAt": "2023-12-08T01:30:14.000Z", "mediaTagMapping": { "mappingId": 16919, "mediaId": 3555, "tagId": 26, "createdAt": "2023-12-06T23:32:54.000Z", "updatedAt": "2023-12-08T02:15:02.000Z" } }, { "tagId": 27, "tagName": "Beauty", "parentTagId": null, "createdAt": "2023-12-06T22:32:53.000Z", "updatedAt": "2023-12-08T01:30:15.000Z", "mediaTagMapping": { "mappingId": 16920, "mediaId": 3555, "tagId": 27, "createdAt": "2023-12-06T22:32:54.000Z", "updatedAt": "2023-12-08T02:15:04.000Z" } } ] }
解决方案
Sequelize可以通过以下两种方式实现需求:
方法一:使用子查询(EXISTS)
通过主查询的where条件筛选出关联目标标签的媒体,同时在include中完整加载所有标签,不会过滤标签数据。
const { Op } = require('sequelize'); // 假设已定义好media、tags模型,以及中间表mediaTagMapping const result = await media.findAll({ include: [{ model: tags }], // 完整加载所有关联标签 where: { [Op.exists]: sequelize.literal(`( SELECT 1 FROM mediaTagMapping mtm INNER JOIN tags t ON mtm.tagId = t.tagId WHERE mtm.mediaId = media.mediaId AND t.tagName = 'movie' )`) } });
方法二:分步查询
先获取所有关联目标标签的媒体ID,再通过这些ID查询媒体并加载全部标签:
// 第一步:获取所有带目标标签的媒体ID const taggedMediaIds = await mediaTagMapping.findAll({ attributes: ['mediaId'], // 只取mediaId字段 include: [{ model: tags, where: { tagName: 'movie' } }] }).then(rows => rows.map(item => item.mediaId)); // 第二步:查询这些媒体并加载全部标签 const result = await media.findAll({ include: [{ model: tags }], where: { mediaId: taggedMediaIds } });
两种方法都能实现:筛选出关联指定标签的媒体,同时返回媒体的所有关联标签。方法一为单查询,性能更优;方法二逻辑更直观,适合复杂场景。
内容的提问来源于stack exchange,提问作者None
相关产品推荐
相关产品推荐

