addrkit: 'selfcheck' command — resolve the whole registry against itself
Exhaustive check of all 72,825 indexed addresses: 72,811 exact (99.98%), 14 partial (malformed house units, e.g. '27с1', '32 к 1/4-1/6'), 0 not_found, 0 ambiguous.
This commit is contained in:
@@ -16,6 +16,7 @@ import (
|
||||
"git.totmin.ru/en2zmax/addrkit/internal/adapters/llm"
|
||||
"git.totmin.ru/en2zmax/addrkit/internal/adapters/registrystore"
|
||||
"git.totmin.ru/en2zmax/addrkit/internal/config"
|
||||
"git.totmin.ru/en2zmax/addrkit/internal/domain/address"
|
||||
"git.totmin.ru/en2zmax/addrkit/internal/domain/match"
|
||||
"git.totmin.ru/en2zmax/addrkit/internal/eval"
|
||||
"git.totmin.ru/en2zmax/addrkit/internal/seed"
|
||||
@@ -35,6 +36,8 @@ func main() {
|
||||
os.Exit(runResolve(os.Args[2:]))
|
||||
case "eval":
|
||||
os.Exit(runEval(os.Args[2:]))
|
||||
case "selfcheck":
|
||||
os.Exit(runSelfcheck(os.Args[2:]))
|
||||
case "help", "-h", "--help":
|
||||
usage()
|
||||
os.Exit(0)
|
||||
@@ -53,6 +56,7 @@ Usage:
|
||||
addrkit verify -db registry.db
|
||||
addrkit resolve -db registry.db "2-я Станционная улица 1/1 стр."
|
||||
addrkit eval -db registry.db [-sample 500] [-seed 1]
|
||||
addrkit selfcheck -db registry.db
|
||||
`)
|
||||
}
|
||||
|
||||
@@ -265,3 +269,57 @@ func (c *countingRewriter) avg() time.Duration {
|
||||
}
|
||||
return c.total / time.Duration(c.calls)
|
||||
}
|
||||
|
||||
// runSelfcheck resolves every indexed address against the index and reports how
|
||||
// many resolve back to themselves. This is the strongest answer to "do all
|
||||
// addresses resolve?": it exercises the whole registry, not a sample.
|
||||
func runSelfcheck(args []string) int {
|
||||
fs := flag.NewFlagSet("selfcheck", flag.ExitOnError)
|
||||
db := fs.String("db", "", "SQLite database")
|
||||
_ = fs.Parse(args)
|
||||
if *db == "" {
|
||||
fmt.Fprintln(os.Stderr, "selfcheck: -db is required")
|
||||
return 2
|
||||
}
|
||||
_, entries, err := registrystore.Load(*db)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "selfcheck: %v\n", err)
|
||||
return 1
|
||||
}
|
||||
resolver := match.NewResolver(match.NewIndex("selfcheck", entries, match.DefaultOptions()), nil)
|
||||
|
||||
exact := 0
|
||||
byStatus := map[string]int{}
|
||||
var failures []string
|
||||
for _, e := range entries {
|
||||
res := resolver.Resolve(context.Background(), e.Address)
|
||||
if res.Status == address.StatusFound && sameEntry(res.Address, e) {
|
||||
exact++
|
||||
continue
|
||||
}
|
||||
byStatus[string(res.Status)]++
|
||||
if len(failures) < 40 {
|
||||
failures = append(failures, fmt.Sprintf("[%s] %q -> %q", res.Status, e.Address, res.Address))
|
||||
}
|
||||
}
|
||||
out := map[string]any{
|
||||
"entries": len(entries),
|
||||
"exact": exact,
|
||||
"exact_rate": float64(exact) / float64(max(len(entries), 1)),
|
||||
"non_exact": byStatus,
|
||||
"failures": failures,
|
||||
}
|
||||
enc := json.NewEncoder(os.Stdout)
|
||||
enc.SetIndent("", " ")
|
||||
_ = enc.Encode(out)
|
||||
return 0
|
||||
}
|
||||
|
||||
// sameEntry reports whether a resolved address is the same street and house
|
||||
// unit as the registry entry it came from.
|
||||
func sameEntry(resolved string, e address.Entry) bool {
|
||||
p := address.Parse(resolved)
|
||||
return p.StreetKey == e.StreetKey &&
|
||||
p.House == e.House && p.HouseFrac == e.HouseFrac && p.HouseLetter == e.HouseLetter &&
|
||||
p.Building == e.Building && p.BuildingFrac == e.BuildingFrac && p.BuildingLetter == e.BuildingLetter
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user