1

enter image description hereI'm now trying to input a longitude and latitude and return a Pers model if the distance is under (any number) meters. And I know CLLocation is a bad usage in here, but it didn't came up with any idea with postgis. So in this condition, what do I have is a controller down below, and I think I should code something in the return filter. But I don't actually know what to do inside the filter to make the compile pass.

Controller :

import Foundation
import Vapor
import Fluent
import CoreLocation

final class InformationController{
    func temp(_ req:Request) throws -> EventLoopFuture<[Pers]> {
  
        let userlocation = try req.content.decode(Pers.UserLocation.self)
        
        let mylocation = CLLocation(latitude: userlocation.latitude, longitude: userlocation.lonitude)
        
        let pers = try req.content.decode([Pers].self)
        
        let newUsers = pers.filter{ user in
            let location = CLLocation(latitude: user.latitude, longitude: user.longitude)
            return location.distance(from: mylocation) < 100
                
        }
        
        return Pers.query(on: req.db).filter().all()
        
    }

}

Pers model :

import Foundation
import Fluent
import Vapor
import FluentPostgresDriver


final class Pers:Model,Content{
    static let schema = "people"
    
    @ID(key: .id)
    var id:UUID?
    
    @Field(key: "姓名")
    var name: String
    
    @Field(key: "IG帳號")
    var account: String
    
    @Field(key: "頭像")
    var picture: String
    
    @Field(key: "年紀")
    var age: String
    
    @Field(key: "生日")
    var birth: String
    
    @Field(key:"緯度")
    var latitude: Double
    
    @Field(key:"經度")
    var longitude: Double
    
    @Field(key: "居住城市")
    var city: String
    
    @Field(key: "興趣")
    var hobby : String
    
    @Parent(key: "user_id")
    var user: User

    init(){}
    
    init(id:UUID?=nil, name:String, account:String, picture:String ,age:String, birth:String,latitude: Double, longitude: Double, city:String, hobby:String, userId:UUID){
        self.id=id
        self.name=name
        self.account=account
        self.picture=picture
        self.age=age
        self.birth=birth
        self.latitude = latitude
        self.longitude = longitude
        self.city=city
        self.hobby=hobby
        self.$user.id=userId
    }
}

extension Pers{
    struct UserLocation: Content {
        var latitude: Double
        var lonitude: Double
    }
}
蔡濡安
  • 95
  • 7
  • @Nick Oh, but I don't actually know to convert the type to EventLoopFuture type. I'm a newbies in here. – 蔡濡安 Aug 18 '21 at 07:33
  • 1
    @Nick So you mean that I can just calculate the json data that if it is in the range on the client side. – 蔡濡安 Aug 18 '21 at 07:53
  • @Nick While I was testing the api. I've sent a in range longitude and latitude. But the postman told me "Value of type 'Array' required for key ''." . I don't actually know what it means. I've added the screenshot on the poster. – 蔡濡安 Aug 18 '21 at 07:55

1 Answers1

2

The answer to your immediate question is to just return newUsers, changing the return type of your route as shown, but I would also decode using something like:

struct LocData: Decodable {
    let userLocation: Pers.UserLocation
    let pers: [Pers]
}

func temp(_ req:Request) throws -> [Pers] {
    let locData = try req.content.decode(LocData.self)
    let userlocation = locData.userLocation
    let mylocation = CLLocation(latitude: userlocation.latitude, longitude: userlocation.longitude)
        
    return locData.pers.filter{ user in
        let location = CLLocation(latitude: user.latitude, longitude: user.longitude)
        return location.distance(from: mylocation) < 100  
    }
}

Your JSON needs to look something like:

{ "userLocation" : { ... },
  "pers" : [ ... ]
}

Your subsequent problem is you are trying to decode the request data two different ways. The first one works but the second one fails. You were only supplying JSON data for the first decode in your example image, so there was no array for the second decode.

The more fundamental issue is that you should really be doing this operation client-side, given that you must have all the source data you need in order to send it to the server. It doesn't make sense to transfer a potentially large dataset to do a simple, self-contained calculation only to return (a subset of) the same dataset as a response. See How to find my distance to a known location in JavaScript for an example implementation of the required calculation.

Nick
  • 4,820
  • 18
  • 31
  • 47
  • I may know what you mean to do on the client side. But I still want the article I posted work. After using your code, the postman's issue still the same. I've added the screen shot on my poster. Sorry for bothering that much cause I have been thinking this all day. – 蔡濡安 Aug 18 '21 at 08:10
  • Hey, another question. Base on what you mean. I will send all the `Pers` json data to client side. But how do I perform the data I want to present but not all the data. – 蔡濡安 Aug 18 '21 at 08:57
  • You need to post a new question. I think you are saying you want to send some Pers fields but not all of them? I can answer that easily. – Nick Aug 18 '21 at 09:57
  • https://stackoverflow.com/questions/68829771/adding-constraint-in-request-in-swiftui I've asked a new question here. – 蔡濡安 Aug 18 '21 at 10:14
  • By the way , the main goal is using a latitude and a longitude to return a "in range `Per`" . Not the distance. – 蔡濡安 Aug 18 '21 at 10:45
  • But the postman tells me this "Value required for key 'userLocation". And I've added the user location already. I posted a new picture on the top of the article. – 蔡濡安 Aug 18 '21 at 14:02
  • 1
    Check your case: you've got `userLocation` in your structure and `userlocation` in the JSON. `userLocation` is preferred. SO is not for rolling support. – Nick Aug 18 '21 at 15:32