2020-11-22 16:59:41 +08:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
2020-11-29 09:16:41 +08:00
|
|
|
"sort"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"github.com/hbollon/go-edlib"
|
2020-11-22 16:59:41 +08:00
|
|
|
"github.com/serverwentdown/datetime.link/data"
|
|
|
|
)
|
|
|
|
|
2020-11-29 09:16:41 +08:00
|
|
|
// FullSearchCities uses a very basic iterative method to search for cities
|
2020-11-22 16:59:41 +08:00
|
|
|
// with the given string
|
|
|
|
func FullSearchCities(cities map[string]*data.City, zone string) ([]*data.City, error) {
|
2020-11-29 09:16:41 +08:00
|
|
|
// TODO: optimisations
|
|
|
|
ratings := []cityRatings{}
|
|
|
|
for _, city := range cities {
|
|
|
|
rating, err := compareCity(city, zone)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
ratings = append(ratings, cityRatings{city, rating})
|
|
|
|
}
|
|
|
|
sort.Slice(ratings, func(i, j int) bool { return ratings[i].Rating > ratings[j].Rating })
|
|
|
|
topCities := make([]*data.City, 10)
|
|
|
|
for i := 0; i < 10; i++ {
|
|
|
|
topCities[i] = ratings[i].City
|
|
|
|
//l.Debug("city", zap.String("n", topCities[i].Name), zap.Float32("r", ratings[i].Rating))
|
|
|
|
}
|
|
|
|
return topCities, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
type cityRatings struct {
|
|
|
|
City *data.City
|
|
|
|
Rating float32
|
|
|
|
}
|
|
|
|
|
|
|
|
func compareCity(city *data.City, zone string) (float32, error) {
|
|
|
|
// City Name is preferred
|
|
|
|
cityDistance, err := compare(city.Name, zone)
|
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
for _, altname := range city.AlternateNames {
|
|
|
|
altnameDistance, err := compare(altname, zone)
|
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
altnameDistance *= 0.9
|
|
|
|
cityDistance = floatMax(cityDistance, altnameDistance)
|
|
|
|
}
|
|
|
|
// Admin1 Name is next preferred
|
|
|
|
admin1Distance, err := compare(city.Admin1.Name, zone)
|
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
// Country Name is next preferred
|
|
|
|
countryDistance, err := compare(city.Country.Name, zone)
|
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
// Merge 3 values
|
|
|
|
rating := floatMax(cityDistance, admin1Distance*0.9, countryDistance*0.9)
|
|
|
|
return rating, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func compare(str1, str2 string) (float32, error) {
|
|
|
|
algo := edlib.JaroWinkler
|
|
|
|
//algo := edlib.Levenshtein
|
|
|
|
res, err := edlib.StringsSimilarity(strings.ToLower(str1), strings.ToLower(str2), algo)
|
|
|
|
return res * res * res, err
|
|
|
|
}
|
|
|
|
|
|
|
|
func floatMax(a float32, bs ...float32) float32 {
|
|
|
|
for _, b := range bs {
|
|
|
|
if a > b {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
a = b
|
|
|
|
}
|
|
|
|
return a
|
2020-11-22 16:59:41 +08:00
|
|
|
}
|