location is importing in psql
This commit is contained in:
@@ -3,6 +3,8 @@ services:
|
||||
db:
|
||||
image: 'postgres:17.2-alpine3.21'
|
||||
restart: always
|
||||
ports:
|
||||
- 5432:5432
|
||||
environment:
|
||||
POSTGRES_PASSWORD: 1234
|
||||
networks:
|
||||
|
||||
5
docker/postgresql/start.sh
Normal file → Executable file
5
docker/postgresql/start.sh
Normal file → Executable file
@@ -4,5 +4,10 @@
|
||||
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
cd "$SCRIPT_DIR"
|
||||
|
||||
# Stop any existing containers and remove them
|
||||
docker compose down
|
||||
|
||||
# Start the services in detached mode
|
||||
docker compose up -d
|
||||
|
||||
echo "PostgreSQL is ready"
|
||||
|
||||
BIN
examples/data/location/location_example
Executable file
BIN
examples/data/location/location_example
Executable file
Binary file not shown.
@@ -2,7 +2,7 @@
|
||||
|
||||
- make docker build (see docker_ubuntu example)
|
||||
- start from docker_ubuntu
|
||||
- for build use our vlanf approach (example see docker_ubuntu, make sure we have our zinit & ssh working)
|
||||
- for build use our vlang approach (example see docker_ubuntu, make sure we have our zinit & ssh working)
|
||||
- install the web UI: openwebui (not by docker but use uv to install this software)
|
||||
- use https://github.com/astral-sh/uv for the python part
|
||||
- as last step, clean it all up (remove apt cache, ...)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
module location
|
||||
|
||||
import db.sqlite
|
||||
import db.pg
|
||||
import os
|
||||
import encoding.csv
|
||||
import freeflowuniverse.herolib.osal
|
||||
@@ -9,7 +9,7 @@ import freeflowuniverse.herolib.core.pathlib
|
||||
// LocationDB handles all database operations for locations
|
||||
pub struct LocationDB {
|
||||
mut:
|
||||
db sqlite.DB
|
||||
db pg.DB
|
||||
tmp_dir pathlib.Path
|
||||
db_dir pathlib.Path
|
||||
}
|
||||
@@ -17,7 +17,48 @@ mut:
|
||||
// new_location_db creates a new LocationDB instance
|
||||
pub fn new_location_db(reset bool) !LocationDB {
|
||||
mut db_dir := pathlib.get_dir(path:'${os.home_dir()}/hero/var/db/location.db',create: true)!
|
||||
db := sqlite.connect("${db_dir.path}/locations.db")!
|
||||
|
||||
// PostgreSQL connection parameters with defaults
|
||||
mut host := os.getenv('POSTGRES_HOST')
|
||||
if host == '' {
|
||||
host = 'localhost'
|
||||
}
|
||||
port := os.getenv('POSTGRES_PORT')
|
||||
port_num := if port == '' { 5432 } else { port.int() }
|
||||
mut user := os.getenv('POSTGRES_USER')
|
||||
if user == '' {
|
||||
user = 'postgres'
|
||||
}
|
||||
mut password := os.getenv('POSTGRES_PASSWORD')
|
||||
if password == '' {
|
||||
password = '1234'
|
||||
}
|
||||
mut dbname := os.getenv('POSTGRES_DB')
|
||||
if dbname == '' {
|
||||
dbname = 'locations'
|
||||
}
|
||||
|
||||
// First try to connect to create the database if it doesn't exist
|
||||
mut init_db := pg.connect(
|
||||
host: host
|
||||
port: port_num
|
||||
user: user
|
||||
password: password
|
||||
dbname: 'postgres'
|
||||
) or { return error('Failed to connect to PostgreSQL: ${err}') }
|
||||
|
||||
init_db.exec("CREATE DATABASE ${dbname}") or {}
|
||||
init_db.close()
|
||||
|
||||
// Now connect to our database
|
||||
db := pg.connect(
|
||||
host: host
|
||||
port: port_num
|
||||
user: user
|
||||
password: password
|
||||
dbname: dbname
|
||||
) or { return error('Failed to connect to PostgreSQL: ${err}') }
|
||||
|
||||
mut loc_db := LocationDB{
|
||||
db: db
|
||||
tmp_dir: pathlib.get_dir(path: '/tmp/location/',create: true)!
|
||||
@@ -30,9 +71,11 @@ pub fn new_location_db(reset bool) !LocationDB {
|
||||
// init_tables drops and recreates all tables
|
||||
fn (mut l LocationDB) init_tables(reset bool) ! {
|
||||
if reset {
|
||||
l.db.exec('DROP TABLE IF EXISTS AlternateName')!
|
||||
l.db.exec('DROP TABLE IF EXISTS City')!
|
||||
l.db.exec('DROP TABLE IF EXISTS Country')!
|
||||
sql l.db {
|
||||
drop table AlternateName
|
||||
drop table City
|
||||
drop table Country
|
||||
}!
|
||||
}
|
||||
|
||||
sql l.db {
|
||||
@@ -49,5 +92,5 @@ fn (mut l LocationDB) init_tables(reset bool) ! {
|
||||
|
||||
// close closes the database connection
|
||||
pub fn (mut l LocationDB) close() ! {
|
||||
l.db.close() or { return err }
|
||||
l.db.close()
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ pub:
|
||||
feature_class string @[max_len: 1] // For filtering (P for populated places)
|
||||
feature_code string @[max_len: 10] // Detailed type (PPL, PPLA, etc.)
|
||||
search_priority int
|
||||
accuracy u8 = 1 //1=estimated, 4=geonameid, 6=centroid of addresses or shape
|
||||
accuracy i16 = 1 //1=estimated, 4=geonameid, 6=centroid of addresses or shape
|
||||
}
|
||||
|
||||
pub struct AlternateName {
|
||||
|
||||
5
lib/data/location/readme.md
Normal file
5
lib/data/location/readme.md
Normal file
@@ -0,0 +1,5 @@
|
||||
|
||||
|
||||
make sure to do
|
||||
|
||||
brew install libpq
|
||||
@@ -1,177 +1,177 @@
|
||||
module location
|
||||
|
||||
import db.sqlite
|
||||
import db.pg
|
||||
|
||||
// // search searches for locations based on the provided options
|
||||
// pub fn (l Location) search(query string, country_code string, limit int, fuzzy bool) ![]SearchResult {
|
||||
// opts := SearchOptions{
|
||||
// query: query
|
||||
// country_code: country_code
|
||||
// limit: limit
|
||||
// fuzzy: fuzzy
|
||||
// }
|
||||
// return l.db.search_locations(opts)
|
||||
// }
|
||||
|
||||
// search searches for locations based on the provided options
|
||||
pub fn (l Location) search(query string, country_code string, limit int, fuzzy bool) ![]SearchResult {
|
||||
opts := SearchOptions{
|
||||
query: query
|
||||
country_code: country_code
|
||||
limit: limit
|
||||
fuzzy: fuzzy
|
||||
}
|
||||
return l.db.search_locations(opts)
|
||||
}
|
||||
// // search_near searches for locations near the given coordinates
|
||||
// pub fn (l Location) search_near(lat f64, lon f64, radius f64, limit int) ![]SearchResult {
|
||||
// opts := CoordinateSearchOptions{
|
||||
// coordinates: Coordinates{
|
||||
// latitude: lat
|
||||
// longitude: lon
|
||||
// }
|
||||
// radius: radius
|
||||
// limit: limit
|
||||
// }
|
||||
// return l.db.search_by_coordinates(opts)
|
||||
// }
|
||||
|
||||
// search_near searches for locations near the given coordinates
|
||||
pub fn (l Location) search_near(lat f64, lon f64, radius f64, limit int) ![]SearchResult {
|
||||
opts := CoordinateSearchOptions{
|
||||
coordinates: Coordinates{
|
||||
latitude: lat
|
||||
longitude: lon
|
||||
}
|
||||
radius: radius
|
||||
limit: limit
|
||||
}
|
||||
return l.db.search_by_coordinates(opts)
|
||||
}
|
||||
// // search_locations searches for locations based on the provided options
|
||||
// pub fn (l LocationDB) search_locations(opts SearchOptions) ![]SearchResult {
|
||||
// mut query_conditions := []string{}
|
||||
// mut params := []string{}
|
||||
|
||||
// if opts.query != '' {
|
||||
// if opts.fuzzy {
|
||||
// query_conditions << '(c.name ILIKE $${params.len + 1} OR c.ascii_name ILIKE $${params.len + 2})'
|
||||
// params << '%${opts.query}%'
|
||||
// params << '%${opts.query}%'
|
||||
// } else {
|
||||
// query_conditions << '(c.name = $${params.len + 1} OR c.ascii_name = $${params.len + 2})'
|
||||
// params << opts.query
|
||||
// params << opts.query
|
||||
// }
|
||||
// }
|
||||
|
||||
// search_locations searches for locations based on the provided options
|
||||
pub fn (l LocationDB) search_locations(opts SearchOptions) ![]SearchResult {
|
||||
mut query_conditions := []string{}
|
||||
mut params := []string{}
|
||||
// if opts.country_code != '' {
|
||||
// query_conditions << 'c.country_iso2 = $${params.len + 1}'
|
||||
// params << opts.country_code
|
||||
// }
|
||||
|
||||
if opts.query != '' {
|
||||
if opts.fuzzy {
|
||||
query_conditions << '(c.name LIKE ? OR c.ascii_name LIKE ?)'
|
||||
params << '%${opts.query}%'
|
||||
params << '%${opts.query}%'
|
||||
} else {
|
||||
query_conditions << '(c.name = ? OR c.ascii_name = ?)'
|
||||
params << opts.query
|
||||
params << opts.query
|
||||
}
|
||||
}
|
||||
// where_clause := if query_conditions.len > 0 { 'WHERE ' + query_conditions.join(' AND ') } else { '' }
|
||||
|
||||
if opts.country_code != '' {
|
||||
query_conditions << 'c.country_iso2 = ?'
|
||||
params << opts.country_code
|
||||
}
|
||||
// query := '
|
||||
// SELECT c.*, co.*
|
||||
// FROM City c
|
||||
// JOIN Country co ON c.country_iso2 = co.iso2
|
||||
// ${where_clause}
|
||||
// ORDER BY c.search_priority DESC, c.population DESC
|
||||
// LIMIT ${opts.limit}
|
||||
// '
|
||||
|
||||
where_clause := if query_conditions.len > 0 { 'WHERE ' + query_conditions.join(' AND ') } else { '' }
|
||||
// rows := l.db.exec_param_many(query, params)!
|
||||
// mut results := []SearchResult{cap: rows.len}
|
||||
|
||||
query := '
|
||||
SELECT c.*, co.*
|
||||
FROM City c
|
||||
JOIN Country co ON c.country_iso2 = co.iso2
|
||||
${where_clause}
|
||||
ORDER BY c.search_priority DESC, c.population DESC
|
||||
LIMIT ${opts.limit}
|
||||
'
|
||||
// for row in rows {
|
||||
// city := City{
|
||||
// id: row.vals[0].int() or { 0 }
|
||||
// name: row.vals[1] or { '' }
|
||||
// ascii_name: row.vals[2] or { '' }
|
||||
// country_iso2: row.vals[3] or { '' }
|
||||
// postal_code: row.vals[4] or { '' }
|
||||
// state_name: row.vals[5] or { '' }
|
||||
// state_code: row.vals[6] or { '' }
|
||||
// county_name: row.vals[7] or { '' }
|
||||
// county_code: row.vals[8] or { '' }
|
||||
// community_name: row.vals[9] or { '' }
|
||||
// community_code: row.vals[10] or { '' }
|
||||
// latitude: row.vals[11].f64() or { 0.0 }
|
||||
// longitude: row.vals[12].f64() or { 0.0 }
|
||||
// population: row.vals[13].i64() or { 0 }
|
||||
// timezone: row.vals[14] or { '' }
|
||||
// feature_class: row.vals[15] or { '' }
|
||||
// feature_code: row.vals[16] or { '' }
|
||||
// search_priority: row.vals[17].int() or { 0 }
|
||||
// accuracy: u8(row.vals[18].int() or { 1 })
|
||||
// }
|
||||
|
||||
rows := l.db.exec_param_many(query, params)!
|
||||
mut results := []SearchResult{cap: rows.len}
|
||||
// country := Country{
|
||||
// iso2: row.vals[19] or { '' }
|
||||
// name: row.vals[20] or { '' }
|
||||
// iso3: row.vals[21] or { '' }
|
||||
// continent: row.vals[22] or { '' }
|
||||
// population: row.vals[23].i64() or { 0 }
|
||||
// timezone: row.vals[24] or { '' }
|
||||
// import_date: row.vals[25].i64() or { 0 }
|
||||
// }
|
||||
|
||||
for row in rows {
|
||||
city := City{
|
||||
id: row.vals[0].int()
|
||||
name: row.vals[1]
|
||||
ascii_name: row.vals[2]
|
||||
country_iso2: row.vals[3]
|
||||
postal_code: row.vals[4]
|
||||
state_name: row.vals[5]
|
||||
state_code: row.vals[6]
|
||||
county_name: row.vals[7]
|
||||
county_code: row.vals[8]
|
||||
community_name: row.vals[9]
|
||||
community_code: row.vals[10]
|
||||
latitude: row.vals[11].f64()
|
||||
longitude: row.vals[12].f64()
|
||||
population: row.vals[13].i64()
|
||||
timezone: row.vals[14]
|
||||
feature_class: row.vals[15]
|
||||
feature_code: row.vals[16]
|
||||
search_priority: row.vals[17].int()
|
||||
accuracy: row.vals[18].u8()
|
||||
}
|
||||
// results << SearchResult{
|
||||
// city: city
|
||||
// country: country
|
||||
// similarity: 1.0 // TODO: implement proper similarity scoring
|
||||
// }
|
||||
// }
|
||||
|
||||
country := Country{
|
||||
iso2: row.vals[19]
|
||||
name: row.vals[20]
|
||||
iso3: row.vals[21]
|
||||
continent: row.vals[22]
|
||||
population: row.vals[23].i64()
|
||||
timezone: row.vals[24]
|
||||
import_date: row.vals[25].i64()
|
||||
}
|
||||
// return results
|
||||
// }
|
||||
|
||||
results << SearchResult{
|
||||
city: city
|
||||
country: country
|
||||
similarity: 1.0 // TODO: implement proper similarity scoring
|
||||
}
|
||||
}
|
||||
|
||||
return results
|
||||
}
|
||||
|
||||
// search_by_coordinates finds locations near the given coordinates
|
||||
pub fn (l LocationDB) search_by_coordinates(opts CoordinateSearchOptions) ![]SearchResult {
|
||||
// Use the Haversine formula to calculate distances
|
||||
query := "
|
||||
SELECT c.*, co.*,
|
||||
(6371 * acos(cos(radians(?)) * cos(radians(latitude)) *
|
||||
cos(radians(longitude) - radians(?)) + sin(radians(?)) *
|
||||
sin(radians(latitude)))) AS distance
|
||||
FROM City c
|
||||
JOIN Country co ON c.country_iso2 = co.iso2
|
||||
HAVING distance < ?
|
||||
ORDER BY distance
|
||||
LIMIT ?
|
||||
"
|
||||
// // search_by_coordinates finds locations near the given coordinates
|
||||
// pub fn (l LocationDB) search_by_coordinates(opts CoordinateSearchOptions) ![]SearchResult {
|
||||
// // Use the Haversine formula to calculate distances
|
||||
// query := "
|
||||
// WITH distances AS (
|
||||
// SELECT c.*, co.*,
|
||||
// (6371 * acos(cos(radians($1)) * cos(radians(latitude)) *
|
||||
// cos(radians(longitude) - radians($2)) + sin(radians($1)) *
|
||||
// sin(radians(latitude)))) AS distance
|
||||
// FROM City c
|
||||
// JOIN Country co ON c.country_iso2 = co.iso2
|
||||
// )
|
||||
// SELECT * FROM distances
|
||||
// WHERE distance < $3
|
||||
// ORDER BY distance
|
||||
// LIMIT $4
|
||||
// "
|
||||
|
||||
params := [
|
||||
opts.coordinates.latitude.str(),
|
||||
opts.coordinates.longitude.str(),
|
||||
opts.coordinates.latitude.str(),
|
||||
opts.radius.str(),
|
||||
opts.limit.str()
|
||||
]
|
||||
rows := l.db.exec_param_many(query, params)!
|
||||
// params := [
|
||||
// opts.coordinates.latitude.str(),
|
||||
// opts.coordinates.longitude.str(),
|
||||
// opts.radius.str(),
|
||||
// opts.limit.str()
|
||||
// ]
|
||||
// rows := l.db.exec_param_many(query, params)!
|
||||
|
||||
mut results := []SearchResult{cap: rows.len}
|
||||
// mut results := []SearchResult{cap: rows.len}
|
||||
|
||||
for row in rows {
|
||||
city := City{
|
||||
id: row.vals[0].int()
|
||||
name: row.vals[1]
|
||||
ascii_name: row.vals[2]
|
||||
country_iso2: row.vals[3]
|
||||
postal_code: row.vals[4]
|
||||
state_name: row.vals[5]
|
||||
state_code: row.vals[6]
|
||||
county_name: row.vals[7]
|
||||
county_code: row.vals[8]
|
||||
community_name: row.vals[9]
|
||||
community_code: row.vals[10]
|
||||
latitude: row.vals[11].f64()
|
||||
longitude: row.vals[12].f64()
|
||||
population: row.vals[13].i64()
|
||||
timezone: row.vals[14]
|
||||
feature_class: row.vals[15]
|
||||
feature_code: row.vals[16]
|
||||
search_priority: row.vals[17].int()
|
||||
accuracy: row.vals[18].u8()
|
||||
}
|
||||
// for row in rows {
|
||||
// city := City{
|
||||
// id: row.vals[0].int() or { 0 }
|
||||
// name: row.vals[1] or { '' }
|
||||
// ascii_name: row.vals[2] or { '' }
|
||||
// country_iso2: row.vals[3] or { '' }
|
||||
// postal_code: row.vals[4] or { '' }
|
||||
// state_name: row.vals[5] or { '' }
|
||||
// state_code: row.vals[6] or { '' }
|
||||
// county_name: row.vals[7] or { '' }
|
||||
// county_code: row.vals[8] or { '' }
|
||||
// community_name: row.vals[9] or { '' }
|
||||
// community_code: row.vals[10] or { '' }
|
||||
// latitude: row.vals[11].f64() or { 0.0 }
|
||||
// longitude: row.vals[12].f64() or { 0.0 }
|
||||
// population: row.vals[13].i64() or { 0 }
|
||||
// timezone: row.vals[14] or { '' }
|
||||
// feature_class: row.vals[15] or { '' }
|
||||
// feature_code: row.vals[16] or { '' }
|
||||
// search_priority: row.vals[17].int() or { 0 }
|
||||
// accuracy: u8(row.vals[18].int() or { 1 })
|
||||
// }
|
||||
|
||||
country := Country{
|
||||
iso2: row.vals[19]
|
||||
name: row.vals[20]
|
||||
iso3: row.vals[21]
|
||||
continent: row.vals[22]
|
||||
population: row.vals[23].i64()
|
||||
timezone: row.vals[24]
|
||||
import_date: row.vals[25].i64()
|
||||
}
|
||||
// country := Country{
|
||||
// iso2: row.vals[19] or { '' }
|
||||
// name: row.vals[20] or { '' }
|
||||
// iso3: row.vals[21] or { '' }
|
||||
// continent: row.vals[22] or { '' }
|
||||
// population: row.vals[23].i64() or { 0 }
|
||||
// timezone: row.vals[24] or { '' }
|
||||
// import_date: row.vals[25].i64() or { 0 }
|
||||
// }
|
||||
|
||||
results << SearchResult{
|
||||
city: city
|
||||
country: country
|
||||
similarity: 1.0
|
||||
}
|
||||
}
|
||||
// results << SearchResult{
|
||||
// city: city
|
||||
// country: country
|
||||
// similarity: 1.0
|
||||
// }
|
||||
// }
|
||||
|
||||
return results
|
||||
}
|
||||
// return results
|
||||
// }
|
||||
|
||||
Reference in New Issue
Block a user