如何用Prisma在单查询中批量添加视频至播放列表(避免重复)
我有Playlist和Video模型,二者为显式多对多关系(用于存储额外数据)。现在我手里有一个Video主键(URL)数组,想把这些视频添加到指定播放列表中,且仅添加不存在的视频。
目前我能实现单次添加一个视频的逻辑(示例代码如下),但想知道是否可以批量添加多个视频,最好通过单个查询完成。我知道upsert可以实现“不存在则创建,存在则更新”,但设置空update的方式流程复杂,而且不确定该从哪个入口编写批量查询:是通过update Playlist模型创建多个关联?还是updateMany Video模型?或是直接操作关联表?
现有单条添加代码:
const id = /* Playlist的ID */ const videoUrl = /* Video的URL,作为主键 */ // 我想使用videoUrls数组,而不是单个videoUrl const updatedPlaylist = await prisma.playlist.update({ where: { id }, include: { videos: { include: { video: true } } }, data: { videos: { upsert: { where: { playlistId_videoUrl: { playlistId: id, videoUrl: videoUrl, }, }, create: { addedAt: new Date(), video: { connect: { url: videoUrl }, }, }, update: {}, }, }, }, });
方法1:直接操作中间关联表(推荐,单查询批量添加)
因为是显式多对多关系,Prisma会生成对应的中间模型(比如PlaylistVideo,包含playlistId、videoUrl、addedAt等字段,且复合主键为playlistId_videoUrl)。你可以直接对这个中间模型使用createMany,并开启skipDuplicates参数来忽略已存在的关联:
const playlistId = /* Playlist的ID */ const videoUrls = ["url1", "url2", "url3"]; // 你的视频URL数组 await prisma.playlistVideo.createMany({ data: videoUrls.map(url => ({ playlistId, videoUrl: url, addedAt: new Date() })), skipDuplicates: true // 关键:忽略已存在的复合主键记录,不会报错 }); // 如需返回更新后的Playlist及关联视频,再单独查询一次 const updatedPlaylist = await prisma.playlist.findUnique({ where: { id: playlistId }, include: { videos: { include: { video: true } } } });
这个方法的优势是单查询完成批量插入,skipDuplicates会自动跳过已存在的playlistId+videoUrl组合,无需额外判断。
方法2:通过Playlist的update操作批量创建关联
如果你希望在同一个查询中完成更新并返回结果,可以在playlist.update的data.videos里使用create数组,结合connect,但需要注意:这种方式如果有重复的关联会报错,所以需要先查询出播放列表已有的视频URL,过滤掉重复项后再创建:
const playlistId = /* Playlist的ID */ const videoUrls = ["url1", "url2", "url3"]; // 先查询已存在的关联视频URL const existingPlaylist = await prisma.playlist.findUnique({ where: { id: playlistId }, select: { videos: { select: { videoUrl: true } } } }); const existingVideoUrls = existingPlaylist.videos.map(item => item.videoUrl); // 过滤出需要新增的视频URL const newVideoUrls = videoUrls.filter(url => !existingVideoUrls.includes(url)); // 批量创建关联 const updatedPlaylist = await prisma.playlist.update({ where: { id: playlistId }, include: { videos: { include: { video: true } } }, data: { videos: { create: newVideoUrls.map(url => ({ addedAt: new Date(), video: { connect: { url } } })) } } });
这个方法需要两次查询,但可以直接返回更新后的Playlist对象。如果你的业务场景允许先查询再过滤,这种方式也能满足需求。
注意事项
Prisma目前不支持在update的关联操作中批量使用upsert,只能单个指定。所以用createMany + skipDuplicates是最简洁的批量去重添加方式。
内容的提问来源于stack exchange,提问作者N.T. Dang

