-
Notifications
You must be signed in to change notification settings - Fork 0
/
search.go
81 lines (74 loc) · 1.99 KB
/
search.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
package main
import (
"sort"
"strings"
"github.com/hbollon/go-edlib"
"github.com/serverwentdown/datetime.link/data"
)
// FullSearchCities uses a very basic iterative method to search for cities
// with the given string
func FullSearchCities(cities map[string]*data.City, zone string) ([]*data.City, error) {
// 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
}