Save address IDs when doing pool geocoding

This commit is contained in:
Eli Ribble 2026-04-15 20:29:42 +00:00
parent 6a8ae6d81a
commit ac27c60e0c
No known key found for this signature in database
8 changed files with 447 additions and 52 deletions

View file

@ -9,6 +9,7 @@ import (
"github.com/Gleipnir-Technology/bob/dialect/psql"
"github.com/Gleipnir-Technology/bob/dialect/psql/dialect"
"github.com/Gleipnir-Technology/bob/dialect/psql/im"
"github.com/Gleipnir-Technology/bob/dialect/psql/sm"
//bobtypes "github.com/Gleipnir-Technology/bob/types"
"github.com/Gleipnir-Technology/nidus-sync/db/models"
"github.com/Gleipnir-Technology/nidus-sync/h3utils"
@ -124,9 +125,11 @@ func insertAddress(ctx context.Context, txn bob.Executor, address types.Address)
}
return &row.ID, nil
}
func insertAddresses(ctx context.Context, txn bob.Executor, features []stadia.GeocodeFeature) ([]int32, error) {
func insertAddresses(ctx context.Context, txn bob.Executor, features []stadia.GeocodeFeature) ([]types.Address, error) {
query := addressQuery()
for _, feature := range features {
gids := make([]string, len(features))
for i, feature := range features {
gids[i] = feature.Properties.GID
lng := feature.Geometry.Coordinates[0]
lat := feature.Geometry.Coordinates[1]
cell, err := h3utils.GetCell(lng, lat, 15)
@ -148,15 +151,24 @@ func insertAddresses(ctx context.Context, txn bob.Executor, features []stadia.Ge
psql.Arg(feature.Street()),
psql.Raw("''"),
),
im.OnConflict("gid").DoNothing(),
)
}
rows, err := bob.All(ctx, txn, query, scan.StructMapper[_rowWithID]())
_, err := bob.All(ctx, txn, query, scan.StructMapper[_rowWithID]())
if err != nil {
return nil, fmt.Errorf("insert: %w", err)
}
results := make([]int32, len(rows))
for _, row := range rows {
results = append(results, row.ID)
addresses, err := models.Addresses.Query(
sm.Where(
models.Addresses.Columns.Gid.EQ(psql.Any(gids)),
),
).All(ctx, txn)
if err != nil {
return nil, fmt.Errorf("query by gid: %w", err)
}
results := make([]types.Address, len(addresses))
for i, address := range addresses {
results[i] = types.AddressFromModel(address)
}
return results, nil
}

View file

@ -4,7 +4,6 @@ import (
"context"
"encoding/json"
"fmt"
"strings"
"time"
"github.com/Gleipnir-Technology/bob"
@ -58,8 +57,11 @@ func GeocodeRaw(ctx context.Context, org *models.Organization, address string) (
if err != nil {
return nil, fmt.Errorf("client raw geocode failure on %s: %w", address, err)
}
insertAddresses(ctx, db.PGInstance.BobDB, resp.Features)
return toGeocodeResult(*resp, address)
addresses, err := insertAddresses(ctx, db.PGInstance.BobDB, resp.Features)
if err != nil {
return nil, fmt.Errorf("insert addresses: %w", err)
}
return toGeocodeResult(*resp, address, addresses)
}
func GeocodeStructured(ctx context.Context, org *models.Organization, a types.Address) (*GeocodeResult, error) {
street := fmt.Sprintf("%s %s", a.Number, a.Street)
@ -75,8 +77,11 @@ func GeocodeStructured(ctx context.Context, org *models.Organization, a types.Ad
if err != nil {
return nil, fmt.Errorf("client structured geocode failure on %s: %w", a.String(), err)
}
insertAddresses(ctx, db.PGInstance.BobDB, resp.Features)
return toGeocodeResult(*resp, a.String())
addresses, err := insertAddresses(ctx, db.PGInstance.BobDB, resp.Features)
if err != nil {
return nil, fmt.Errorf("insert addresses: %w", err)
}
return toGeocodeResult(*resp, a.String(), addresses)
}
func ReverseGeocode(ctx context.Context, location types.Location) (*GeocodeResult, error) {
req := stadia.RequestReverseGeocode{
@ -87,54 +92,33 @@ func ReverseGeocode(ctx context.Context, location types.Location) (*GeocodeResul
if err != nil {
return nil, fmt.Errorf("client reverse geocode failure on %s: %w", location.String(), err)
}
insertAddresses(ctx, db.PGInstance.BobDB, resp.Features)
return toGeocodeResult(*resp, location.String())
addresses, err := insertAddresses(ctx, db.PGInstance.BobDB, resp.Features)
if err != nil {
return nil, fmt.Errorf("insert addresses: %w", err)
}
return toGeocodeResult(*resp, location.String(), addresses)
}
func toGeocodeResult(resp stadia.GeocodeResponse, address_msg string) (*GeocodeResult, error) {
func toGeocodeResult(resp stadia.GeocodeResponse, address_msg string, addresses []types.Address) (*GeocodeResult, error) {
if len(resp.Features) < 1 {
return nil, fmt.Errorf("%s matched no locations", address_msg)
}
feature := resp.Features[0]
if len(addresses) < 1 {
return nil, fmt.Errorf("no addresses")
}
if len(resp.Features) > 1 {
if !allFeaturesIdenticalEnough(resp.Features) {
return nil, fmt.Errorf("%s matched more than one location, and they differ a lot", address_msg)
}
}
feature := resp.Features[0]
address := addresses[0]
if feature.Geometry.Type != "Point" {
return nil, fmt.Errorf("wrong type %s from %s", feature.Geometry.Type, address_msg)
}
longitude := feature.Geometry.Coordinates[0]
latitude := feature.Geometry.Coordinates[1]
cell, err := h3utils.GetCell(longitude, latitude, 15)
cell, err := h3utils.GetCell(address.Location.Longitude, address.Location.Latitude, 15)
if err != nil {
return nil, fmt.Errorf("failed to convert lat %f lng %f to h3 cell", longitude, latitude)
}
country_s := strings.ToLower(feature.Properties.CountryA)
// Depending on what kind of request we made we'll get wildly different result structures
// This first structure generally works for forword geocoding
address := types.Address{
Country: country_s,
GID: feature.Properties.GID,
Locality: feature.Properties.Locality,
Location: &types.Location{
Longitude: feature.Geometry.Coordinates[0],
Latitude: feature.Geometry.Coordinates[1],
},
Number: feature.Properties.HouseNumber,
PostalCode: feature.Properties.PostalCode,
Region: feature.Properties.Region,
Raw: feature.Properties.FormattedAddressLine,
Street: feature.Properties.Street,
Unit: "",
}
// If we don't have a locality, try populating for reverse geocoding
if address.Country == "" {
address.Country = strings.ToLower(feature.Properties.Context.ISO3166A3)
address.Locality = feature.Properties.Context.WhosOnFirst.Locality.Name
address.Number = feature.Properties.AddressComponents.Number
address.PostalCode = feature.Properties.AddressComponents.PostalCode
address.Street = feature.Properties.AddressComponents.Street
return nil, fmt.Errorf("failed to convert lat %f lng %f to h3 cell", address.Location.Longitude, address.Location.Latitude)
}
return &GeocodeResult{
Address: address,