Swift Vapor查询按距离过滤和排序

huangapple go评论73阅读模式
英文:

Swift Vapor query filtering and sorting by distance

问题

我正在开发Swift Vapor项目。我需要按照距离筛选和排序用户(距离不应超过100公里,最近的用户应该排在最前面)。我有纬度和经度的坐标。我需要使用查询方法中的filter和sort来实现优化。我该如何做?

我正在使用PostgreSQL数据库。

func getAll(_ req: Request, arguments: UserFilter) async throws -> [User] {
  let user = try req.auth.require(User.self)
  var query = User.query(on: req.db)

  arguments.filter(&query)

  if let lat = user.latitude, let lon = user.longitude {
    query.filter(
      User.self,
      field: \.latitude, // 提供字段的KeyPath
      method: .lessThanOrEqual, // 提供过滤方法
      value: lat + 0.1 // 提供值
    )
    
    // 在此处添加排序逻辑,根据距离排序
    // 请注意,需要计算距离并将其添加到查询中
  }

  return try await query.all()
}
@OptionalField(key: "latitude")
var latitude: Float?

@OptionalField(key: "longitude")
var longitude: Float?

请注意,上面的代码示例中,我添加了如何过滤纬度字段的示例。你需要根据提供的经度和距离值来计算并添加排序逻辑,以确保最接近的用户排在最前面。

英文:

I'm working on the swift vapor project. I need to filter and sort users by distance(it should not exceed 100 kilometers and the first user should be the closest). I have the coordinates latitude and longitude. I need to do this specifically with query methods filter and sort for good optimization. How can I do this?

I'm using PostgreSQL db.

func getAll(_ req: Request, arguments: UserFilter) async throws -> [User] {
  let user = try req.auth.require(User.self)
  var query = User.query(on: req.db)

  arguments. filter(&query)

  if let lat = user.latitude, let lon = user.longtitude {
    query. filter(
      User.self, 
      field: KeyPath<Schema, QueryableProperty>, // not yet provided
      method: DatabaseQuery.Filter.Method,       // not yet provided
      value: Decodable & Encodable)              // not yet provided
    )
  }
}

return try await query.all()
@OptionalField(key: "latitude")
var latitude: Float?

@OptionalField(key: "longtitude")
var longtitude: Float?

答案1

得分: 1

我不确定你是否能直接使用ORM来实现这个,如果你想保留Float的话。如果你想使用ORM,我建议你使用https://github.com/brokenhandsio/fluent-postgis 这个库。

如果你想保留Float,你可以使用两种方法来应用勾股定理:

  1. 切换到原始SQL:
let query = """
SELECT * FROM your_table 
ORDER BY SQRT(POW(111.1 * (latitude - \(bind: lat)), 2) + 
             POW(111.1 * (longitude - \(bind: lon)) * COS(latitude / 57.3), 2))
"""
  1. 在查询之后对它们进行排序
.map { models in
    return models.sorted { model1, model2 in
        let distance1 = sqrt(pow(111.1 * (model1.latitude - lat), 2) + 
                                 pow(111.1 * (model1.longitude - lat) * cos(model1.latitude / 57.3), 2))
        let distance2 = sqrt(pow(111.1 * (model2.latitude - lat), 2) + 
                                 pow(111.1 * (model2.longitude - lon) * cos(model2.latitude / 57.3), 2))
        return distance1 < distance2
    }
}

其中:

  • 111.1是每纬度公里数的近似值(如果使用英里,改为69.1)
  • (latitude - \(lat))计算纬度差异
  • COS(latitude / 57.3)是调整经度差异以适应地球曲率的近似值。57.3是一个弧度中的度数的近似值(180/π),因此这将纬度从度转换为弧度
  • (longitude - \(lon))计算经度差异

请注意,这个定理是对距离的近似值,所以在长距离上可能不是特别精确!

英文:

I'm not sure you are able to do this directly using the ORM if you're keen on keeping Floats. If you want to use the ORM I'd say your best bet is https://github.com/brokenhandsio/fluent-postgis

If you want to keep Floats you can use the Pythagorean Theorem in two manners:

  1. Drop down do raw SQL:
let query = """
SELECT * FROM your_table 
ORDER BY SQRT(POW(111.1 * (latitude - \(bind: lat)), 2) + 
             POW(111.1 * (longitude - \(bind: lon)) * COS(latitude / 57.3), 2))
"""
  1. Sort them after querying
.map { models in
   return models.sorted { model1, model2 in
       let distance1 = sqrt(pow(111.1 * (model1.latitude - lat), 2) + 
                                pow(111.1 * (model1.longitude - lat) * cos(model1.latitude / 57.3), 2))
       let distance2 = sqrt(pow(111.1 * (model2.latitude - lat), 2) + 
                                pow(111.1 * (model2.longitude - lon) * cos(model2.latitude / 57.3), 2))
       return distance1 < distance2
   }

Where:

  • 111.1 is an approximation of the number of Kms per degree of latitude. (change to 69.1 if you're using miles)
  • (latitude - \(lat)) calculates the difference in latitudes
  • COS(latitude / 57.3) is an approximation to adjust the longitude difference for the curvature of the Earth. 57.3 is approximately the number of degrees in one radian (180/π), so this is converting the latitude from degrees to radians
  • (longitude - \(lon)) calculates the difference in longitudes

Be aware that this theorem is approximating the distance so at long distances it might not be particularly precise!

huangapple
  • 本文由 发表于 2023年7月27日 21:14:52
  • 转载请务必保留本文链接:https://go.coder-hub.com/76780133.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定