如何在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
相关产品推荐
相关产品推荐

