You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Prisma中使用自定义函数式Where子句(避免原生查询)

在Prisma中实现自定义Where子句(避免原生查询)

核心结论

你示例里的lt: (val) => calculateMaxLat(val)这种直接在Prisma查询条件中传入JS函数的写法不被支持,因为Prisma的查询API会被编译为SQL,无法直接解析执行客户端的JS函数。但有几种替代方案可以满足你的需求,既不用全量写原生查询,也能保留关联、排序、分页逻辑。

可行方案

1. 在Where子句中嵌入SQL表达式(推荐)

利用Prisma的prisma.sql在where条件中嵌入PostGIS的距离计算逻辑,其他查询逻辑(关联、排序、分页)仍用Prisma原生API实现:

const targetLat = 15.00;
const targetLng = 10.00;
const maxDistance = 1000; // 单位:公里

const locations = await prisma.location.findMany({
  where: {
    AND: [
      // 嵌入PostGIS的距离判断SQL
      prisma.sql`ST_DWithin(
        ST_MakePoint(Longitude, Latitude)::geography, 
        ST_MakePoint(${targetLng}, ${targetLat})::geography, 
        ${maxDistance * 1000}
      )`
    ],
    // 可添加其他Prisma风格的过滤条件
    // id: { gt: 0 }
  },
  include: {
    // 保留关联查询逻辑
    relatedModel: true
  },
  orderBy: {
    id: 'asc'
  },
  skip: 0,
  take: 10, // 分页功能正常生效
});

这种方式的优势是:仅在需要自定义逻辑的部分嵌入SQL,其余部分完全沿用Prisma的类型安全和便捷API,不会破坏现有查询结构。

2. 提前计算边界坐标,用原生条件过滤

如果不想嵌入SQL,可以先用地理库(如geolib)计算出目标位置的坐标边界,再用Prisma的gt/lt进行范围过滤:

import { getBoundingBox } from 'geolib';

const target = { latitude: 15.00, longitude: 10.00 };
// 计算1000公里范围内的坐标边界(单位转成米)
const boundingBox = getBoundingBox(target, 1000 * 1000);

const locations = await prisma.location.findMany({
  where: {
    latitude: {
      gt: boundingBox[0].latitude,
      lt: boundingBox[1].latitude
    },
    longitude: {
      gt: boundingBox[0].longitude,
      lt: boundingBox[1].longitude
    },
    // 可选:如果需要精确距离,可再结合PostGIS的过滤逻辑
  },
  include: { /* 关联查询 */ },
  orderBy: { /* 排序规则 */ },
  skip: 0,
  take: 10
});

注意:这种方式是近似过滤,边界内的点可能超出实际距离范围,适合对精度要求不高的场景,或者后续再在内存中做精确过滤(但会影响分页,不建议)。

3. 封装Prisma扩展,复用自定义查询

把距离查询逻辑封装成Prisma扩展方法,调用时更简洁,同时保留所有Prisma查询能力:

const prisma = new PrismaClient().$extends({
  model: {
    location: {
      async findWithinDistance(
        targetLat: number, 
        targetLng: number, 
        maxDistanceKm: number, 
        options?: Omit<Prisma.LocationFindManyArgs, 'where'>
      ) {
        return prisma.location.findMany({
          where: {
            AND: [
              prisma.sql`ST_DWithin(
                ST_MakePoint(Longitude, Latitude)::geography, 
                ST_MakePoint(${targetLng}, ${targetLat})::geography, 
                ${maxDistanceKm * 1000}
              )`
            ]
          },
          ...options
        });
      }
    }
  }
});

// 调用示例
const locations = await prisma.location.findWithinDistance(15.00, 10.00, 1000, {
  include: { relatedModel: true },
  orderBy: { id: 'asc' },
  skip: 0,
  take: 10
});

这种方式把复杂的自定义逻辑封装起来,团队成员调用时无需关心底层SQL实现,同时完全兼容Prisma的所有查询选项。


内容的提问来源于stack exchange,提问作者Emre A

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 02:05:26