Prisma 查询中筛选存在关联数据的子分类(非空菜单)

本文介绍如何使用 prisma 的关系过滤语法(`some`)精准查询拥有至少一个菜单项的子分类,避免因误用 `not: null` 导致的语法错误,并确保返回结果同时包含关联的菜单数据。

在 Prisma 中,对一对多关系(如 SubCategory 与 Menu)进行“非空”条件筛选时,不能直接对数组字段使用 where: { id: { not: null } }——这不仅逻辑错误(menu 字段本身是数组,不可能为 null),更会触发 Prisma 客户端报错:"Argument 'not' must not be null."。根本原因在于:menu 是一个关系字段(类型为 Menu[]),其值始终是数组(空数组 [] 或非空数组),而非可为 null 的标量或对象。

✅ 正确做法是使用 Prisma 提供的关系过滤

操作符 some,它专用于判断「至少存在一个满足条件的关联记录」。当传入空对象 {} 时,some: {} 表示「存在任意一条关联的 Menu 记录」,即等价于「菜单非空」。

以下是推荐的完整查询代码:

const topCategories = await this.prisma.subCategory.findMany({
  where: {
    menu: {
      some: {}, // ✅ 关键:筛选拥有至少一个 Menu 的 SubCategory
    },
  },
  include: {
    menu: true, // ✅ 同时加载所有匹配的菜单数据
  },
  orderBy: {
    id: 'desc',
  },
  take: 50,
});

⚠️ 注意事项:

  • some: {} 不等同于 some: { id: { not: 0 } } 等复杂条件——空对象即表示“存在任意匹配项”,语义清晰且性能友好;
  • 若需进一步筛选菜单(例如只包含已启用的菜单),可扩展为 some: { isActive: true };
  • 不要混淆 include 中的 where(用于过滤被包含的关联数据)与 findMany 根级 where(用于筛选主模型本身)。前者影响返回的 menu 子集,后者决定哪些 subCategory 被选中;
  • menu 关系在模型中未定义 @relation(fields: [...]) 外键字段,说明你可能采用隐式多对多或通过中间表管理。请确保数据库中 Menu 表实际存在指向 SubCategory 的外键(如 subCategoryId),否则 some 过滤将无法生效。

总结:当目标是「查找拥有至少一个关联记录的父级模型」时,始终优先使用 relationField: { some: {} };这是 Prisma 关系查询的核心范式,兼具准确性、可读性与执行效率。