@mailwoman/resolver-wof-sqlite 9.3.0 → 10.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/address/index.ts +3 -3
- package/lib/address/{point.ts → point/index.ts} +55 -7
- package/lib/address/{point-interpolation.ts → point/interpolation.ts} +2 -3
- package/lib/ancestry/backfill.ts +4 -2
- package/lib/ancestry/index.ts +1 -2
- package/lib/{build-candidate.ts → build/candidate.ts} +62 -31
- package/lib/candidate/alias-bags.ts +68 -7
- package/lib/{candidate-ancestors-schema.ts → candidate/ancestors/schema.ts} +1 -1
- package/lib/candidate/{ancestors-sidecar.ts → ancestors/sidecar.ts} +2 -2
- package/lib/candidate/extract-fold.ts +91 -10
- package/lib/{candidate-importance.ts → candidate/importance.ts} +1 -1
- package/lib/{candidate-lookup.ts → candidate/lookup.ts} +13 -16
- package/lib/candidate/name-roles.ts +2 -2
- package/lib/candidate/own-name.ts +1 -0
- package/lib/{candidate-schema.ts → candidate/schema.ts} +2 -2
- package/lib/{candidate-scoring.ts → candidate/scoring.ts} +1 -1
- package/lib/coincident-roles.ts +2 -3
- package/lib/coverage-manifest-schema.ts +2 -1
- package/lib/currency-backfill.ts +2 -2
- package/lib/fst/builder.ts +1 -1
- package/lib/fst/deserialize-web.ts +2 -2
- package/lib/fst/freshness.ts +0 -1
- package/lib/fst/serialize.ts +9 -9
- package/lib/geonames/aliases.ts +1 -7
- package/lib/geonames/postal.ts +1 -7
- package/lib/index.ts +18 -25
- package/lib/interpolation.ts +1 -1
- package/lib/lookup.ts +6 -5
- package/lib/place-importance-schema.ts +1 -9
- package/lib/poi/lookup.ts +1 -2
- package/lib/postal/{city-alias-lookup.ts → city/alias/lookup.ts} +1 -1
- package/lib/postal/index.ts +3 -3
- package/lib/primary-preference.ts +15 -5
- package/lib/reverse.ts +1 -2
- package/lib/search-fetch.ts +1 -2
- package/lib/{sqlite-utils.ts → sqlite/utils.ts} +0 -1
- package/lib/street/{centroid.ts → centroid/index.ts} +2 -2
- package/lib/street/index.ts +4 -4
- package/lib/street/{morphology-fst-builder.ts → morphology/fst/builder.ts} +8 -2
- package/lib/street/{morphology-fst-loader.ts → morphology/fst/loader.ts} +1 -1
- package/lib/street/normalize.ts +42 -1
- package/lib/uprn/lookup.ts +1 -2
- package/lib/weights-overlay-linker.ts +5 -4
- package/out/address/index.d.ts +3 -3
- package/out/address/index.d.ts.map +1 -1
- package/out/address/index.js +3 -3
- package/out/address/index.js.map +1 -1
- package/out/address/{point.d.ts → point/index.d.ts} +5 -3
- package/out/address/point/index.d.ts.map +1 -0
- package/out/address/{point.js → point/index.js} +38 -7
- package/out/address/point/index.js.map +1 -0
- package/out/address/{point-interpolation.d.ts → point/interpolation.d.ts} +3 -3
- package/out/address/point/interpolation.d.ts.map +1 -0
- package/out/address/{point-interpolation.js → point/interpolation.js} +1 -1
- package/out/address/point/interpolation.js.map +1 -0
- package/out/address/{point-schema.d.ts → point/schema.d.ts} +1 -1
- package/out/address/point/schema.d.ts.map +1 -0
- package/out/address/{point-schema.js → point/schema.js} +1 -1
- package/out/address/point/schema.js.map +1 -0
- package/out/ancestry/backfill.d.ts.map +1 -1
- package/out/ancestry/backfill.js +4 -2
- package/out/ancestry/backfill.js.map +1 -1
- package/out/ancestry/index.d.ts.map +1 -1
- package/out/ancestry/index.js +1 -1
- package/out/ancestry/index.js.map +1 -1
- package/out/{build-candidate.d.ts → build/candidate.d.ts} +12 -4
- package/out/build/candidate.d.ts.map +1 -0
- package/out/{build-candidate.js → build/candidate.js} +39 -24
- package/out/build/candidate.js.map +1 -0
- package/out/{build-slim.d.ts → build/slim.d.ts} +1 -1
- package/out/build/slim.d.ts.map +1 -0
- package/out/{build-slim.js → build/slim.js} +1 -1
- package/out/build/slim.js.map +1 -0
- package/out/candidate/alias-bags.d.ts +10 -2
- package/out/candidate/alias-bags.d.ts.map +1 -1
- package/out/candidate/alias-bags.js +49 -5
- package/out/candidate/alias-bags.js.map +1 -1
- package/out/{candidate-ancestors-schema.d.ts → candidate/ancestors/schema.d.ts} +2 -2
- package/out/candidate/ancestors/schema.d.ts.map +1 -0
- package/out/{candidate-ancestors-schema.js → candidate/ancestors/schema.js} +1 -1
- package/out/candidate/ancestors/schema.js.map +1 -0
- package/out/candidate/{ancestors-sidecar.d.ts → ancestors/sidecar.d.ts} +2 -2
- package/out/candidate/ancestors/sidecar.d.ts.map +1 -0
- package/out/candidate/{ancestors-sidecar.js → ancestors/sidecar.js} +2 -2
- package/out/candidate/ancestors/sidecar.js.map +1 -0
- package/out/candidate/extract-fold.d.ts +12 -3
- package/out/candidate/extract-fold.d.ts.map +1 -1
- package/out/candidate/extract-fold.js +57 -8
- package/out/candidate/extract-fold.js.map +1 -1
- package/out/{candidate-fts.d.ts → candidate/fts.d.ts} +1 -1
- package/out/candidate/fts.d.ts.map +1 -0
- package/out/{candidate-fts.js → candidate/fts.js} +1 -1
- package/out/candidate/fts.js.map +1 -0
- package/out/{candidate-importance.d.ts → candidate/importance.d.ts} +1 -1
- package/out/candidate/importance.d.ts.map +1 -0
- package/out/{candidate-importance.js → candidate/importance.js} +1 -1
- package/out/candidate/importance.js.map +1 -0
- package/out/{candidate-lookup.d.ts → candidate/lookup.d.ts} +7 -7
- package/out/candidate/lookup.d.ts.map +1 -0
- package/out/{candidate-lookup.js → candidate/lookup.js} +13 -11
- package/out/candidate/lookup.js.map +1 -0
- package/out/candidate/name-roles.d.ts +1 -1
- package/out/candidate/name-roles.d.ts.map +1 -1
- package/out/candidate/name-roles.js +1 -1
- package/out/candidate/name-roles.js.map +1 -1
- package/out/candidate/own-name.d.ts.map +1 -1
- package/out/candidate/own-name.js +1 -0
- package/out/candidate/own-name.js.map +1 -1
- package/out/{candidate-schema.d.ts → candidate/schema.d.ts} +3 -3
- package/out/candidate/schema.d.ts.map +1 -0
- package/out/{candidate-schema.js → candidate/schema.js} +2 -2
- package/out/candidate/schema.js.map +1 -0
- package/out/{candidate-scoring.d.ts → candidate/scoring.d.ts} +1 -1
- package/out/candidate/scoring.d.ts.map +1 -0
- package/out/{candidate-scoring.js → candidate/scoring.js} +2 -2
- package/out/candidate/scoring.js.map +1 -0
- package/out/capitals.d.ts.map +1 -1
- package/out/coincident-roles.d.ts +1 -1
- package/out/coincident-roles.d.ts.map +1 -1
- package/out/coincident-roles.js +1 -1
- package/out/coincident-roles.js.map +1 -1
- package/out/convention/index.d.ts.map +1 -1
- package/out/convention/index.js.map +1 -1
- package/out/coverage-manifest-schema.d.ts.map +1 -1
- package/out/coverage-manifest-schema.js +2 -1
- package/out/coverage-manifest-schema.js.map +1 -1
- package/out/currency-backfill.d.ts +2 -2
- package/out/currency-backfill.d.ts.map +1 -1
- package/out/env.d.ts +1 -1
- package/out/fst/autocomplete.js.map +1 -1
- package/out/fst/builder.d.ts.map +1 -1
- package/out/fst/builder.js +1 -1
- package/out/fst/builder.js.map +1 -1
- package/out/fst/deserialize-web.js +2 -2
- package/out/fst/deserialize-web.js.map +1 -1
- package/out/fst/freshness.d.ts +0 -1
- package/out/fst/freshness.d.ts.map +1 -1
- package/out/fst/freshness.js +0 -1
- package/out/fst/freshness.js.map +1 -1
- package/out/fst/matcher.d.ts.map +1 -1
- package/out/fst/serialize.js +9 -9
- package/out/fst/serialize.js.map +1 -1
- package/out/fts/index.js.map +1 -1
- package/out/geonames/aliases.d.ts +0 -5
- package/out/geonames/aliases.d.ts.map +1 -1
- package/out/geonames/aliases.js +1 -6
- package/out/geonames/aliases.js.map +1 -1
- package/out/geonames/postal.d.ts +0 -6
- package/out/geonames/postal.d.ts.map +1 -1
- package/out/geonames/postal.js +1 -6
- package/out/geonames/postal.js.map +1 -1
- package/out/index.d.ts +18 -19
- package/out/index.d.ts.map +1 -1
- package/out/index.js +13 -14
- package/out/index.js.map +1 -1
- package/out/interpolation.d.ts +1 -1
- package/out/interpolation.d.ts.map +1 -1
- package/out/lookup.d.ts +2 -2
- package/out/lookup.d.ts.map +1 -1
- package/out/lookup.js +4 -4
- package/out/lookup.js.map +1 -1
- package/out/nsul/lookup.d.ts.map +1 -1
- package/out/place-importance-schema.d.ts +0 -1
- package/out/place-importance-schema.d.ts.map +1 -1
- package/out/place-importance-schema.js +1 -2
- package/out/place-importance-schema.js.map +1 -1
- package/out/poi/lookup.d.ts.map +1 -1
- package/out/poi/lookup.js +1 -1
- package/out/poi/lookup.js.map +1 -1
- package/out/poi/schema.d.ts.map +1 -1
- package/out/postal/{city-alias-lookup.d.ts → city/alias/lookup.d.ts} +2 -2
- package/out/postal/city/alias/lookup.d.ts.map +1 -0
- package/out/postal/{city-alias-lookup.js → city/alias/lookup.js} +1 -1
- package/out/postal/city/alias/lookup.js.map +1 -0
- package/out/postal/{city-alias-schema.d.ts → city/alias/schema.d.ts} +1 -1
- package/out/postal/city/alias/schema.d.ts.map +1 -0
- package/out/postal/{city-alias-schema.js → city/alias/schema.js} +1 -1
- package/out/postal/city/alias/schema.js.map +1 -0
- package/out/postal/{city-candidate-schema.d.ts → city/candidate-schema.d.ts} +1 -1
- package/out/postal/city/candidate-schema.d.ts.map +1 -0
- package/out/postal/{city-candidate-schema.js → city/candidate-schema.js} +1 -1
- package/out/postal/city/candidate-schema.js.map +1 -0
- package/out/postal/index.d.ts +3 -3
- package/out/postal/index.js +3 -3
- package/out/postcode-point-lookup.d.ts.map +1 -1
- package/out/primary-preference.d.ts +3 -3
- package/out/primary-preference.d.ts.map +1 -1
- package/out/primary-preference.js +12 -4
- package/out/primary-preference.js.map +1 -1
- package/out/reverse.d.ts.map +1 -1
- package/out/reverse.js +1 -1
- package/out/reverse.js.map +1 -1
- package/out/search-fetch.d.ts.map +1 -1
- package/out/search-fetch.js +1 -1
- package/out/search-fetch.js.map +1 -1
- package/out/{sqlite-convention-source.d.ts → sqlite/convention-source.d.ts} +1 -1
- package/out/sqlite/convention-source.d.ts.map +1 -0
- package/out/{sqlite-convention-source.js → sqlite/convention-source.js} +1 -1
- package/out/sqlite/convention-source.js.map +1 -0
- package/out/{sqlite-utils.d.ts → sqlite/utils.d.ts} +1 -2
- package/out/sqlite/utils.d.ts.map +1 -0
- package/out/{sqlite-utils.js → sqlite/utils.js} +1 -4
- package/out/sqlite/utils.js.map +1 -0
- package/out/street/{centroid.d.ts → centroid/index.d.ts} +2 -2
- package/out/street/centroid/index.d.ts.map +1 -0
- package/out/street/{centroid.js → centroid/index.js} +1 -1
- package/out/street/centroid/index.js.map +1 -0
- package/out/street/{centroid-schema.d.ts → centroid/schema.d.ts} +1 -1
- package/out/street/centroid/schema.d.ts.map +1 -0
- package/out/street/{centroid-schema.js → centroid/schema.js} +1 -1
- package/out/street/centroid/schema.js.map +1 -0
- package/out/street/index.d.ts +4 -4
- package/out/street/index.d.ts.map +1 -1
- package/out/street/index.js +4 -4
- package/out/street/index.js.map +1 -1
- package/out/street/{morphology-fst-builder.d.ts → morphology/fst/builder.d.ts} +1 -1
- package/out/street/morphology/fst/builder.d.ts.map +1 -0
- package/out/street/{morphology-fst-builder.js → morphology/fst/builder.js} +8 -3
- package/out/street/morphology/fst/builder.js.map +1 -0
- package/out/street/{morphology-fst-loader.d.ts → morphology/fst/loader.d.ts} +1 -1
- package/out/street/morphology/fst/loader.d.ts.map +1 -0
- package/out/street/{morphology-fst-loader.js → morphology/fst/loader.js} +2 -2
- package/out/street/morphology/fst/loader.js.map +1 -0
- package/out/street/name-lookup.d.ts.map +1 -1
- package/out/street/name-lookup.js.map +1 -1
- package/out/street/normalize.d.ts +13 -1
- package/out/street/normalize.d.ts.map +1 -1
- package/out/street/normalize.js +37 -0
- package/out/street/normalize.js.map +1 -1
- package/out/street/segment-schema.d.ts.map +1 -1
- package/out/uprn/lookup.d.ts.map +1 -1
- package/out/uprn/lookup.js +1 -1
- package/out/uprn/lookup.js.map +1 -1
- package/out/weights-overlay-linker.d.ts.map +1 -1
- package/out/weights-overlay-linker.js +4 -3
- package/out/weights-overlay-linker.js.map +1 -1
- package/package.json +64 -64
- package/out/address/point-interpolation.d.ts.map +0 -1
- package/out/address/point-interpolation.js.map +0 -1
- package/out/address/point-schema.d.ts.map +0 -1
- package/out/address/point-schema.js.map +0 -1
- package/out/address/point.d.ts.map +0 -1
- package/out/address/point.js.map +0 -1
- package/out/build-candidate.d.ts.map +0 -1
- package/out/build-candidate.js.map +0 -1
- package/out/build-slim.d.ts.map +0 -1
- package/out/build-slim.js.map +0 -1
- package/out/candidate/ancestors-sidecar.d.ts.map +0 -1
- package/out/candidate/ancestors-sidecar.js.map +0 -1
- package/out/candidate-ancestors-schema.d.ts.map +0 -1
- package/out/candidate-ancestors-schema.js.map +0 -1
- package/out/candidate-fts.d.ts.map +0 -1
- package/out/candidate-fts.js.map +0 -1
- package/out/candidate-importance.d.ts.map +0 -1
- package/out/candidate-importance.js.map +0 -1
- package/out/candidate-lookup.d.ts.map +0 -1
- package/out/candidate-lookup.js.map +0 -1
- package/out/candidate-schema.d.ts.map +0 -1
- package/out/candidate-schema.js.map +0 -1
- package/out/candidate-scoring.d.ts.map +0 -1
- package/out/candidate-scoring.js.map +0 -1
- package/out/postal/city-alias-lookup.d.ts.map +0 -1
- package/out/postal/city-alias-lookup.js.map +0 -1
- package/out/postal/city-alias-schema.d.ts.map +0 -1
- package/out/postal/city-alias-schema.js.map +0 -1
- package/out/postal/city-candidate-schema.d.ts.map +0 -1
- package/out/postal/city-candidate-schema.js.map +0 -1
- package/out/sqlite-convention-source.d.ts.map +0 -1
- package/out/sqlite-convention-source.js.map +0 -1
- package/out/sqlite-utils.d.ts.map +0 -1
- package/out/sqlite-utils.js.map +0 -1
- package/out/street/centroid-schema.d.ts.map +0 -1
- package/out/street/centroid-schema.js.map +0 -1
- package/out/street/centroid.d.ts.map +0 -1
- package/out/street/centroid.js.map +0 -1
- package/out/street/morphology-fst-builder.d.ts.map +0 -1
- package/out/street/morphology-fst-builder.js.map +0 -1
- package/out/street/morphology-fst-loader.d.ts.map +0 -1
- package/out/street/morphology-fst-loader.js.map +0 -1
- /package/lib/address/{point-schema.ts → point/schema.ts} +0 -0
- /package/lib/{build-slim.ts → build/slim.ts} +0 -0
- /package/lib/{candidate-fts.ts → candidate/fts.ts} +0 -0
- /package/lib/postal/{city-alias-schema.ts → city/alias/schema.ts} +0 -0
- /package/lib/postal/{city-candidate-schema.ts → city/candidate-schema.ts} +0 -0
- /package/lib/{sqlite-convention-source.ts → sqlite/convention-source.ts} +0 -0
- /package/lib/street/{centroid-schema.ts → centroid/schema.ts} +0 -0
|
@@ -8,17 +8,51 @@
|
|
|
8
8
|
import { DatabaseClient } from "@mailwoman/sqlite/client"
|
|
9
9
|
import { tableExists } from "@mailwoman/sqlite/introspection"
|
|
10
10
|
|
|
11
|
-
import
|
|
11
|
+
import { CANDIDATE_ANCESTOR_COLUMNS, CANDIDATE_ANCESTOR_TABLE } from "#candidate/ancestors/schema"
|
|
12
12
|
import type { PlaceAttrs, StageRow } from "#candidate/place-attrs"
|
|
13
|
+
import type { CandidateDatabase } from "#candidate/schema"
|
|
13
14
|
import type { WOFDatabase } from "#schema"
|
|
14
15
|
import { normalizeLocalityForKey } from "#street/normalize"
|
|
15
16
|
|
|
17
|
+
/**
|
|
18
|
+
* The extract's own region-tier ancestry, when it carries any: extract id → the admin region's id. A locality extract
|
|
19
|
+
* derived from a national register (the Taiwanese 鄉鎮市區 from the civil-affairs address points) knows which 縣市 each row
|
|
20
|
+
* belongs to, and writes that as an `ancestors` row against the admin region's WOF id. Without it a folded row has no
|
|
21
|
+
* region scope, so a `parentID`-scoped probe misses it and the cascade widens to whatever namesake carries the key.
|
|
22
|
+
*
|
|
23
|
+
* Only a region the admin build staged (`attrs`) counts: an id the admin does not know cannot be denormalized into a
|
|
24
|
+
* closure row, and a scope nothing can match would be a silent zero rather than an absence.
|
|
25
|
+
*/
|
|
26
|
+
function extractRegionAncestry(pc: DatabaseClient<WOFDatabase>, attrs: Map<number, PlaceAttrs>): Map<number, number> {
|
|
27
|
+
const regionOf = new Map<number, number>()
|
|
28
|
+
|
|
29
|
+
if (!tableExists(pc, "ancestors")) {
|
|
30
|
+
return regionOf
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
for (const r of pc
|
|
34
|
+
.prepare(
|
|
35
|
+
"SELECT id, MIN(ancestor_id) AS ancestor_id FROM ancestors WHERE ancestor_placetype = 'region' GROUP BY id"
|
|
36
|
+
)
|
|
37
|
+
.iterate()) {
|
|
38
|
+
const rid = Number(r.ancestor_id)
|
|
39
|
+
|
|
40
|
+
if (attrs.has(rid)) {
|
|
41
|
+
regionOf.set(Number(r.id), rid)
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return regionOf
|
|
46
|
+
}
|
|
47
|
+
|
|
16
48
|
/**
|
|
17
49
|
* Fold ONE extract (`spr` rows at `extractPlacetype` carrying real coordinates) in, then pass 4b: the alias names
|
|
18
50
|
* hanging off that same extract's `names` table.
|
|
19
51
|
*
|
|
20
52
|
* Self-contained by construction — it shares only the staging statement and the code dictionaries with the admin passes
|
|
21
|
-
* above it, and nothing downstream reads anything it produces except the
|
|
53
|
+
* above it, and nothing downstream reads anything it produces except the counters it returns. The one thing it writes
|
|
54
|
+
* beyond the staging table is a depth-1 closure row for a row whose extract names its region (see
|
|
55
|
+
* {@link extractRegionAncestry}), so the pick's stamped ancestors agree with the scope it was found under.
|
|
22
56
|
*/
|
|
23
57
|
export function foldExtract(ctx: {
|
|
24
58
|
/**
|
|
@@ -30,8 +64,13 @@ export function foldExtract(ctx: {
|
|
|
30
64
|
ccID: (code: string | null) => number
|
|
31
65
|
ptID: (pt: string | null) => number
|
|
32
66
|
stageRow: StageRow
|
|
67
|
+
/**
|
|
68
|
+
* The admin places pass 1 staged — what an extract's region ancestry is resolved against. Absent, no extract row
|
|
69
|
+
* takes a region scope (the postcode extracts, which carry none).
|
|
70
|
+
*/
|
|
71
|
+
attrs?: Map<number, PlaceAttrs>
|
|
33
72
|
progress: (phase: string, message: string) => void
|
|
34
|
-
}): { primaries: number; aliases: number } {
|
|
73
|
+
}): { primaries: number; aliases: number; scoped: number; ancestorRows: number } {
|
|
35
74
|
const { out, extractPath, extractPlacetype, ccID, ptID, stageRow, progress } = ctx
|
|
36
75
|
|
|
37
76
|
progress(extractPlacetype === "postalcode" ? "postcodes" : "localities", `reading ${extractPath}`)
|
|
@@ -41,8 +80,24 @@ export function foldExtract(ctx: {
|
|
|
41
80
|
// Per-extract, not the admin `attrs` map: pass 1 only ever sees the admin DB, so the alias pass
|
|
42
81
|
// below has nothing to join against unless this primary loop records what it staged.
|
|
43
82
|
const pcAttrs = new Map<number, PlaceAttrs>()
|
|
83
|
+
const regionOf = ctx.attrs ? extractRegionAncestry(pc, ctx.attrs) : new Map<number, number>()
|
|
84
|
+
|
|
85
|
+
const insAncestor = out.prepare(
|
|
86
|
+
`INSERT OR IGNORE INTO ${CANDIDATE_ANCESTOR_TABLE} VALUES (${CANDIDATE_ANCESTOR_COLUMNS.map(() => "?").join(", ")})`
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
// The region's own closure rows, as the sidecar pass wrote them. A scoped row inherits the whole chain above its
|
|
90
|
+
// region — macroregion, country — not the region alone: the widened-scope refusal reads a pick's stamped ancestors
|
|
91
|
+
// against the parent the walk resolved, and a 縣市 that resolves to its macroregion record (`桃園市`, which WOF names
|
|
92
|
+
// only at that tier) would otherwise contradict a lineage that stops at the region.
|
|
93
|
+
const regionChain = out.prepare(
|
|
94
|
+
`SELECT depth, parent_spr_id, parent_placetype_id, parent_name, parent_name_key FROM ${CANDIDATE_ANCESTOR_TABLE} WHERE spr_id = ? ORDER BY depth`
|
|
95
|
+
)
|
|
96
|
+
|
|
44
97
|
let primaries = 0
|
|
45
98
|
let aliases = 0
|
|
99
|
+
let scoped = 0
|
|
100
|
+
let ancestorRows = 0
|
|
46
101
|
|
|
47
102
|
out.exec("BEGIN")
|
|
48
103
|
|
|
@@ -60,12 +115,15 @@ export function foldExtract(ctx: {
|
|
|
60
115
|
|
|
61
116
|
const lat = r.latitude as number
|
|
62
117
|
const lon = r.longitude as number
|
|
118
|
+
const id = Number(r.id)
|
|
119
|
+
const rid = regionOf.get(id) ?? 0
|
|
63
120
|
|
|
64
|
-
// region_id 0 (a postcode is unique by
|
|
65
|
-
// (no population). bbox = the
|
|
121
|
+
// region_id from the extract's own ancestry when it names one, else 0 (a postcode is unique by
|
|
122
|
+
// name+country — no same-name disambiguation); neg_rank 0 (no population). bbox = the row's own
|
|
123
|
+
// min/max (falls back to the centroid point).
|
|
66
124
|
const a: PlaceAttrs = {
|
|
67
125
|
cid: ccID(r.country as string | null),
|
|
68
|
-
rid
|
|
126
|
+
rid,
|
|
69
127
|
ptid: pcPtid,
|
|
70
128
|
name,
|
|
71
129
|
lat,
|
|
@@ -83,15 +141,38 @@ export function foldExtract(ctx: {
|
|
|
83
141
|
imp: null,
|
|
84
142
|
}
|
|
85
143
|
|
|
86
|
-
pcAttrs.set(
|
|
87
|
-
stageRow(key, a,
|
|
144
|
+
pcAttrs.set(id, a)
|
|
145
|
+
stageRow(key, a, id, 1)
|
|
146
|
+
|
|
147
|
+
if (rid !== 0) {
|
|
148
|
+
const region = ctx.attrs!.get(rid)!
|
|
149
|
+
|
|
150
|
+
insAncestor.run(id, 1, rid, region.ptid, region.name, region.pkey)
|
|
151
|
+
|
|
152
|
+
scoped++
|
|
153
|
+
|
|
154
|
+
ancestorRows++
|
|
155
|
+
|
|
156
|
+
for (const link of regionChain.iterate(rid)) {
|
|
157
|
+
insAncestor.run(
|
|
158
|
+
id,
|
|
159
|
+
Number(link.depth) + 1,
|
|
160
|
+
Number(link.parent_spr_id),
|
|
161
|
+
Number(link.parent_placetype_id),
|
|
162
|
+
String(link.parent_name ?? ""),
|
|
163
|
+
String(link.parent_name_key ?? "")
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
ancestorRows++
|
|
167
|
+
}
|
|
168
|
+
}
|
|
88
169
|
|
|
89
170
|
primaries++
|
|
90
171
|
}
|
|
91
172
|
|
|
92
173
|
out.exec("COMMIT")
|
|
93
174
|
|
|
94
|
-
//
|
|
175
|
+
// Fold postcode aliases into the staged candidate names.
|
|
95
176
|
//
|
|
96
177
|
// The delivery-city names GeoNames supplies for a ZIP ("Brooklyn" for 11201) are written into
|
|
97
178
|
// the extract's `names` table by `postcode/centroid-fills.ts`'s `geonamesNameFill`. Everything
|
|
@@ -134,5 +215,5 @@ export function foldExtract(ctx: {
|
|
|
134
215
|
progress("postcode-aliases", `${extractPath} has no \`names\` table — no delivery-city aliases to fold`)
|
|
135
216
|
}
|
|
136
217
|
|
|
137
|
-
return { primaries, aliases }
|
|
218
|
+
return { primaries, aliases, scoped, ancestorRows }
|
|
138
219
|
}
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
import { haversineKm } from "@mailwoman/spatial"
|
|
45
45
|
import { DatabaseClient } from "@mailwoman/sqlite/client"
|
|
46
46
|
|
|
47
|
-
import type { CandidateDatabase } from "#candidate
|
|
47
|
+
import type { CandidateDatabase } from "#candidate/schema"
|
|
48
48
|
import { normalizeLocalityForKey } from "#street/normalize"
|
|
49
49
|
|
|
50
50
|
/**
|
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|
* @author Teffen Ellis, et al.
|
|
5
5
|
*
|
|
6
6
|
* Node-side {@link PlaceLookup} over the byte-range CANDIDATE table (`build-candidate.ts`) — the
|
|
7
|
-
* SAME gazetteer the browser
|
|
8
|
-
* `
|
|
9
|
-
* instead of sql.js-httpvfs. This is what makes the server/CLI resolver match the
|
|
10
|
-
* lookup surface, one artifact, one ranking.
|
|
7
|
+
* SAME gazetteer the browser runtime resolves against ({@link WOFCandidateTableLookup} in
|
|
8
|
+
* `packages/resolver-wof-wasm/lib/httpvfs/resolver.ts`), but reading a LOCAL `candidate.db` via
|
|
9
|
+
* `node:sqlite` instead of sql.js-httpvfs. This is what makes the server/CLI resolver match the
|
|
10
|
+
* browser: one lookup surface, one artifact, one ranking.
|
|
11
11
|
*
|
|
12
12
|
* The query is a single contiguous probe on the `WITHOUT ROWID` B-tree keyed `(name_key,
|
|
13
13
|
* country_id, region_id, placetype_id, neg_rank, spr_id)`. `name_key` is the SHARED
|
|
@@ -23,13 +23,11 @@
|
|
|
23
23
|
* `bbox` field on {@link FindPlaceQuery}).
|
|
24
24
|
*/
|
|
25
25
|
|
|
26
|
+
import { expandPlacetypeFilter } from "@mailwoman/codex/placetype-map"
|
|
27
|
+
import { type Ancestor, type GazetteerArtifactCoverage, referentialFromPopulation } from "@mailwoman/core/resolver"
|
|
28
|
+
import { allRows } from "@mailwoman/core/utils"
|
|
26
29
|
import { jaroWinkler, levenshteinSimilarity } from "@mailwoman/match/comparators"
|
|
27
|
-
import {
|
|
28
|
-
expandPlacetypeFilter,
|
|
29
|
-
partitionByContainment,
|
|
30
|
-
type Ancestor,
|
|
31
|
-
type GazetteerArtifactCoverage,
|
|
32
|
-
} from "@mailwoman/resolver"
|
|
30
|
+
import { partitionByContainment } from "@mailwoman/resolver"
|
|
33
31
|
import { haversineKm } from "@mailwoman/spatial"
|
|
34
32
|
import { DatabaseClient } from "@mailwoman/sqlite/client"
|
|
35
33
|
import type { PathBuilderLike } from "path-ts"
|
|
@@ -40,16 +38,15 @@ import {
|
|
|
40
38
|
intervalContains,
|
|
41
39
|
type CandidateAncestorTable,
|
|
42
40
|
type IntervalLabel,
|
|
43
|
-
} from "#candidate
|
|
44
|
-
import { CANDIDATE_FTS_TABLE } from "#candidate
|
|
45
|
-
import type { CandidateDatabase, CandidateTable, CountryCodeTable, PlacetypeCodeTable } from "#candidate
|
|
41
|
+
} from "#candidate/ancestors/schema"
|
|
42
|
+
import { CANDIDATE_FTS_TABLE } from "#candidate/fts"
|
|
43
|
+
import type { CandidateDatabase, CandidateTable, CountryCodeTable, PlacetypeCodeTable } from "#candidate/schema"
|
|
46
44
|
import { readGazetteerCoverageManifest } from "#coverage-manifest-schema"
|
|
47
|
-
import {
|
|
48
|
-
import { POSTAL_CITY_CANDIDATE_TABLE, type PostalCityCandidateTable } from "#postal/city-candidate-schema"
|
|
45
|
+
import { POSTAL_CITY_CANDIDATE_TABLE, type PostalCityCandidateTable } from "#postal/city/candidate-schema"
|
|
49
46
|
import { rankByPrimaryPreference, type RankedRow, RERANK_FETCH } from "#primary-preference"
|
|
50
47
|
import { applyProximityRerank } from "#proximity-rerank"
|
|
51
48
|
import { REGION_CLASS_PLACETYPES, regionQualifierProbeKeys } from "#region-keys"
|
|
52
|
-
import {
|
|
49
|
+
import { hasColumn, hasTable } from "#sqlite-utils"
|
|
53
50
|
import { type NameKey, normalizeLocalityForKey, stripLocalityQualifier } from "#street/normalize"
|
|
54
51
|
import type { FindPlaceQuery, PlaceCandidate, PlaceLookup, WOFPlacetype } from "#types"
|
|
55
52
|
|
|
@@ -9,9 +9,9 @@ import { isOfficialLanguage } from "@mailwoman/codex/country"
|
|
|
9
9
|
import type { DatabaseClient } from "@mailwoman/sqlite/client"
|
|
10
10
|
import { tableExists } from "@mailwoman/sqlite/introspection"
|
|
11
11
|
|
|
12
|
-
import type { CandidateDatabase } from "#candidate-schema"
|
|
13
12
|
import { isOwnNameVariant } from "#candidate/own-name"
|
|
14
13
|
import type { PlaceAttrs } from "#candidate/place-attrs"
|
|
14
|
+
import type { CandidateDatabase } from "#candidate/schema"
|
|
15
15
|
import type { WOFDatabase } from "#schema"
|
|
16
16
|
import { normalizeLocalityForKey } from "#street/normalize"
|
|
17
17
|
|
|
@@ -141,7 +141,7 @@ export function stampNameRoles(ctx: {
|
|
|
141
141
|
.run().changes
|
|
142
142
|
)
|
|
143
143
|
|
|
144
|
-
//
|
|
144
|
+
// Detect aliases that are the holder's own primary name in another variant.
|
|
145
145
|
// orthography — romanization, spacing/diacritic variant, or abbreviation expansion. The verdict
|
|
146
146
|
// is per (alias key, primary key) pair, so it runs in JS over the still-unstamped alias rows;
|
|
147
147
|
// an uncovered script answers no-verdict and stamps nothing (own-name.ts owns the predicate and
|
|
@@ -127,6 +127,7 @@ export function romanizeNameKey(key: string): string | null {
|
|
|
127
127
|
* Edit similarity between a holder's primary name key and one of its alias keys, both romanized and
|
|
128
128
|
* abbreviation-expanded. `null` when either side's script is uncovered.
|
|
129
129
|
*/
|
|
130
|
+
// repo-health-ignore export-name-affix -- romanizes and expands abbreviations first; `nameSimilarity` compares as given.
|
|
130
131
|
export function ownNameSimilarity(primaryKey: string, aliasKey: string): number | null {
|
|
131
132
|
const primary = romanizeNameKey(primaryKey)
|
|
132
133
|
const alias = romanizeNameKey(aliasKey)
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*
|
|
6
6
|
* Typed schema for the byte-range CANDIDATE gazetteer (`candidate.db`) — the single source of truth
|
|
7
7
|
* for the columns shared by the BUILDER ({@link buildCandidateTable}) and the READERS (the Node
|
|
8
|
-
* {@link WOFCandidateTableLookup} + the browser
|
|
8
|
+
* {@link WOFCandidateTableLookup} + the browser `@mailwoman/resolver-wof-wasm/httpvfs/resolver`). Before this module each
|
|
9
9
|
* side hand-wrote the column list; a rename in one place broke the other at runtime. Now the
|
|
10
10
|
* contract is a Kysely `Database` interface (`new DatabaseClient<CandidateDatabase>(...)` for
|
|
11
11
|
* typed inserts) plus the table DDL as strings — so a column change is a compile error on every
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
|
|
18
18
|
import { sql, type Kysely } from "kysely"
|
|
19
19
|
|
|
20
|
-
import type { CandidateAncestorsDatabase } from "#candidate
|
|
20
|
+
import type { CandidateAncestorsDatabase } from "#candidate/ancestors/schema"
|
|
21
21
|
import type { CapitalTable } from "#capital-schema"
|
|
22
22
|
import type { NameKey } from "#street/normalize"
|
|
23
23
|
|
|
@@ -7,12 +7,12 @@
|
|
|
7
7
|
* ordered — the weighted-sum score and the exact-match tiering that ranks over it.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
+
import { compareReferential, referentialFromPopulation } from "@mailwoman/core/resolver"
|
|
10
11
|
import { haversineKm } from "@mailwoman/spatial"
|
|
11
12
|
import type { DatabaseClient } from "@mailwoman/sqlite/client"
|
|
12
13
|
|
|
13
14
|
import { exactMatchIDs, officialNameIDs } from "#exact-match"
|
|
14
15
|
import { foldQueryText } from "#fts/index"
|
|
15
|
-
import { compareReferential, referentialFromPopulation } from "#place-importance-schema"
|
|
16
16
|
import { populationBoostTerm, type RankingWeights } from "#ranking-weights"
|
|
17
17
|
import type { RawSearchRow } from "#search-fetch"
|
|
18
18
|
import type { FindPlaceQuery, PlaceCandidate, WOFPlacetype } from "#types"
|
package/lib/coincident-roles.ts
CHANGED
|
@@ -38,14 +38,13 @@
|
|
|
38
38
|
* be wired as a post-step of the main `scripts/build-unified-wof.ts`.
|
|
39
39
|
*/
|
|
40
40
|
|
|
41
|
-
import type { CoincidentLocality } from "@mailwoman/resolver"
|
|
41
|
+
import type { CoincidentLocality } from "@mailwoman/core/resolver"
|
|
42
|
+
import { allRows } from "@mailwoman/core/utils"
|
|
42
43
|
import { haversineKm } from "@mailwoman/spatial"
|
|
43
44
|
import type { DatabaseClient } from "@mailwoman/sqlite/client"
|
|
44
45
|
import { tableExists } from "@mailwoman/sqlite/introspection"
|
|
45
46
|
|
|
46
47
|
import type { WOFDatabase } from "#schema"
|
|
47
|
-
import { allRows } from "#sqlite-utils"
|
|
48
|
-
|
|
49
48
|
/**
|
|
50
49
|
* Table of places that hold more than one admin role — a locality that is also its county seat. Written by the
|
|
51
50
|
* gazetteer build, read by the resolver when a coincident locality has to be chosen.
|
|
@@ -29,10 +29,11 @@ import {
|
|
|
29
29
|
type CountryCoverageFact,
|
|
30
30
|
type GazetteerArtifactCoverage,
|
|
31
31
|
} from "@mailwoman/core/resolver"
|
|
32
|
+
import { allRows } from "@mailwoman/core/utils"
|
|
32
33
|
import type { DatabaseClient } from "@mailwoman/sqlite/client"
|
|
33
34
|
import { sql, type Kysely } from "kysely"
|
|
34
35
|
|
|
35
|
-
import {
|
|
36
|
+
import { hasTable } from "#sqlite-utils"
|
|
36
37
|
|
|
37
38
|
/**
|
|
38
39
|
* One country's hard-filter coverage measurement — the storage form of {@link CountryCoverageFact}.
|
package/lib/currency-backfill.ts
CHANGED
|
@@ -16,9 +16,9 @@ import type { DatabaseClient } from "@mailwoman/sqlite/client"
|
|
|
16
16
|
import { resolvePath } from "path-ts"
|
|
17
17
|
import { TSVSpliterator } from "spliterator"
|
|
18
18
|
|
|
19
|
-
import type { loadImportanceIndex } from "#candidate
|
|
20
|
-
import type { CandidateDatabase } from "#candidate-schema"
|
|
19
|
+
import type { loadImportanceIndex } from "#candidate/importance"
|
|
21
20
|
import type { PlaceAttrs, StageRow } from "#candidate/place-attrs"
|
|
21
|
+
import type { CandidateDatabase } from "#candidate/schema"
|
|
22
22
|
import type { WOFDatabase } from "#schema"
|
|
23
23
|
import { normalizeLocalityForKey } from "#street/normalize"
|
|
24
24
|
|
package/lib/fst/builder.ts
CHANGED
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
* PlaceEntry at terminals → return FSTMatcher.
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
+
import { allRows, getRow } from "@mailwoman/core/utils"
|
|
14
15
|
import { DatabaseClient } from "@mailwoman/sqlite/client"
|
|
15
16
|
import { resolvePath } from "path-ts"
|
|
16
17
|
|
|
@@ -20,7 +21,6 @@ import { FSTMatcher, normalizeTokens } from "#fst/matcher"
|
|
|
20
21
|
import type { BuildFSTOpts, BuildFSTResult, FSTProvenance, PlaceEntry, PlacetypeID } from "#fst/types"
|
|
21
22
|
import { loadImportanceSplit } from "#place-importance-schema"
|
|
22
23
|
import type { WOFDatabase } from "#schema"
|
|
23
|
-
import { allRows, getRow } from "#sqlite-utils"
|
|
24
24
|
|
|
25
25
|
const DEFAULT_PLACETYPES: PlacetypeID[] = [
|
|
26
26
|
"country",
|
|
@@ -67,7 +67,7 @@ export function deserializeFSTWeb(input: ArrayBuffer | Uint8Array): FSTMatcher {
|
|
|
67
67
|
|
|
68
68
|
let pos = HEADER_SIZE
|
|
69
69
|
|
|
70
|
-
//
|
|
70
|
+
// Decode the interned string table.
|
|
71
71
|
const strOffsets = new Uint32Array(stringCount + 1)
|
|
72
72
|
|
|
73
73
|
for (let i = 0; i <= stringCount; i++) {
|
|
@@ -86,7 +86,7 @@ export function deserializeFSTWeb(input: ArrayBuffer | Uint8Array): FSTMatcher {
|
|
|
86
86
|
|
|
87
87
|
pos += stringBytes
|
|
88
88
|
|
|
89
|
-
//
|
|
89
|
+
// Decode the state table and its transitions.
|
|
90
90
|
const stateEntrySize = version >= VERSION_WIDE_STATE_COUNTERS ? WIDE_STATE_ENTRY_SIZE : NARROW_STATE_ENTRY_SIZE
|
|
91
91
|
// v5 grew the place entry by the encyclopedic float; v4-and-below files are read at the old stride.
|
|
92
92
|
const placeEntrySize = isSplit ? SPLIT_PLACE_ENTRY_SIZE : LEGACY_PLACE_ENTRY_SIZE
|
package/lib/fst/freshness.ts
CHANGED
|
@@ -146,7 +146,6 @@ export async function peekFSTStampFields(path: string): Promise<FSTStampFields |
|
|
|
146
146
|
* Re-exported from `@mailwoman/core/hash`. This package's own test imports it from here, and the sidecar convention
|
|
147
147
|
* below is what it is for.
|
|
148
148
|
*/
|
|
149
|
-
export { md5File } from "@mailwoman/core/hash"
|
|
150
149
|
|
|
151
150
|
/**
|
|
152
151
|
* The source identity an FST build should stamp, or a check should compare against.
|
package/lib/fst/serialize.ts
CHANGED
|
@@ -77,7 +77,7 @@ for (let i = 0; i < PLACETYPE_ORDER.length; i++) {
|
|
|
77
77
|
export function serializeFST(matcher: FSTMatcher, provenance?: FSTProvenance): Buffer {
|
|
78
78
|
const nodes = matcher.toNodes() as FSTNode[]
|
|
79
79
|
|
|
80
|
-
//
|
|
80
|
+
// Intern state-edge strings before serializing their table.
|
|
81
81
|
const stringMap = new Map<string, number>()
|
|
82
82
|
const strings: string[] = []
|
|
83
83
|
|
|
@@ -106,7 +106,7 @@ export function serializeFST(matcher: FSTMatcher, provenance?: FSTProvenance): B
|
|
|
106
106
|
const encodedStrings = strings.map((s) => Buffer.from(s, "utf8"))
|
|
107
107
|
const stringBytes = encodedStrings.reduce((sum, b) => sum + b.length, 0)
|
|
108
108
|
|
|
109
|
-
//
|
|
109
|
+
// Count each serialized record class.
|
|
110
110
|
let totalEdges = 0
|
|
111
111
|
let totalPlaces = 0
|
|
112
112
|
|
|
@@ -115,7 +115,7 @@ export function serializeFST(matcher: FSTMatcher, provenance?: FSTProvenance): B
|
|
|
115
115
|
totalPlaces += node.places.length
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
-
//
|
|
118
|
+
// Allocate the complete binary buffer.
|
|
119
119
|
const stringTableSize = (strings.length + 1) * 4 + stringBytes
|
|
120
120
|
const stateTableSize = nodes.length * WIDE_STATE_ENTRY_SIZE
|
|
121
121
|
const edgeTableSize = totalEdges * EDGE_ENTRY_SIZE
|
|
@@ -127,7 +127,7 @@ export function serializeFST(matcher: FSTMatcher, provenance?: FSTProvenance): B
|
|
|
127
127
|
const buf = Buffer.alloc(totalSize)
|
|
128
128
|
let pos = 0
|
|
129
129
|
|
|
130
|
-
//
|
|
130
|
+
// Read the versioned binary header.
|
|
131
131
|
// flags bit0 (survey #4, 2026-07-27): place rows carry surface-ambiguity data in the former _pad
|
|
132
132
|
// byte (pp+6 = crossCountryBranches u8, pp+7 reserved). Presence-signaled here so VERSION stays
|
|
133
133
|
// put: pre-ambiguity artifacts read flags=0 → readers expose `undefined`, never a fake 0.
|
|
@@ -151,7 +151,7 @@ export function serializeFST(matcher: FSTMatcher, provenance?: FSTProvenance): B
|
|
|
151
151
|
buf.writeUInt32LE(provenanceJson ? binarySize : 0, pos)
|
|
152
152
|
pos += 4
|
|
153
153
|
|
|
154
|
-
//
|
|
154
|
+
// Read the interned string table.
|
|
155
155
|
let strOffset = 0
|
|
156
156
|
|
|
157
157
|
for (const encoded of encodedStrings) {
|
|
@@ -170,7 +170,7 @@ export function serializeFST(matcher: FSTMatcher, provenance?: FSTProvenance): B
|
|
|
170
170
|
pos += encoded.length
|
|
171
171
|
}
|
|
172
172
|
|
|
173
|
-
//
|
|
173
|
+
// Read state, edge, and place tables.
|
|
174
174
|
const stateTableStart = pos
|
|
175
175
|
const edgeTableStart = stateTableStart + stateTableSize
|
|
176
176
|
const placeTableStart = edgeTableStart + edgeTableSize
|
|
@@ -234,7 +234,7 @@ export function serializeFST(matcher: FSTMatcher, provenance?: FSTProvenance): B
|
|
|
234
234
|
}
|
|
235
235
|
|
|
236
236
|
export function deserializeFST(buf: Buffer): FSTMatcher {
|
|
237
|
-
//
|
|
237
|
+
// Verify the versioned binary header.
|
|
238
238
|
if (buf.length < HEADER_SIZE) throw new Error("FST buffer too small for header")
|
|
239
239
|
|
|
240
240
|
if (!buf.subarray(0, 4).equals(MAGIC)) throw new Error("FST magic mismatch")
|
|
@@ -257,7 +257,7 @@ export function deserializeFST(buf: Buffer): FSTMatcher {
|
|
|
257
257
|
|
|
258
258
|
let pos = HEADER_SIZE
|
|
259
259
|
|
|
260
|
-
//
|
|
260
|
+
// Verify string-table offsets and contents.
|
|
261
261
|
const strOffsets = new Uint32Array(stringCount + 1)
|
|
262
262
|
|
|
263
263
|
for (let i = 0; i <= stringCount; i++) {
|
|
@@ -276,7 +276,7 @@ export function deserializeFST(buf: Buffer): FSTMatcher {
|
|
|
276
276
|
|
|
277
277
|
pos += stringBytes
|
|
278
278
|
|
|
279
|
-
//
|
|
279
|
+
// Verify state-table offsets and transitions.
|
|
280
280
|
const stateEntrySize = version >= VERSION_WIDE_STATE_COUNTERS ? WIDE_STATE_ENTRY_SIZE : NARROW_STATE_ENTRY_SIZE
|
|
281
281
|
// v5 grew the place entry by the encyclopedic float; v4-and-below files are read at the old stride.
|
|
282
282
|
const placeEntrySize = version >= VERSION_TWO_SCORE_SPLIT ? SPLIT_PLACE_ENTRY_SIZE : LEGACY_PLACE_ENTRY_SIZE
|
package/lib/geonames/aliases.ts
CHANGED
|
@@ -26,19 +26,13 @@
|
|
|
26
26
|
|
|
27
27
|
import { isOfficialLanguage } from "@mailwoman/codex/country"
|
|
28
28
|
import { pathExists } from "@mailwoman/core/fs/readers"
|
|
29
|
+
import { GEONAMES_ID_BASE, GEONAMES_POSTAL_ID_BASE } from "@mailwoman/core/resolver/synthetic-id-ranges"
|
|
29
30
|
import type { DatabaseClient } from "@mailwoman/sqlite/client"
|
|
30
31
|
import { join, type PathBuilderLike } from "path-ts"
|
|
31
32
|
import { TSVSpliterator } from "spliterator"
|
|
32
33
|
|
|
33
|
-
import { GEONAMES_POSTAL_ID_BASE } from "#geonames/postal"
|
|
34
34
|
import type { WOFDatabase } from "#schema"
|
|
35
35
|
|
|
36
|
-
/**
|
|
37
|
-
* Synthetic id base for GeoNames-sourced rows (#743/#193) — above Overture's 8e12 so the three sources (WOF real ids,
|
|
38
|
-
* Overture, GeoNames) never collide in a combined DB.
|
|
39
|
-
*/
|
|
40
|
-
export const GEONAMES_ID_BASE = 9_000_000_000_000
|
|
41
|
-
|
|
42
36
|
/**
|
|
43
37
|
* The four tables the alias fold writes into the range it owns. The purge and the ingest must agree on this list — a
|
|
44
38
|
* table written but not purged is exactly the #1514 defect.
|
package/lib/geonames/postal.ts
CHANGED
|
@@ -35,6 +35,7 @@
|
|
|
35
35
|
*/
|
|
36
36
|
|
|
37
37
|
import { pathExists } from "@mailwoman/core/fs/readers"
|
|
38
|
+
import { GEONAMES_POSTAL_ID_BASE } from "@mailwoman/core/resolver/synthetic-id-ranges"
|
|
38
39
|
import type { DatabaseClient } from "@mailwoman/sqlite/client"
|
|
39
40
|
import { join, type PathBuilderLike } from "path-ts"
|
|
40
41
|
import { TSVSpliterator } from "spliterator"
|
|
@@ -47,13 +48,6 @@ import type { WOFDatabase } from "#schema"
|
|
|
47
48
|
*/
|
|
48
49
|
const GEONAMES_POSTAL_COLUMNS = 11
|
|
49
50
|
|
|
50
|
-
/**
|
|
51
|
-
* Synthetic id base for GeoNames-POSTAL rows — its own namespace above the alias fold's {@link GEONAMES_ID_BASE} (9e12)
|
|
52
|
-
* allocation so all four sources (WOF, Overture, GeoNames-alias, GeoNames-postal) coexist collision-free in a combined
|
|
53
|
-
* DB.
|
|
54
|
-
*/
|
|
55
|
-
export const GEONAMES_POSTAL_ID_BASE = 9_500_000_000_000
|
|
56
|
-
|
|
57
51
|
/**
|
|
58
52
|
* The #920 name law: reduce a postcode to the sanitized-query token shape — strip every non-letter/number — so the
|
|
59
53
|
* stored name matches what `sanitizeFTSQuery` produces from the parsed postcode token. `"110 00"` → `"11000"`,
|
package/lib/index.ts
CHANGED
|
@@ -29,25 +29,25 @@ export {
|
|
|
29
29
|
createCandidateIntervalTable,
|
|
30
30
|
intervalContains,
|
|
31
31
|
MAX_ANCESTOR_DEPTH,
|
|
32
|
-
} from "#candidate
|
|
32
|
+
} from "#candidate/ancestors/schema"
|
|
33
33
|
|
|
34
34
|
export type {
|
|
35
35
|
CandidateAncestorsDatabase,
|
|
36
36
|
CandidateAncestorTable,
|
|
37
37
|
CandidateIntervalTable,
|
|
38
38
|
IntervalLabel,
|
|
39
|
-
} from "#candidate
|
|
39
|
+
} from "#candidate/ancestors/schema"
|
|
40
40
|
|
|
41
|
-
export { CANDIDATE_FTS_TABLE, createCandidateFTS } from "#candidate
|
|
41
|
+
export { CANDIDATE_FTS_TABLE, createCandidateFTS } from "#candidate/fts"
|
|
42
42
|
|
|
43
43
|
export {
|
|
44
44
|
ImportanceIndex,
|
|
45
45
|
IMPORTANCE_JOIN_RADIUS_KM,
|
|
46
46
|
type ImportanceIndexStats,
|
|
47
47
|
loadImportanceIndex,
|
|
48
|
-
} from "#candidate
|
|
48
|
+
} from "#candidate/importance"
|
|
49
49
|
|
|
50
|
-
export { WOFCandidateTableLookup, type WOFCandidateTableLookupOpts } from "#candidate
|
|
50
|
+
export { WOFCandidateTableLookup, type WOFCandidateTableLookupOpts } from "#candidate/lookup"
|
|
51
51
|
|
|
52
52
|
export {
|
|
53
53
|
COUNTRY_BBOX_TABLE,
|
|
@@ -61,33 +61,26 @@ export {
|
|
|
61
61
|
export type { CountryBBoxTable, CountryCoverageTable, GazetteerCoverageDatabase } from "#coverage-manifest-schema"
|
|
62
62
|
export { SQLiteStreetNameLookup, type SQLiteStreetNameLookupOpts } from "#street/name-lookup"
|
|
63
63
|
|
|
64
|
-
export {
|
|
65
|
-
GEONAMES_ID_BASE,
|
|
66
|
-
type GeonamesIngestProgress,
|
|
67
|
-
ingestGeonamesAliases,
|
|
68
|
-
purgeGeonamesAliasRange,
|
|
69
|
-
} from "#geonames/aliases"
|
|
70
|
-
|
|
71
|
-
export { GEONAMES_POSTAL_ID_BASE } from "#geonames/postal"
|
|
64
|
+
export { type GeonamesIngestProgress, ingestGeonamesAliases, purgeGeonamesAliasRange } from "#geonames/aliases"
|
|
72
65
|
|
|
73
|
-
export { ADDRESS_POINT_COLUMNS, createAddressPointIndexes, createAddressPointTable } from "#address/point
|
|
74
|
-
export type { AddressPointDatabase, AddressPointTable } from "#address/point
|
|
66
|
+
export { ADDRESS_POINT_COLUMNS, createAddressPointIndexes, createAddressPointTable } from "#address/point/schema"
|
|
67
|
+
export type { AddressPointDatabase, AddressPointTable } from "#address/point/schema"
|
|
75
68
|
|
|
76
69
|
export {
|
|
77
70
|
WOFPostalCityAliasLookup,
|
|
78
71
|
type PostalCityAlias,
|
|
79
72
|
type WOFPostalCityAliasLookupOpts,
|
|
80
|
-
} from "#postal/city
|
|
73
|
+
} from "#postal/city/alias/lookup"
|
|
81
74
|
|
|
82
|
-
export type { PostalCityAliasDatabase, PostalCityAliasTable } from "#postal/city
|
|
75
|
+
export type { PostalCityAliasDatabase, PostalCityAliasTable } from "#postal/city/alias/schema"
|
|
83
76
|
|
|
84
77
|
export {
|
|
85
78
|
POSTAL_CITY_CANDIDATE_COLUMNS,
|
|
86
79
|
POSTAL_CITY_CANDIDATE_TABLE,
|
|
87
80
|
createPostalCityCandidateTable,
|
|
88
|
-
} from "#postal/city
|
|
81
|
+
} from "#postal/city/candidate-schema"
|
|
89
82
|
|
|
90
|
-
export type { PostalCityCandidateDatabase, PostalCityCandidateTable } from "#postal/city
|
|
83
|
+
export type { PostalCityCandidateDatabase, PostalCityCandidateTable } from "#postal/city/candidate-schema"
|
|
91
84
|
|
|
92
85
|
export {
|
|
93
86
|
ADDRESS_CONVENTION_TABLE,
|
|
@@ -103,7 +96,7 @@ export {
|
|
|
103
96
|
type Strategy,
|
|
104
97
|
} from "#convention/index"
|
|
105
98
|
|
|
106
|
-
export { SqliteConventionSource } from "#sqlite
|
|
99
|
+
export { SqliteConventionSource } from "#sqlite/convention-source"
|
|
107
100
|
|
|
108
101
|
export { WOFPostcodeLookup, type PostcodePlace } from "#postcode-point-lookup"
|
|
109
102
|
|
|
@@ -127,17 +120,17 @@ export {
|
|
|
127
120
|
type WOFReverseGeocoderOpts,
|
|
128
121
|
} from "#reverse"
|
|
129
122
|
|
|
130
|
-
export { AddressPointInterpolator } from "#address/point
|
|
131
|
-
export { AddressPointSqliteLookup } from "#address/point"
|
|
123
|
+
export { AddressPointInterpolator } from "#address/point/interpolation"
|
|
124
|
+
export { AddressPointSqliteLookup } from "#address/point/index"
|
|
132
125
|
|
|
133
126
|
export {
|
|
134
127
|
STREET_CENTROID_COLUMNS,
|
|
135
128
|
createStreetCentroidIndexes,
|
|
136
129
|
createStreetCentroidTable,
|
|
137
|
-
} from "#street/centroid
|
|
130
|
+
} from "#street/centroid/schema"
|
|
138
131
|
|
|
139
|
-
export type { StreetCentroidDatabase, StreetCentroidTable } from "#street/centroid
|
|
140
|
-
export { StreetCentroidSqliteLookup } from "#street/centroid"
|
|
132
|
+
export type { StreetCentroidDatabase, StreetCentroidTable } from "#street/centroid/schema"
|
|
133
|
+
export { StreetCentroidSqliteLookup } from "#street/centroid/index"
|
|
141
134
|
|
|
142
135
|
export {
|
|
143
136
|
StreetInterpolator,
|
package/lib/interpolation.ts
CHANGED
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
*/
|
|
28
28
|
|
|
29
29
|
import { parseJSONStrict } from "@mailwoman/core/json"
|
|
30
|
-
import type { InterpolationLookup } from "@mailwoman/resolver"
|
|
30
|
+
import type { InterpolationLookup } from "@mailwoman/core/resolver"
|
|
31
31
|
import { clampFraction, haversineKm, pointAlong } from "@mailwoman/spatial"
|
|
32
32
|
import { DatabaseClient } from "@mailwoman/sqlite/client"
|
|
33
33
|
|
package/lib/lookup.ts
CHANGED
|
@@ -10,13 +10,15 @@
|
|
|
10
10
|
* See `docs/plan/phases/PHASE_4_2_wof_sqlite.md` for the design rationale.
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
import { expandPlacetypeFilter
|
|
13
|
+
import { expandPlacetypeFilter } from "@mailwoman/codex/placetype-map"
|
|
14
|
+
import type { Ancestor, CoincidentLocality } from "@mailwoman/core/resolver"
|
|
15
|
+
import { allRows } from "@mailwoman/core/utils"
|
|
14
16
|
import { haversineKm } from "@mailwoman/spatial"
|
|
15
17
|
import type { SQLInputValue } from "@mailwoman/sqlite/client"
|
|
16
18
|
import { DatabaseClient } from "@mailwoman/sqlite/client"
|
|
17
19
|
|
|
18
20
|
import { ancestorLineage } from "#ancestry/index"
|
|
19
|
-
import { candidateFromSearchRow, rankCandidates } from "#candidate
|
|
21
|
+
import { candidateFromSearchRow, rankCandidates } from "#candidate/scoring"
|
|
20
22
|
import { loadCoincidentLocalities } from "#coincident-roles"
|
|
21
23
|
import {
|
|
22
24
|
ADDRESS_CONVENTION_TABLE,
|
|
@@ -46,12 +48,11 @@ import {
|
|
|
46
48
|
import { normalizePlacetypes, sanitizeFTSQuery } from "#fts/query"
|
|
47
49
|
import { cfNormalize, softNameScore } from "#name-score"
|
|
48
50
|
import { encyclopedicClauses } from "#place-importance-schema"
|
|
49
|
-
import type { WOFPostalCityAliasLookup } from "#postal/city
|
|
51
|
+
import type { WOFPostalCityAliasLookup } from "#postal/city/alias/lookup"
|
|
50
52
|
import { DEFAULT_WEIGHTS, populationScaleTerm, type RankingWeights } from "#ranking-weights"
|
|
51
53
|
import type { WOFDatabase } from "#schema"
|
|
52
54
|
import { fetchSearchRows, type RawSearchRow } from "#search-fetch"
|
|
53
|
-
import { SqliteConventionSource } from "#sqlite
|
|
54
|
-
import { allRows } from "#sqlite-utils"
|
|
55
|
+
import { SqliteConventionSource } from "#sqlite/convention-source"
|
|
55
56
|
import type { FindPlaceQuery, PlaceCandidate, PlaceLookup, WOFPlacetype } from "#types"
|
|
56
57
|
|
|
57
58
|
export interface WOFSQLitePlaceLookupOpts {
|