Vapor4中如何使用PostGIS实现按经纬度距离过滤Pers模型
解决方案
前置准备
- 首先确保你的PostgreSQL数据库已经安装并启用PostGIS扩展,登录数据库后执行以下SQL即可:
CREATE EXTENSION IF NOT EXISTS postgis; - 你原有控制器里的逻辑存在冗余:不需要从请求中解码
[Pers]数组,我们直接将距离计算逻辑下沉到数据库层执行,避免全量拉取数据再内存过滤的性能问题。
核心查询实现
Fluent支持传入自定义SQL片段作为过滤条件,我们可以直接调用PostGIS的距离计算函数实现需求,修改后的控制器代码如下:
import Foundation import Vapor import Fluent import FluentPostgresDriver final class InformationController{ func temp(_ req:Request) throws -> EventLoopFuture<[Pers]> { // 解码用户传入的位置参数 let userLocation = try req.content.decode(Pers.UserLocation.self) // 指定搜索半径,单位:米 let searchRadius: Double = 100 // 直接用PostGIS函数在数据库层过滤 return Pers.query(on: req.db) .filter(.sql(raw: """ ST_DistanceSphere( ST_MakePoint(經度, 緯度), ST_MakePoint($1, $2) ) < $3 """, bindings: [ userLocation.lonitude, userLocation.latitude, searchRadius ] )) .all() } }
注意:PostGIS的
ST_MakePoint函数参数顺序是先经度后纬度,不要写反,否则计算出来的距离会完全错误。
性能优化建议
如果你的people表数据量较大,可以给经纬度字段加GIST空间索引大幅提升查询速度,执行以下SQL创建索引:
CREATE INDEX idx_people_location ON people USING GIST (ST_MakePoint(經度, 緯度));
进阶方案(可选)
如果后续有大量地理位置相关查询需求,可以引入FluentPostGIS库,将Pers模型的经纬度合并为单个PostGIS Point类型字段,后续查询写法会更简洁,也不需要手动构造点对象。
内容的提问来源于stack exchange,提问作者蔡濡安
相关产品推荐
相关产品推荐

