
本文介绍如何使用 prisma 的 `some` 关系过滤器,精准查询拥有至少一个菜单项的子分类,并正确包含其菜单数据,避免因误用 `where: { id: { not: null } }` 导致的语法错误。
在 Prisma 中,findMany 的 include 选项用于 eager-loading 关联数据,但它不支持在 include 内部直接进行条件过滤(如 where)来控制主查询的范围。你最初尝试的写法:
include: {
menu: {
where: { id: { not: null } }, // ❌ 错误:not 不能为 null,且此写法无法过滤主表
}
}不仅会触发 "Argument 'not' must not be null" 报错(因为 null 是无效值,应使用 isSet: true 或更推荐的关系谓词),更重要的是——include.where 只影响被加载的关联数据子集,不会过滤 SubCategory 本身。也就是说,即使某个 SubCategory 的 menu 数组为空,它仍会被返回,只是 menu 字段为 []。
✅ 正确目标是:仅返回那些至少关联了一个 Menu 的 SubCategory 记录。这属于“主表过滤”,必须使用 where 顶层的关系过滤器(Relation Filters),而非 include 内部。
Prisma 提供了语义清晰的关系操作符,其中 some: {} 表示“存在至少一条满足条件的关联记录”。由于你只需判断“有菜单”,无需额外条件,空对象 {} 即可:
const topCategories = await this.prisma.subCategory.findMany({
where: {
menu: {
some: {}, // ✅ 关键:只返回 menu 关系中至少有一条记录的 SubCategory
},
},
include: {
menu: true, // ✅ 同时加载全部关联菜单(无额外过滤)
},
orderBy: {
id: 'desc',
},
take: 50,
});? 补充说明: menu: { some: {} } 等价于 SQL 中的 EXISTS (SELECT 1 FROM "Menu" WHERE "Menu"."subCategoryId" = "SubCategory"."id"); 若需进一步约束菜单(例如只包含启用状态的菜单),可写为 some: { isActive: true }; 不要使用 isEmpty: false(该谓词不存在),也不要用 not: null(id 是非空字段,且 not 不接受 null 字面量)。
? 小贴士:
- 如果你还想排除某些菜单(如软删除的),可在 include.menu 中添加 where 进行加载时过滤,但注意这与主查询过滤无关:
include: { menu: { where: { deletedAt: null }, // 仅加载未删除的菜单 } } - 最终结果中,每个 SubCategory 的 menu 字段将是一个非空数组(长度 ≥ 1),完全符合“菜单不为空”的业务需求。
综上,牢记原则:用 where.{relation}: { some: {...} } 过滤主表;用 include.{relation}.where 过滤关联数据加载内容。 正确组合二者,即可写出高效、语义明确的 Prisma 查询。










