Skip to content

Commit a8407c2

Browse files
committed
Added support for searching by administration area or country
The input string "name" is split by comma character (","), first part is used for the original search, seconds part is used for separate search for administration areas and countries. This can be used for example to search for city Queenstown in New Zealand with query "/v1/search?name=Queenstown,New Zealand". If the input string doesn't contain comma, it works as previously.
1 parent f714ed4 commit a8407c2

2 files changed

Lines changed: 27 additions & 3 deletions

File tree

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,9 @@ systemctl start geocoding-api.service
3434
systemctl status geocoding-api.service
3535
```
3636

37-
Additionally, nginx proxy should be used
37+
Geonames data are parsed and processed during the first start, which requires some times and memor. At least 6GB RAM is required and it takes about 25 minutes on a CPU with 2 Skylake cores. Database loading takes about 5 minutes after that.
38+
39+
Additionally, nginx proxy should be used.
3840

3941
## Terms & Privacy
4042
Open-Meteo APIs are free for open-source developer and non-commercial use. We do not restrict access, but ask for fair use.

Sources/App/GeocodingapiController.swift

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,21 @@ struct GeocodingapiController: RouteCollection {
5555
let language = params.language ?? "en"
5656
let languageId = database.geonames.languages.firstIndex(of: language) ?? database.geonames.languages.firstIndex(of: "en")!
5757
let count = try params.getCount()
58-
59-
var results = params.name.count < 2 ? [] : database.search(params.name, languageId: Int32(languageId), maxCount: count)
58+
59+
var name = params.name
60+
var areaIds: [Int32]?
61+
if name.contains(",") { // Split string by comma, so we can filter the results later by second part
62+
let parts = name.components(separatedBy: ",")
63+
name = parts[0].trimmingCharacters(in: .whitespacesAndNewlines)
64+
let areaName = parts[1].trimmingCharacters(in: .whitespacesAndNewlines)
65+
if areaName.count > 1 {
66+
areaIds = database.search(areaName, languageId: Int32(languageId), maxCount: 10).map({
67+
return $0.0
68+
})
69+
}
70+
}
71+
72+
var results = params.name.count < 2 ? [] : database.search(name, languageId: Int32(languageId), maxCount: count)
6073
/// TODO country filter need to be inside database match, because `count` would be wrong otherwise
6174
if let countryCode = params.countryCode {
6275
/*guard let countryId = searchTree.geonames.countryIso2.firstIndex(of: countryCode) else {
@@ -69,6 +82,15 @@ struct GeocodingapiController: RouteCollection {
6982
return c == countryCode
7083
})
7184
}
85+
if let areas = areaIds {
86+
results = results.filter({ // Filter the results by second part of the original string
87+
return areas.contains(database.geonames.geonames[$0.0]?.admin1ID ?? -1)
88+
|| areas.contains(database.geonames.geonames[$0.0]?.admin2ID ?? -1)
89+
|| areas.contains(database.geonames.geonames[$0.0]?.admin3ID ?? -1)
90+
|| areas.contains(database.geonames.geonames[$0.0]?.admin4ID ?? -1)
91+
|| areas.contains(database.geonames.geonames[$0.0]?.countryID ?? -1)
92+
})
93+
}
7294
let mapped: [GeocodingApi.Geoname] = results.map({
7395
guard let geoname = database.geonames.getResponse(id: $0.0, languageId: Int32(languageId), searchRank: $0.1) else {
7496
fatalError("Geoname in search index was not in database.")

0 commit comments

Comments
 (0)