
本文讲解如何在 prisma 中正确筛选出“至少拥有一个菜单”的子分类(subcategory),避免误用 `where: { id: { not: null } }` 导致语法错误,并提供可直接运行的查询示例与关键注意事项。
在 Prisma 中,当你希望基于一对多关系(如 SubCategory 与 Menu)进行条件过滤——例如“只获取那些已配置至少一个菜单的子分类”——不能对关系字段(如 menu)使用 where: { id: { not: null } }。原因在于:
- menu 是一个关系数组(Menu[]),其本身永远不会是 null 或 undefined,即使没有关联记录,它也默认为空数组 [];
- id: { not: null } 是针对标量字段的写法,而 Prisma 不允许在关系字段上直接对嵌套字段(如 menu.id)做此类空值判断;
- 更重要的是,not: null 在 Prisma Client 中是被明确禁止的语法,会抛出 "Argument 'not' must not be null" 错误。
✅ 正确做法是使用 关系过滤操作符 some:
它用于判断一对多关系中是否存在满足条件的关联记录。若传入空对象 {},即表示“只要存在至少一条关联记录即可”,完美契合“菜单非空”的业务需求。
以下是推荐的完整查询代码:
const topCategories = await this.prisma.subCategory.findMany({
where: {
menu: {
some: {}, // ✅ 关键:筛选出 menu 数组长度 ≥ 1 的 SubCategory
},
},
include: {
menu: true, // ✅ 同时加载关联的菜单数据(可选,按需保留)
},
orderBy: {
id: 'desc',
},
take: 50,
});? 注意事项与最佳实践:
- some: {} 是最简形式,等价于 some: { id: { not: 0 } } 等任意恒真条件,但语义更清晰、性能更优;
- 若需进一步约束菜单属性(如仅包含“启用状态”的菜单),可扩展为:
menu: { some: { status: 'ACTIVE', // 假设 Menu 模型中有 status 字段 } } - include: { menu: true } 与 where 中的 some 是正交的:前者控制返回数据是否包含关联字段,后者控制主查询的筛选逻辑;
- 不要混淆 include 内的 where(用于裁剪关联数据子集,如只加载前3个菜单)和顶层 where(用于筛选主模型实例)。本例中,include: { menu: { where: ... } } 无法实现“排除无菜单的子分类”,必须用顶层 where.menu.some。
总结:Prisma 的关系过滤依赖语义化操作符(some / every / none),而非标量空值判断。掌握 some: {} 是实现“关联数据非空”筛选的简洁、高效且符合 Prisma 设计哲学的标准方案。










