@mailwoman/resolver-wof-sqlite 9.4.0 → 10.1.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/README.md +8 -8
- package/lib/address/{point-interpolation.ts → point/interpolation.ts} +40 -38
- package/lib/address/point/schema.ts +132 -0
- package/lib/address/point.ts +68 -81
- package/lib/{address/index.ts → address.ts} +2 -2
- package/lib/ancestry/backfill.ts +24 -88
- package/lib/{ancestry/index.ts → ancestry.ts} +19 -15
- package/lib/build/candidate.ts +608 -0
- package/lib/build/population-corrections.ts +64 -0
- package/lib/build/slim.ts +338 -0
- package/lib/candidate/alias-bags.ts +74 -14
- package/lib/candidate/ancestors/schema.ts +211 -0
- package/lib/candidate/{ancestors-sidecar.ts → ancestors/sidecar.ts} +17 -24
- package/lib/candidate/country-display-names.ts +6 -20
- package/lib/candidate/extract-fold.ts +12 -57
- package/lib/candidate/fts.ts +28 -0
- package/lib/candidate/importance.ts +221 -0
- package/lib/candidate/lookup.ts +669 -0
- package/lib/candidate/name-roles.ts +16 -34
- package/lib/candidate/own-name.ts +34 -31
- package/lib/candidate/place-attrs.ts +20 -13
- package/lib/candidate/schema.ts +211 -0
- package/lib/candidate/scoring.ts +222 -0
- package/lib/capital-schema.ts +21 -17
- package/lib/capitals.ts +26 -24
- package/lib/coincident-roles.ts +74 -55
- package/lib/convention/schema.ts +13 -7
- package/lib/convention.ts +144 -0
- package/lib/coverage-manifest-schema.ts +34 -39
- package/lib/currency-backfill.ts +24 -69
- package/lib/env.ts +1 -1
- package/lib/exact-match.ts +31 -20
- package/lib/extracts.ts +96 -73
- package/lib/fst/autocomplete.ts +52 -35
- package/lib/fst/builder.ts +30 -36
- package/lib/fst/deserialize-web.ts +8 -9
- package/lib/fst/format.ts +41 -23
- package/lib/fst/freshness.ts +17 -95
- package/lib/fst/matcher.ts +2 -2
- package/lib/fst/serialize.ts +45 -60
- package/lib/fst/types.ts +71 -59
- package/lib/fts/query.ts +5 -36
- package/lib/fts.ts +280 -0
- package/lib/geonames/aliases.ts +72 -149
- package/lib/geonames/postal.ts +162 -77
- package/lib/index.ts +20 -27
- package/lib/interpolation.ts +43 -96
- package/lib/lookup.ts +137 -257
- package/lib/name-score.ts +9 -7
- package/lib/nsul/lookup.ts +36 -53
- package/lib/nsul/schema.ts +16 -43
- package/lib/paths.ts +159 -0
- package/lib/place-importance-schema.ts +57 -164
- package/lib/poi/lookup.ts +100 -109
- package/lib/poi/schema.ts +42 -26
- package/lib/polygon-schema.ts +10 -6
- package/lib/postal/city/alias/lookup.ts +79 -0
- package/lib/postal/{city-alias-schema.ts → city/alias/schema.ts} +14 -10
- package/lib/postal/{city-candidate-schema.ts → city/candidate-schema.ts} +26 -20
- package/lib/postal.ts +9 -0
- package/lib/postcode-point-lookup.ts +9 -19
- package/lib/primary-preference.ts +45 -125
- package/lib/proximity-rerank.ts +43 -32
- package/lib/ranking-weights.ts +98 -76
- package/lib/region-keys.ts +68 -40
- package/lib/reverse.ts +108 -72
- package/lib/schema.ts +74 -42
- package/lib/search-fetch.ts +90 -68
- package/lib/sqlite/convention-source.ts +57 -0
- package/lib/sqlite/utils.ts +79 -0
- package/lib/street/centroid/schema.ts +149 -0
- package/lib/street/centroid.ts +16 -25
- package/lib/street/{morphology-fst-builder.ts → morphology/fst/builder.ts} +56 -41
- package/lib/street/{morphology-fst-loader.ts → morphology/fst/loader.ts} +36 -29
- package/lib/street/name-lookup.ts +43 -35
- package/lib/street/normalize.ts +85 -216
- package/lib/street/segment-schema.ts +116 -43
- package/lib/{street/index.ts → street.ts} +3 -3
- package/lib/types.ts +94 -140
- package/lib/unified-schema.ts +29 -29
- package/lib/uprn/existence.ts +30 -19
- package/lib/uprn/lookup.ts +28 -67
- package/lib/uprn/schema.ts +18 -40
- package/lib/weights-overlay-linker.ts +204 -232
- package/out/address/point/interpolation.d.ts +45 -0
- package/out/address/point/interpolation.d.ts.map +1 -0
- package/out/address/{point-interpolation.js → point/interpolation.js} +38 -38
- package/out/address/point/interpolation.js.map +1 -0
- package/out/address/point/schema.d.ts +74 -0
- package/out/address/point/schema.d.ts.map +1 -0
- package/out/address/{point-schema.js → point/schema.js} +4 -20
- package/out/address/point/schema.js.map +1 -0
- package/out/address/point.d.ts +24 -21
- package/out/address/point.d.ts.map +1 -1
- package/out/address/point.js +60 -77
- package/out/address/point.js.map +1 -1
- package/out/{address/index.js → address.d.ts} +3 -3
- package/out/address.d.ts.map +1 -0
- package/out/{address/index.d.ts → address.js} +3 -3
- package/out/address.js.map +1 -0
- package/out/ancestry/backfill.d.ts +11 -64
- package/out/ancestry/backfill.d.ts.map +1 -1
- package/out/ancestry/backfill.js +18 -76
- package/out/ancestry/backfill.js.map +1 -1
- package/out/ancestry.d.ts +49 -0
- package/out/ancestry.d.ts.map +1 -0
- package/out/ancestry.js +59 -0
- package/out/ancestry.js.map +1 -0
- package/out/build/candidate.d.ts +170 -0
- package/out/build/candidate.d.ts.map +1 -0
- package/out/{build-candidate.js → build/candidate.js} +33 -102
- package/out/build/candidate.js.map +1 -0
- package/out/build/population-corrections.d.ts +52 -0
- package/out/build/population-corrections.d.ts.map +1 -0
- package/out/build/population-corrections.js +40 -0
- package/out/build/population-corrections.js.map +1 -0
- package/out/build/slim.d.ts +44 -0
- package/out/build/slim.d.ts.map +1 -0
- package/out/{build-slim.js → build/slim.js} +33 -94
- package/out/build/slim.js.map +1 -0
- package/out/candidate/alias-bags.d.ts +13 -5
- package/out/candidate/alias-bags.d.ts.map +1 -1
- package/out/candidate/alias-bags.js +55 -12
- package/out/candidate/alias-bags.js.map +1 -1
- package/out/candidate/ancestors/schema.d.ts +166 -0
- package/out/candidate/ancestors/schema.d.ts.map +1 -0
- package/out/candidate/ancestors/schema.js +131 -0
- package/out/candidate/ancestors/schema.js.map +1 -0
- package/out/candidate/ancestors/sidecar.d.ts +27 -0
- package/out/candidate/ancestors/sidecar.d.ts.map +1 -0
- package/out/candidate/{ancestors-sidecar.js → ancestors/sidecar.js} +17 -24
- package/out/candidate/ancestors/sidecar.js.map +1 -0
- package/out/candidate/country-display-names.d.ts +5 -15
- package/out/candidate/country-display-names.d.ts.map +1 -1
- package/out/candidate/country-display-names.js +6 -20
- package/out/candidate/country-display-names.js.map +1 -1
- package/out/candidate/extract-fold.d.ts +8 -16
- package/out/candidate/extract-fold.d.ts.map +1 -1
- package/out/candidate/extract-fold.js +6 -48
- package/out/candidate/extract-fold.js.map +1 -1
- package/out/candidate/fts.d.ts +19 -0
- package/out/candidate/fts.d.ts.map +1 -0
- package/out/candidate/fts.js +22 -0
- package/out/candidate/fts.js.map +1 -0
- package/out/candidate/importance.d.ts +127 -0
- package/out/candidate/importance.d.ts.map +1 -0
- package/out/candidate/importance.js +162 -0
- package/out/candidate/importance.js.map +1 -0
- package/out/candidate/lookup.d.ts +36 -0
- package/out/candidate/lookup.d.ts.map +1 -0
- package/out/candidate/lookup.js +456 -0
- package/out/candidate/lookup.js.map +1 -0
- package/out/candidate/name-roles.d.ts +10 -22
- package/out/candidate/name-roles.d.ts.map +1 -1
- package/out/candidate/name-roles.js +15 -33
- package/out/candidate/name-roles.js.map +1 -1
- package/out/candidate/own-name.d.ts +23 -25
- package/out/candidate/own-name.d.ts.map +1 -1
- package/out/candidate/own-name.js +34 -31
- package/out/candidate/own-name.js.map +1 -1
- package/out/candidate/place-attrs.d.ts +20 -13
- package/out/candidate/place-attrs.d.ts.map +1 -1
- package/out/candidate/place-attrs.js +4 -5
- package/out/candidate/place-attrs.js.map +1 -1
- package/out/candidate/schema.d.ts +120 -0
- package/out/candidate/schema.d.ts.map +1 -0
- package/out/{candidate-schema.js → candidate/schema.js} +11 -23
- package/out/candidate/schema.js.map +1 -0
- package/out/{candidate-scoring.d.ts → candidate/scoring.d.ts} +8 -9
- package/out/candidate/scoring.d.ts.map +1 -0
- package/out/candidate/scoring.js +157 -0
- package/out/candidate/scoring.js.map +1 -0
- package/out/capital-schema.d.ts +21 -17
- package/out/capital-schema.d.ts.map +1 -1
- package/out/capital-schema.js +17 -15
- package/out/capital-schema.js.map +1 -1
- package/out/capitals.d.ts +26 -24
- package/out/capitals.d.ts.map +1 -1
- package/out/capitals.js +23 -21
- package/out/capitals.js.map +1 -1
- package/out/coincident-roles.d.ts +60 -43
- package/out/coincident-roles.d.ts.map +1 -1
- package/out/coincident-roles.js +61 -50
- package/out/coincident-roles.js.map +1 -1
- package/out/convention/schema.d.ts +12 -6
- package/out/convention/schema.d.ts.map +1 -1
- package/out/convention/schema.js +6 -4
- package/out/convention/schema.js.map +1 -1
- package/out/convention.d.ts +97 -0
- package/out/convention.d.ts.map +1 -0
- package/out/convention.js +81 -0
- package/out/convention.js.map +1 -0
- package/out/coverage-manifest-schema.d.ts +33 -38
- package/out/coverage-manifest-schema.d.ts.map +1 -1
- package/out/coverage-manifest-schema.js +20 -29
- package/out/coverage-manifest-schema.js.map +1 -1
- package/out/currency-backfill.d.ts +13 -35
- package/out/currency-backfill.d.ts.map +1 -1
- package/out/currency-backfill.js +12 -39
- package/out/currency-backfill.js.map +1 -1
- package/out/env.d.ts +2 -2
- package/out/env.js +1 -1
- package/out/exact-match.d.ts +19 -10
- package/out/exact-match.d.ts.map +1 -1
- package/out/exact-match.js +31 -20
- package/out/exact-match.js.map +1 -1
- package/out/extracts.d.ts +65 -54
- package/out/extracts.d.ts.map +1 -1
- package/out/extracts.js +63 -55
- package/out/extracts.js.map +1 -1
- package/out/fst/autocomplete.d.ts +28 -20
- package/out/fst/autocomplete.d.ts.map +1 -1
- package/out/fst/autocomplete.js +38 -27
- package/out/fst/autocomplete.js.map +1 -1
- package/out/fst/builder.d.ts.map +1 -1
- package/out/fst/builder.js +30 -36
- package/out/fst/builder.js.map +1 -1
- package/out/fst/deserialize-web.d.ts +3 -3
- package/out/fst/deserialize-web.d.ts.map +1 -1
- package/out/fst/deserialize-web.js +8 -9
- package/out/fst/deserialize-web.js.map +1 -1
- package/out/fst/format.d.ts +41 -23
- package/out/fst/format.d.ts.map +1 -1
- package/out/fst/format.js +41 -23
- package/out/fst/format.js.map +1 -1
- package/out/fst/freshness.d.ts +11 -73
- package/out/fst/freshness.d.ts.map +1 -1
- package/out/fst/freshness.js +12 -76
- package/out/fst/freshness.js.map +1 -1
- package/out/fst/matcher.d.ts +1 -1
- package/out/fst/matcher.d.ts.map +1 -1
- package/out/fst/matcher.js +2 -2
- package/out/fst/matcher.js.map +1 -1
- package/out/fst/serialize.d.ts +30 -26
- package/out/fst/serialize.d.ts.map +1 -1
- package/out/fst/serialize.js +44 -58
- package/out/fst/serialize.js.map +1 -1
- package/out/fst/types.d.ts +71 -59
- package/out/fst/types.d.ts.map +1 -1
- package/out/fst/types.js +1 -4
- package/out/fst/types.js.map +1 -1
- package/out/{fst/index.js → fst.d.ts} +1 -1
- package/out/fst.d.ts.map +1 -0
- package/out/{fst/index.d.ts → fst.js} +1 -1
- package/out/fst.js.map +1 -0
- package/out/fts/query.d.ts +2 -27
- package/out/fts/query.d.ts.map +1 -1
- package/out/fts/query.js +5 -36
- package/out/fts/query.js.map +1 -1
- package/out/fts.d.ts +126 -0
- package/out/fts.d.ts.map +1 -0
- package/out/fts.js +188 -0
- package/out/fts.js.map +1 -0
- package/out/geonames/aliases.d.ts +26 -67
- package/out/geonames/aliases.d.ts.map +1 -1
- package/out/geonames/aliases.js +44 -121
- package/out/geonames/aliases.js.map +1 -1
- package/out/geonames/postal.d.ts +84 -50
- package/out/geonames/postal.d.ts.map +1 -1
- package/out/geonames/postal.js +115 -78
- package/out/geonames/postal.js.map +1 -1
- package/out/{geonames/index.js → geonames.d.ts} +1 -1
- package/out/geonames.d.ts.map +1 -0
- package/out/{geonames/index.d.ts → geonames.js} +1 -1
- package/out/geonames.js.map +1 -0
- package/out/index.d.ts +20 -21
- package/out/index.d.ts.map +1 -1
- package/out/index.js +14 -15
- package/out/index.js.map +1 -1
- package/out/interpolation.d.ts +15 -52
- package/out/interpolation.d.ts.map +1 -1
- package/out/interpolation.js +25 -68
- package/out/interpolation.js.map +1 -1
- package/out/lookup.d.ts +24 -53
- package/out/lookup.d.ts.map +1 -1
- package/out/lookup.js +117 -223
- package/out/lookup.js.map +1 -1
- package/out/name-score.d.ts +2 -2
- package/out/name-score.d.ts.map +1 -1
- package/out/name-score.js +9 -7
- package/out/name-score.js.map +1 -1
- package/out/nsul/lookup.d.ts +33 -34
- package/out/nsul/lookup.d.ts.map +1 -1
- package/out/nsul/lookup.js +26 -40
- package/out/nsul/lookup.js.map +1 -1
- package/out/nsul/schema.d.ts +16 -43
- package/out/nsul/schema.d.ts.map +1 -1
- package/out/nsul/schema.js +9 -35
- package/out/nsul/schema.js.map +1 -1
- package/out/{nsul/index.d.ts → nsul.d.ts} +1 -1
- package/out/nsul.d.ts.map +1 -0
- package/out/{nsul/index.js → nsul.js} +1 -1
- package/out/nsul.js.map +1 -0
- package/out/paths.d.ts +124 -0
- package/out/paths.d.ts.map +1 -0
- package/out/paths.js +104 -0
- package/out/paths.js.map +1 -0
- package/out/place-importance-schema.d.ts +49 -141
- package/out/place-importance-schema.d.ts.map +1 -1
- package/out/place-importance-schema.js +25 -140
- package/out/place-importance-schema.js.map +1 -1
- package/out/poi/lookup.d.ts +38 -42
- package/out/poi/lookup.d.ts.map +1 -1
- package/out/poi/lookup.js +67 -90
- package/out/poi/lookup.js.map +1 -1
- package/out/poi/schema.d.ts +41 -25
- package/out/poi/schema.d.ts.map +1 -1
- package/out/poi/schema.js +25 -17
- package/out/poi/schema.js.map +1 -1
- package/out/{poi/index.js → poi.d.ts} +1 -1
- package/out/poi.d.ts.map +1 -0
- package/out/{poi/index.d.ts → poi.js} +1 -1
- package/out/poi.js.map +1 -0
- package/out/polygon-schema.d.ts +10 -6
- package/out/polygon-schema.d.ts.map +1 -1
- package/out/polygon-schema.js +2 -2
- package/out/polygon-schema.js.map +1 -1
- package/out/postal/{city-alias-lookup.d.ts → city/alias/lookup.d.ts} +22 -25
- package/out/postal/city/alias/lookup.d.ts.map +1 -0
- package/out/postal/city/alias/lookup.js +49 -0
- package/out/postal/city/alias/lookup.js.map +1 -0
- package/out/postal/{city-alias-schema.d.ts → city/alias/schema.d.ts} +15 -11
- package/out/postal/city/alias/schema.d.ts.map +1 -0
- package/out/postal/{city-alias-schema.js → city/alias/schema.js} +11 -9
- package/out/postal/city/alias/schema.js.map +1 -0
- package/out/postal/city/candidate-schema.d.ts +72 -0
- package/out/postal/city/candidate-schema.d.ts.map +1 -0
- package/out/postal/city/candidate-schema.js +60 -0
- package/out/postal/city/candidate-schema.js.map +1 -0
- package/out/postal.d.ts +9 -0
- package/out/postal.d.ts.map +1 -0
- package/out/postal.js +9 -0
- package/out/postal.js.map +1 -0
- package/out/postcode-point-lookup.d.ts +9 -19
- package/out/postcode-point-lookup.d.ts.map +1 -1
- package/out/postcode-point-lookup.js +5 -17
- package/out/postcode-point-lookup.js.map +1 -1
- package/out/primary-preference.d.ts +39 -93
- package/out/primary-preference.d.ts.map +1 -1
- package/out/primary-preference.js +13 -97
- package/out/primary-preference.js.map +1 -1
- package/out/proximity-rerank.d.ts +40 -29
- package/out/proximity-rerank.d.ts.map +1 -1
- package/out/proximity-rerank.js +38 -29
- package/out/proximity-rerank.js.map +1 -1
- package/out/ranking-weights.d.ts +91 -62
- package/out/ranking-weights.d.ts.map +1 -1
- package/out/ranking-weights.js +26 -25
- package/out/ranking-weights.js.map +1 -1
- package/out/region-keys.d.ts +48 -29
- package/out/region-keys.d.ts.map +1 -1
- package/out/region-keys.js +68 -40
- package/out/region-keys.js.map +1 -1
- package/out/reverse.d.ts +75 -50
- package/out/reverse.d.ts.map +1 -1
- package/out/reverse.js +61 -49
- package/out/reverse.js.map +1 -1
- package/out/schema.d.ts +74 -42
- package/out/schema.d.ts.map +1 -1
- package/out/schema.js +4 -5
- package/out/schema.js.map +1 -1
- package/out/search-fetch.d.ts +16 -12
- package/out/search-fetch.d.ts.map +1 -1
- package/out/search-fetch.js +83 -63
- package/out/search-fetch.js.map +1 -1
- package/out/sqlite/convention-source.d.ts +25 -0
- package/out/sqlite/convention-source.d.ts.map +1 -0
- package/out/sqlite/convention-source.js +48 -0
- package/out/sqlite/convention-source.js.map +1 -0
- package/out/sqlite/utils.d.ts +45 -0
- package/out/sqlite/utils.d.ts.map +1 -0
- package/out/sqlite/utils.js +56 -0
- package/out/sqlite/utils.js.map +1 -0
- package/out/street/centroid/schema.d.ts +100 -0
- package/out/street/centroid/schema.d.ts.map +1 -0
- package/out/street/centroid/schema.js +66 -0
- package/out/street/centroid/schema.js.map +1 -0
- package/out/street/centroid.d.ts +9 -19
- package/out/street/centroid.d.ts.map +1 -1
- package/out/street/centroid.js +11 -22
- package/out/street/centroid.js.map +1 -1
- package/out/street/{morphology-fst-builder.d.ts → morphology/fst/builder.d.ts} +21 -11
- package/out/street/morphology/fst/builder.d.ts.map +1 -0
- package/out/street/{morphology-fst-builder.js → morphology/fst/builder.js} +40 -34
- package/out/street/morphology/fst/builder.js.map +1 -0
- package/out/street/morphology/fst/loader.d.ts +74 -0
- package/out/street/morphology/fst/loader.d.ts.map +1 -0
- package/out/street/{morphology-fst-loader.js → morphology/fst/loader.js} +21 -21
- package/out/street/morphology/fst/loader.js.map +1 -0
- package/out/street/name-lookup.d.ts +24 -18
- package/out/street/name-lookup.d.ts.map +1 -1
- package/out/street/name-lookup.js +32 -31
- package/out/street/name-lookup.js.map +1 -1
- package/out/street/normalize.d.ts +35 -136
- package/out/street/normalize.d.ts.map +1 -1
- package/out/street/normalize.js +74 -191
- package/out/street/normalize.js.map +1 -1
- package/out/street/segment-schema.d.ts +79 -39
- package/out/street/segment-schema.d.ts.map +1 -1
- package/out/street/segment-schema.js +71 -21
- package/out/street/segment-schema.js.map +1 -1
- package/out/{street/index.d.ts → street.d.ts} +4 -4
- package/out/street.d.ts.map +1 -0
- package/out/{street/index.js → street.js} +4 -4
- package/out/street.js.map +1 -0
- package/out/types.d.ts +69 -138
- package/out/types.d.ts.map +1 -1
- package/out/types.js +0 -6
- package/out/types.js.map +1 -1
- package/out/unified-schema.d.ts +8 -12
- package/out/unified-schema.d.ts.map +1 -1
- package/out/unified-schema.js +29 -29
- package/out/unified-schema.js.map +1 -1
- package/out/uprn/existence.d.ts +28 -17
- package/out/uprn/existence.d.ts.map +1 -1
- package/out/uprn/existence.js +26 -16
- package/out/uprn/existence.js.map +1 -1
- package/out/uprn/lookup.d.ts +14 -44
- package/out/uprn/lookup.d.ts.map +1 -1
- package/out/uprn/lookup.js +20 -56
- package/out/uprn/lookup.js.map +1 -1
- package/out/uprn/schema.d.ts +18 -40
- package/out/uprn/schema.d.ts.map +1 -1
- package/out/uprn/schema.js +10 -32
- package/out/uprn/schema.js.map +1 -1
- package/out/{uprn/index.d.ts → uprn.d.ts} +1 -1
- package/out/uprn.d.ts.map +1 -0
- package/out/{uprn/index.js → uprn.js} +1 -1
- package/out/uprn.js.map +1 -0
- package/out/weights-overlay-linker.d.ts +132 -141
- package/out/weights-overlay-linker.d.ts.map +1 -1
- package/out/weights-overlay-linker.js +86 -154
- package/out/weights-overlay-linker.js.map +1 -1
- package/package.json +94 -599
- package/lib/address/point-schema.ts +0 -155
- package/lib/build-candidate.ts +0 -632
- package/lib/build-slim.ts +0 -408
- package/lib/candidate-ancestors-schema.ts +0 -195
- package/lib/candidate-fts.ts +0 -46
- package/lib/candidate-importance.ts +0 -224
- package/lib/candidate-lookup.ts +0 -977
- package/lib/candidate-schema.ts +0 -247
- package/lib/candidate-scoring.ts +0 -261
- package/lib/convention/index.ts +0 -154
- package/lib/fts/index.ts +0 -361
- package/lib/postal/city-alias-lookup.ts +0 -97
- package/lib/postal/index.ts +0 -9
- package/lib/sqlite-convention-source.ts +0 -65
- package/lib/sqlite-utils.ts +0 -82
- package/lib/street/centroid-schema.ts +0 -154
- package/out/address/index.d.ts.map +0 -1
- package/out/address/index.js.map +0 -1
- package/out/address/point-interpolation.d.ts +0 -49
- 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 +0 -97
- package/out/address/point-schema.d.ts.map +0 -1
- package/out/address/point-schema.js.map +0 -1
- package/out/ancestry/index.d.ts +0 -45
- package/out/ancestry/index.d.ts.map +0 -1
- package/out/ancestry/index.js +0 -55
- package/out/ancestry/index.js.map +0 -1
- package/out/build-candidate.d.ts +0 -184
- package/out/build-candidate.d.ts.map +0 -1
- package/out/build-candidate.js.map +0 -1
- package/out/build-slim.d.ts +0 -80
- package/out/build-slim.d.ts.map +0 -1
- package/out/build-slim.js.map +0 -1
- package/out/candidate/ancestors-sidecar.d.ts +0 -32
- 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 +0 -150
- package/out/candidate-ancestors-schema.d.ts.map +0 -1
- package/out/candidate-ancestors-schema.js +0 -123
- package/out/candidate-ancestors-schema.js.map +0 -1
- package/out/candidate-fts.d.ts +0 -37
- package/out/candidate-fts.d.ts.map +0 -1
- package/out/candidate-fts.js +0 -40
- package/out/candidate-fts.js.map +0 -1
- package/out/candidate-importance.d.ts +0 -132
- package/out/candidate-importance.d.ts.map +0 -1
- package/out/candidate-importance.js +0 -169
- package/out/candidate-importance.js.map +0 -1
- package/out/candidate-lookup.d.ts +0 -76
- package/out/candidate-lookup.d.ts.map +0 -1
- package/out/candidate-lookup.js +0 -768
- package/out/candidate-lookup.js.map +0 -1
- package/out/candidate-schema.d.ts +0 -155
- 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 +0 -196
- package/out/candidate-scoring.js.map +0 -1
- package/out/convention/index.d.ts +0 -106
- package/out/convention/index.d.ts.map +0 -1
- package/out/convention/index.js +0 -93
- package/out/convention/index.js.map +0 -1
- package/out/fst/index.d.ts.map +0 -1
- package/out/fst/index.js.map +0 -1
- package/out/fts/index.d.ts +0 -184
- package/out/fts/index.d.ts.map +0 -1
- package/out/fts/index.js +0 -277
- package/out/fts/index.js.map +0 -1
- package/out/geonames/index.d.ts.map +0 -1
- package/out/geonames/index.js.map +0 -1
- package/out/nsul/index.d.ts.map +0 -1
- package/out/nsul/index.js.map +0 -1
- package/out/poi/index.d.ts.map +0 -1
- package/out/poi/index.js.map +0 -1
- package/out/postal/city-alias-lookup.d.ts.map +0 -1
- package/out/postal/city-alias-lookup.js +0 -62
- 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 +0 -66
- package/out/postal/city-candidate-schema.d.ts.map +0 -1
- package/out/postal/city-candidate-schema.js +0 -56
- package/out/postal/city-candidate-schema.js.map +0 -1
- package/out/postal/index.d.ts +0 -9
- package/out/postal/index.d.ts.map +0 -1
- package/out/postal/index.js +0 -9
- package/out/postal/index.js.map +0 -1
- package/out/sqlite-convention-source.d.ts +0 -29
- package/out/sqlite-convention-source.d.ts.map +0 -1
- package/out/sqlite-convention-source.js +0 -57
- package/out/sqlite-convention-source.js.map +0 -1
- package/out/sqlite-utils.d.ts +0 -45
- package/out/sqlite-utils.d.ts.map +0 -1
- package/out/sqlite-utils.js +0 -57
- package/out/sqlite-utils.js.map +0 -1
- package/out/street/centroid-schema.d.ts +0 -105
- package/out/street/centroid-schema.d.ts.map +0 -1
- package/out/street/centroid-schema.js +0 -78
- package/out/street/centroid-schema.js.map +0 -1
- package/out/street/index.d.ts.map +0 -1
- package/out/street/index.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 +0 -67
- package/out/street/morphology-fst-loader.d.ts.map +0 -1
- package/out/street/morphology-fst-loader.js.map +0 -1
- package/out/uprn/index.d.ts.map +0 -1
- package/out/uprn/index.js.map +0 -1
- /package/lib/{fst/index.ts → fst.ts} +0 -0
- /package/lib/{geonames/index.ts → geonames.ts} +0 -0
- /package/lib/{nsul/index.ts → nsul.ts} +0 -0
- /package/lib/{poi/index.ts → poi.ts} +0 -0
- /package/lib/{uprn/index.ts → uprn.ts} +0 -0
package/lib/build-candidate.ts
DELETED
|
@@ -1,632 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @copyright Sister Software
|
|
3
|
-
* @license AGPL-3.0
|
|
4
|
-
* @author Teffen Ellis, et al.
|
|
5
|
-
*
|
|
6
|
-
* Build the global "candidate" lookup DB from a unified admin WOF DB — the byte-range-optimal
|
|
7
|
-
* gazetteer the browser demo resolves against. Instead of FTS5 (whose postings for a common name
|
|
8
|
-
* scatter across a multi-GB file → hundreds of serial range fetches), this materializes one
|
|
9
|
-
* `WITHOUT ROWID` B-tree keyed `(name_key, country_id, region_id, placetype_id, neg_rank,
|
|
10
|
-
* spr_id)`: every place's normalized name + distinct aliases + region abbreviations become rows,
|
|
11
|
-
* population rank is precomputed into `neg_rank`, and the rows are bulk-loaded PRE-SORTED so a
|
|
12
|
-
* resolve is one contiguous B-tree probe (a handful of pages → 1-2 chunk fetches, regardless of
|
|
13
|
-
* global volume).
|
|
14
|
-
*
|
|
15
|
-
* Each row is DENORMALIZED — it carries the place's display `name`, centroid (`latitude`/
|
|
16
|
-
* `longitude`), and `min/max` bbox — so a resolve is one statement, no FTS, no join to spr:
|
|
17
|
-
* SELECT spr_id, name, latitude, longitude, min_lat, ... FROM candidate WHERE name_key = ? AND
|
|
18
|
-
* country_id = ? AND placetype_id IN (...) [AND latitude BETWEEN ...] ORDER BY neg_rank ASC LIMIT
|
|
19
|
-
* K; The demo cascade resolves a parsed region first (its bbox), then constrains the locality to
|
|
20
|
-
* that bbox; `region_id` (the place's region-tier ancestor) is also carried for a future region
|
|
21
|
-
* 2-step.
|
|
22
|
-
*
|
|
23
|
-
* The name_key normalizer is the SHARED {@link normalizeLocalityForKey} — the query side (the demo
|
|
24
|
-
* resolver {@link WOFCandidateTableLookup}) MUST use the same function, the one-normalizer
|
|
25
|
-
* discipline the address-point extract uses, so build/query stay consistent by construction.
|
|
26
|
-
*
|
|
27
|
-
* Measured (2026-06-20, vs the 2.6 GB full-DB FTS): ~5 M rows; ~12 range fetches per 8-query
|
|
28
|
-
* session (the full DB needs 243); US locality 96.8% (region bbox), EU coord parity 88.6%.
|
|
29
|
-
*
|
|
30
|
-
* #28 adds one more denormalized field, `importance` — the toponym-fame prior that decides a BARE
|
|
31
|
-
* city name, joined in from a separate score source by name rather than by id (see
|
|
32
|
-
* `candidate-importance.ts`, which owns that join and explains why the id would be wrong). It is
|
|
33
|
-
* optional: without {@link BuildCandidateOptions.importance} the column is NULL on every row, which
|
|
34
|
-
* the consumer reads as unmeasured and ignores.
|
|
35
|
-
*
|
|
36
|
-
* The build also materializes the ANCESTORS SIDECAR (`candidate_ancestor` closure rows +
|
|
37
|
-
* `candidate_interval` pre/post labels) from the source `ancestors` table — the containment
|
|
38
|
-
* lineage behind {@link WOFCandidateTableLookup.ancestors} and the admin-coherence check.
|
|
39
|
-
* `candidate-ancestors-schema.ts` owns the encoding decision and the DAG/absence semantics.
|
|
40
|
-
*/
|
|
41
|
-
|
|
42
|
-
import { COUNTRY_POPULATION } from "@mailwoman/codex/country"
|
|
43
|
-
import { pathExists } from "@mailwoman/core/fs/readers"
|
|
44
|
-
import { removePath } from "@mailwoman/core/fs/writers"
|
|
45
|
-
import { DatabaseClient } from "@mailwoman/sqlite/client"
|
|
46
|
-
|
|
47
|
-
import { createCandidateFTS } from "#candidate-fts"
|
|
48
|
-
import { IMPORTANCE_JOIN_RADIUS_KM, loadImportanceIndex } from "#candidate-importance"
|
|
49
|
-
import {
|
|
50
|
-
CANDIDATE_COLUMNS,
|
|
51
|
-
createCandidateStagingTables,
|
|
52
|
-
createCandidateTable,
|
|
53
|
-
type CandidateDatabase,
|
|
54
|
-
} from "#candidate-schema"
|
|
55
|
-
import { explodeAliasBags } from "#candidate/alias-bags"
|
|
56
|
-
import { buildAncestorsSidecar } from "#candidate/ancestors-sidecar"
|
|
57
|
-
import { stageCountryDisplayNames } from "#candidate/country-display-names"
|
|
58
|
-
import { foldExtract } from "#candidate/extract-fold"
|
|
59
|
-
import { GLOSS_KEY_THRESHOLD, stampNameRoles } from "#candidate/name-roles"
|
|
60
|
-
import type { PlaceAttrs } from "#candidate/place-attrs"
|
|
61
|
-
import { createCapitalTable } from "#capital-schema"
|
|
62
|
-
import type { CapitalPoint } from "#capitals"
|
|
63
|
-
import { resurrectCurrencyHoles } from "#currency-backfill"
|
|
64
|
-
import type { WOFDatabase } from "#schema"
|
|
65
|
-
import { normalizeLocalityForKey } from "#street/normalize"
|
|
66
|
-
|
|
67
|
-
// The build's contract is this module path; the passes behind it live in `./candidate/`. Re-exported
|
|
68
|
-
// here so a consumer never has to know which pass owns which name.
|
|
69
|
-
export { stageCountryDisplayNames } from "#candidate/country-display-names"
|
|
70
|
-
export { GLOSS_EXCLUDED_PLACETYPES, GLOSS_KEY_THRESHOLD } from "#candidate/name-roles"
|
|
71
|
-
export type { PlaceAttrs } from "#candidate/place-attrs"
|
|
72
|
-
|
|
73
|
-
export interface BuildCandidateOptions {
|
|
74
|
-
/**
|
|
75
|
-
* Source unified admin DB — needs spr, place_population, place_search, place_abbr, ancestors.
|
|
76
|
-
*/
|
|
77
|
-
input: string
|
|
78
|
-
/**
|
|
79
|
-
* Output candidate DB path (overwritten if present).
|
|
80
|
-
*/
|
|
81
|
-
output: string
|
|
82
|
-
/**
|
|
83
|
-
* The capital-status reference entries (#1880) to carry in-artifact — the parsed `data/gazetteer/capitals-v1.json`
|
|
84
|
-
* entries, passed by the CALLER because this module publishes to npm and must not read repo-root paths. Absent → the
|
|
85
|
-
* `capital` table is not created, and the session loader falls back to the repo file where one exists.
|
|
86
|
-
*/
|
|
87
|
-
capitals?: readonly CapitalPoint[]
|
|
88
|
-
/**
|
|
89
|
-
* Optional postcode extracts (`spr` rows with `placetype='postalcode'` + real coords, e.g. postalcode-us.db) — folded
|
|
90
|
-
* in as `postalcode` candidate rows so `findPlace(postalcode)` resolves a ZIP directly (the demo's primary postcode
|
|
91
|
-
* path; the postcode-*.bin anchor stays the fallback). Matches the slim wof-hot.db, which took one such postcode DB.
|
|
92
|
-
*
|
|
93
|
-
* Each extract's `names` table is folded in too (#1495) — that's where the GeoNames delivery-city names live
|
|
94
|
-
* ("Brooklyn" for 11201), and they were previously reachable only through FTS.
|
|
95
|
-
*/
|
|
96
|
-
postcodes?: string[]
|
|
97
|
-
/**
|
|
98
|
-
* Optional LOCALITY extracts (`spr` rows with `placetype='locality'` + real coords, e.g. localities-nz-linz.db — the
|
|
99
|
-
* #1564 NZ suburb tier) — folded through the same extract loop as the postcode extracts, staged as `locality`
|
|
100
|
-
* candidate rows with UNMEASURED population (`neg_rank 0`: a extract row ranks behind any populated namesake and wins
|
|
101
|
-
* only where its key is the answer). Each extract's `names` table folds as aliases, `is_primary = 0`, same as the
|
|
102
|
-
* delivery-city pass. An extract whose `ancestors` table names an admin region for a row gives that row the region's
|
|
103
|
-
* scope (`region_id`) plus closure rows for the region and the region's own chain above it; an extract without one
|
|
104
|
-
* stays unscoped, as before.
|
|
105
|
-
*/
|
|
106
|
-
localities?: string[]
|
|
107
|
-
/**
|
|
108
|
-
* Optional WOF admin database carrying a `place_importance` table — the source of the `importance` column (#28), the
|
|
109
|
-
* toponym-fame prior that decides the bare-city-name class. Joined by `(name_key, country, placetype)` + nearest
|
|
110
|
-
* centroid, NOT by id; see `candidate-importance.ts` for why the id join silently drops the foreign homonyms the
|
|
111
|
-
* prior exists to demote.
|
|
112
|
-
*
|
|
113
|
-
* Omit it and every row's `importance` is NULL — unmeasured, which is what the consumer's positive-evidence-only rule
|
|
114
|
-
* already treats as "do not participate", so the artifact is byte-identical to a pre-#28 build except for the empty
|
|
115
|
-
* column. That is the honest degradation and it is the DEFAULT: a caller with no score source must not get a
|
|
116
|
-
* population-derived stand-in written into a column that means fame.
|
|
117
|
-
*/
|
|
118
|
-
importance?: string
|
|
119
|
-
/**
|
|
120
|
-
* Cross-source currency backfill (#1737). WOF carries deprecated-with-no-successor records for real, populated
|
|
121
|
-
* settlements (Rochester Kent, Aldershot, Telford — 120 GB localities alone), and the currency filter correctly drops
|
|
122
|
-
* them, leaving holes no ranking can fill. When this option is set, pass 1c resurrects a dead locality ONLY under
|
|
123
|
-
* three conditions, positive evidence throughout: no live same-name row of any placetype near the dead record (a
|
|
124
|
-
* distant same-name row is a NAMESAKE and does not block — Rochester, Northumberland pop 318 must not veto Rochester,
|
|
125
|
-
* Kent); an independent GeoNames P-class attestation of the same folded name within
|
|
126
|
-
* {@link CURRENCY_BACKFILL_RADIUS_KM}; and the attestor at or above {@link CURRENCY_BACKFILL_POP_FLOOR}. The staged
|
|
127
|
-
* row keeps the real WOF id, name, centroid and bbox — GeoNames only ATTESTS the place and supplies the population
|
|
128
|
-
* that lets it stand in prominence races. `countries` are judged only where `<cc>.txt` exists under `geonamesDir`;
|
|
129
|
-
* absent dumps are skipped loudly.
|
|
130
|
-
*/
|
|
131
|
-
currencyBackfill?: {
|
|
132
|
-
geonamesDir: string
|
|
133
|
-
countries: readonly string[]
|
|
134
|
-
/**
|
|
135
|
-
* The dead placetypes the resurrection judges; `resurrectCurrencyHoles`'s default (`locality`) when absent. The
|
|
136
|
-
* `localadmin` widening #1746 named is admitted here once its census is read.
|
|
137
|
-
*/
|
|
138
|
-
deadPlacetypes?: readonly string[]
|
|
139
|
-
}
|
|
140
|
-
/**
|
|
141
|
-
* Optional progress callback for CLI / test introspection.
|
|
142
|
-
*/
|
|
143
|
-
onProgress?: (phase: string, message: string) => void
|
|
144
|
-
/**
|
|
145
|
-
* Key-count threshold for the gloss anomaly detector — {@link GLOSS_KEY_THRESHOLD} unless a test passes a
|
|
146
|
-
* fixture-scale value. The production number is the #1730 sweep's own threshold (4,000 places at >= 50 keys).
|
|
147
|
-
*/
|
|
148
|
-
glossKeyThreshold?: number
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
export interface BuildCandidateResult {
|
|
152
|
-
rows: number
|
|
153
|
-
places: number
|
|
154
|
-
primaries: number
|
|
155
|
-
aliases: number
|
|
156
|
-
abbrevs: number
|
|
157
|
-
postcodes: number
|
|
158
|
-
/**
|
|
159
|
-
* Delivery-city (and other `names`-table) aliases folded onto postcode rows — #1495. Zero here means the extracts
|
|
160
|
-
* carried no alias names, NOT that the pass was skipped: a extract with no `names` table reports that separately
|
|
161
|
-
* through `onProgress`.
|
|
162
|
-
*/
|
|
163
|
-
postcodeAliases: number
|
|
164
|
-
/**
|
|
165
|
-
* Closure rows in the `candidate_ancestor` sidecar (see candidate-ancestors-schema.ts). Zero means the source
|
|
166
|
-
* `ancestors` table contributed nothing within the resolvable placetypes — a finding, reported rather than implied.
|
|
167
|
-
*/
|
|
168
|
-
ancestorRows: number
|
|
169
|
-
/**
|
|
170
|
-
* Places carrying at least one closure row.
|
|
171
|
-
*/
|
|
172
|
-
ancestorPlaces: number
|
|
173
|
-
/**
|
|
174
|
-
* Places that received a pre/post interval label — the canonical-parent forest's node count. Places outside it (no
|
|
175
|
-
* recorded ancestry, extract rows, cycle-skipped) have NO label: containment against them is unverifiable, never
|
|
176
|
-
* false.
|
|
177
|
-
*/
|
|
178
|
-
intervalPlaces: number
|
|
179
|
-
/**
|
|
180
|
-
* Places that took an `importance` score from the join (#28).
|
|
181
|
-
*
|
|
182
|
-
* `undefined` and `0` mean different things. `undefined` is "the pass did not run" — no score source was given. A `0`
|
|
183
|
-
* would be the source matching NOTHING, which is a finding. Never collapse the two.
|
|
184
|
-
*/
|
|
185
|
-
importanceScored?: number
|
|
186
|
-
/**
|
|
187
|
-
* Places whose `(name_key, country, placetype)` matched a scored group but whose nearest scored centroid was outside
|
|
188
|
-
* {@link IMPORTANCE_JOIN_RADIUS_KM} — a different town wearing the same name, refused rather than scored.
|
|
189
|
-
*
|
|
190
|
-
* Worth watching across rebuilds: a jump here means the score source and the admin source have drifted apart, and the
|
|
191
|
-
* join is being asked to guess.
|
|
192
|
-
*/
|
|
193
|
-
importanceFiltered?: number
|
|
194
|
-
/**
|
|
195
|
-
* Alias rows stamped `name_role = 'gloss'` — the anomaly detector's certain core (#1730).
|
|
196
|
-
*/
|
|
197
|
-
roleGloss: number
|
|
198
|
-
/**
|
|
199
|
-
* Alias rows stamped `name_role = 'abbr'` — the variant-in-official-language provenance signal (#1730/#936).
|
|
200
|
-
*/
|
|
201
|
-
roleAbbr: number
|
|
202
|
-
/**
|
|
203
|
-
* Admin places whose staged key count reached {@link GLOSS_KEY_THRESHOLD} — the sweep's tail, reported so the stamped
|
|
204
|
-
* fraction has its denominator.
|
|
205
|
-
*/
|
|
206
|
-
keyTailPlaces: number
|
|
207
|
-
/**
|
|
208
|
-
* Of {@link BuildCandidateResult.keyTailPlaces}, how many carry at least one stamped role row.
|
|
209
|
-
*/
|
|
210
|
-
keyTailWithRole: number
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
export async function buildCandidateTable(opts: BuildCandidateOptions): Promise<BuildCandidateResult> {
|
|
214
|
-
const progress = opts.onProgress ?? (() => {})
|
|
215
|
-
|
|
216
|
-
if (await pathExists(opts.output)) {
|
|
217
|
-
await removePath(opts.output)
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
using src = new DatabaseClient<WOFDatabase>(opts.input, { readOnly: true })
|
|
221
|
-
await using kdb = new DatabaseClient<CandidateDatabase>(opts.output)
|
|
222
|
-
// Build-tuning pragmas (raw — Kysely doesn't model PRAGMA). The code dictionaries + the transient
|
|
223
|
-
// staging table come from the SHARED schema DDL, so they can't drift from {@link CandidateDatabase}.
|
|
224
|
-
kdb.exec("PRAGMA page_size=8192; PRAGMA journal_mode=OFF; PRAGMA synchronous=OFF; PRAGMA cache_size=-2000000;")
|
|
225
|
-
|
|
226
|
-
await createCandidateStagingTables(kdb)
|
|
227
|
-
|
|
228
|
-
// --- compact code maps (country/placetype → small int, shrinks the clustered key). The ids are
|
|
229
|
-
// assigned here; the rows are bulk-inserted via kdb once the passes have discovered every code. ---
|
|
230
|
-
const ccodes = new Map<string, number>()
|
|
231
|
-
const ptcodes = new Map<string, number>()
|
|
232
|
-
|
|
233
|
-
const ccID = (code: string | null): number => {
|
|
234
|
-
const c = (code || "??").toUpperCase()
|
|
235
|
-
let id = ccodes.get(c)
|
|
236
|
-
|
|
237
|
-
if (id === undefined) {
|
|
238
|
-
id = ccodes.size
|
|
239
|
-
ccodes.set(c, id)
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
return id
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
const ptID = (pt: string | null): number => {
|
|
246
|
-
const p = pt || ""
|
|
247
|
-
let id = ptcodes.get(p)
|
|
248
|
-
|
|
249
|
-
if (id === undefined) {
|
|
250
|
-
id = ptcodes.size
|
|
251
|
-
ptcodes.set(p, id)
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
return id
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
// --- importance source (#28): loaded BEFORE pass 1, which is the only pass that sees a place's
|
|
258
|
-
// name/country/placetype/centroid together. Absent → every row's `importance` stays NULL. ---
|
|
259
|
-
let importance: ReturnType<typeof loadImportanceIndex> | undefined
|
|
260
|
-
|
|
261
|
-
if (opts.importance) {
|
|
262
|
-
progress("importance", `loading place_importance from ${opts.importance}`)
|
|
263
|
-
importance = loadImportanceIndex(opts.importance)
|
|
264
|
-
|
|
265
|
-
progress(
|
|
266
|
-
"importance",
|
|
267
|
-
`${importance.stats.places.toLocaleString()} scored places in ${importance.stats.keys.toLocaleString()} (name, country, placetype) groups` +
|
|
268
|
-
(importance.stats.unkeyable ? `; ${importance.stats.unkeyable.toLocaleString()} unkeyable names skipped` : "")
|
|
269
|
-
)
|
|
270
|
-
} else {
|
|
271
|
-
// Never a silent column of nulls: a build without a score source produces one, and the reason has
|
|
272
|
-
// to be visible in the log rather than inferred from the artifact.
|
|
273
|
-
progress("importance", "no score source given — `importance` will be NULL on every row")
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
// --- region_id per place (its region-tier ancestor) for same-name disambiguation ---
|
|
277
|
-
progress("region", "loading region ancestry")
|
|
278
|
-
const regionOf = new Map<number, number>()
|
|
279
|
-
let multiRegion = 0
|
|
280
|
-
|
|
281
|
-
// A place can carry more than one region-tier ancestor — 59 do on the 2026-08-03 artifact (42
|
|
282
|
-
// localities, mostly Chinese places on an ambiguous boundary). One `region_id` column holds one of
|
|
283
|
-
// them, and which one it ought to be is a real question this is not the place to answer.
|
|
284
|
-
//
|
|
285
|
-
// MIN is arbitrary but STABLE: an unordered pick lets the stamp for those places differ between two
|
|
286
|
-
// builds of the same source. The count is logged because the number is expected to grow, and should
|
|
287
|
-
// be visible rather than inferred.
|
|
288
|
-
for (const r of src
|
|
289
|
-
.prepare(
|
|
290
|
-
"SELECT id, MIN(ancestor_id) AS ancestor_id, COUNT(DISTINCT ancestor_id) AS n" +
|
|
291
|
-
" FROM ancestors WHERE ancestor_placetype='region' GROUP BY id"
|
|
292
|
-
)
|
|
293
|
-
.iterate()) {
|
|
294
|
-
regionOf.set(Number(r.id), Number(r.ancestor_id))
|
|
295
|
-
|
|
296
|
-
if (Number(r.n) > 1) {
|
|
297
|
-
multiRegion++
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
progress(
|
|
302
|
-
"region",
|
|
303
|
-
`${regionOf.size.toLocaleString()} places carry a region` +
|
|
304
|
-
(multiRegion ? ` (${multiRegion.toLocaleString()} carry more than one; stamped with the lowest id)` : "")
|
|
305
|
-
)
|
|
306
|
-
|
|
307
|
-
// The hot path — millions of clustered rows. Kept a single positional prepared statement (the fastest
|
|
308
|
-
// node:sqlite insert) rather than a per-row query builder. Placeholders come from CANDIDATE_COLUMNS so
|
|
309
|
-
// the column COUNT can't drift; the positional run() args below MUST stay in CANDIDATE_COLUMNS order.
|
|
310
|
-
const insStage = kdb.prepare(`INSERT INTO cand_stage VALUES (${CANDIDATE_COLUMNS.map(() => "?").join(", ")})`)
|
|
311
|
-
|
|
312
|
-
// --- pass 1: primaries (and the per-place attrs the alias/abbrev passes reuse) ---
|
|
313
|
-
progress("primaries", "indexing place names")
|
|
314
|
-
const attrs = new Map<number, PlaceAttrs>()
|
|
315
|
-
let nPrim = 0
|
|
316
|
-
kdb.exec("BEGIN")
|
|
317
|
-
|
|
318
|
-
for (const r of src
|
|
319
|
-
.prepare(
|
|
320
|
-
`SELECT s.id AS id, s.name AS name, s.placetype AS placetype, s.country AS country,
|
|
321
|
-
s.latitude AS lat, s.longitude AS lon,
|
|
322
|
-
s.min_latitude AS mnlat, s.min_longitude AS mnlon, s.max_latitude AS mxlat, s.max_longitude AS mxlon,
|
|
323
|
-
COALESCE(pp.population,0) AS pop
|
|
324
|
-
FROM spr s LEFT JOIN place_population pp ON pp.id = s.id
|
|
325
|
-
WHERE s.is_current != 0 AND s.is_deprecated = 0`
|
|
326
|
-
)
|
|
327
|
-
.iterate()) {
|
|
328
|
-
const sid = Number(r.id)
|
|
329
|
-
const cid = ccID(r.country as string | null)
|
|
330
|
-
const ptid = ptID(r.placetype as string | null)
|
|
331
|
-
const rid = regionOf.get(sid) ?? 0
|
|
332
|
-
// A zero population on a COUNTRY row is a WOF absence artifact, never a real zero — 147 of 237
|
|
333
|
-
// primary country records carried none (measured 2026-08-18, #1650), which ranked those nations
|
|
334
|
-
// below any namesake hamlet in every prominence race ("Georgia" → Georgia VT). The codex table is
|
|
335
|
-
// the secondary source; a country absent from it too stays at zero honestly.
|
|
336
|
-
const wofPop = Number(r.pop) || 0
|
|
337
|
-
const pop = wofPop === 0 && r.placetype === "country" ? (COUNTRY_POPULATION[String(r.country ?? "")] ?? 0) : wofPop
|
|
338
|
-
const neg = -Math.log10(pop + 1)
|
|
339
|
-
const name = String(r.name ?? "")
|
|
340
|
-
const pkey = normalizeLocalityForKey(name)
|
|
341
|
-
const lat = r.lat as number
|
|
342
|
-
const lon = r.lon as number
|
|
343
|
-
|
|
344
|
-
const a: PlaceAttrs = {
|
|
345
|
-
cid,
|
|
346
|
-
rid,
|
|
347
|
-
ptid,
|
|
348
|
-
name,
|
|
349
|
-
lat,
|
|
350
|
-
lon,
|
|
351
|
-
mnLat: r.mnlat as number,
|
|
352
|
-
mnLon: r.mnlon as number,
|
|
353
|
-
mxLat: r.mxlat as number,
|
|
354
|
-
mxLon: r.mxlon as number,
|
|
355
|
-
pop,
|
|
356
|
-
neg,
|
|
357
|
-
pkey,
|
|
358
|
-
imp: importance?.find(name, r.country as string | null, r.placetype as string | null, lat, lon) ?? null,
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
attrs.set(sid, a)
|
|
362
|
-
|
|
363
|
-
if (pkey) {
|
|
364
|
-
insStage.run(
|
|
365
|
-
pkey,
|
|
366
|
-
cid,
|
|
367
|
-
rid,
|
|
368
|
-
ptid,
|
|
369
|
-
neg,
|
|
370
|
-
sid,
|
|
371
|
-
name,
|
|
372
|
-
a.lat,
|
|
373
|
-
a.lon,
|
|
374
|
-
a.mnLat,
|
|
375
|
-
a.mnLon,
|
|
376
|
-
a.mxLat,
|
|
377
|
-
a.mxLon,
|
|
378
|
-
pop,
|
|
379
|
-
1,
|
|
380
|
-
a.imp,
|
|
381
|
-
null
|
|
382
|
-
)
|
|
383
|
-
|
|
384
|
-
nPrim++
|
|
385
|
-
}
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
kdb.exec("COMMIT")
|
|
389
|
-
progress("primaries", `${nPrim.toLocaleString()} primaries; ${attrs.size.toLocaleString()} places`)
|
|
390
|
-
|
|
391
|
-
if (importance) {
|
|
392
|
-
progress(
|
|
393
|
-
"importance",
|
|
394
|
-
`${importance.matched.toLocaleString()} places scored; ` +
|
|
395
|
-
`${importance.refused.toLocaleString()} refused (nearest same-name place > ${IMPORTANCE_JOIN_RADIUS_KM} km away)`
|
|
396
|
-
)
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
const stageRow = (k: string, a: PlaceAttrs, sid: number, isPrimary: number): void => {
|
|
400
|
-
insStage.run(
|
|
401
|
-
k,
|
|
402
|
-
a.cid,
|
|
403
|
-
a.rid,
|
|
404
|
-
a.ptid,
|
|
405
|
-
a.neg,
|
|
406
|
-
sid,
|
|
407
|
-
a.name,
|
|
408
|
-
a.lat,
|
|
409
|
-
a.lon,
|
|
410
|
-
a.mnLat,
|
|
411
|
-
a.mnLon,
|
|
412
|
-
a.mxLat,
|
|
413
|
-
a.mxLon,
|
|
414
|
-
a.pop,
|
|
415
|
-
isPrimary,
|
|
416
|
-
a.imp,
|
|
417
|
-
null
|
|
418
|
-
)
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
// --- pass 1b: country surfaces across scripts (#1678 thread 1) — see stageCountryDisplayNames ---
|
|
422
|
-
// Never a silent zero: a runtime whose ICU lacks these locales degrades to fewer surfaces, and the count is how a
|
|
423
|
-
// reader tells that apart from the pass not having run.
|
|
424
|
-
progress(
|
|
425
|
-
"country-display-names",
|
|
426
|
-
`${stageCountryDisplayNames({
|
|
427
|
-
attrs,
|
|
428
|
-
iso2ByID: new Map([...ccodes].map(([code, id]) => [id, code])),
|
|
429
|
-
countryPtID: ptID("country"),
|
|
430
|
-
stageRow,
|
|
431
|
-
tx: kdb,
|
|
432
|
-
}).toLocaleString()} country surfaces`
|
|
433
|
-
)
|
|
434
|
-
|
|
435
|
-
// --- pass 1c: cross-source currency backfill (#1737 — resurrectCurrencyHoles owns the checks). Runs BEFORE
|
|
436
|
-
// the alias pass so a resurrected place's alt names explode like any primary's. ---
|
|
437
|
-
if (opts.currencyBackfill) {
|
|
438
|
-
const nBackfill = await resurrectCurrencyHoles({
|
|
439
|
-
src,
|
|
440
|
-
tx: kdb,
|
|
441
|
-
geonamesDir: opts.currencyBackfill.geonamesDir,
|
|
442
|
-
countries: opts.currencyBackfill.countries,
|
|
443
|
-
...(opts.currencyBackfill.deadPlacetypes ? { deadPlacetypes: opts.currencyBackfill.deadPlacetypes } : {}),
|
|
444
|
-
attrs,
|
|
445
|
-
ccID,
|
|
446
|
-
ptID,
|
|
447
|
-
regionOf,
|
|
448
|
-
importance,
|
|
449
|
-
stageRow,
|
|
450
|
-
progress,
|
|
451
|
-
})
|
|
452
|
-
|
|
453
|
-
progress("currency-backfill", `${nBackfill.toLocaleString()} resurrections staged`)
|
|
454
|
-
} else {
|
|
455
|
-
// Never a silent absence: a build without the option leaves the deprecated-no-successor holes dead,
|
|
456
|
-
// and the log must say so rather than leave it inferable only from a missing row.
|
|
457
|
-
progress("currency-backfill", "not configured — deprecated-no-successor holes stay dead (#1737)")
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
// --- pass 2: distinct normalized aliases from place_search.alt_names (explodeAliasBags owns the loop) ---
|
|
461
|
-
progress("aliases", "exploding alias bags")
|
|
462
|
-
const { nAlias, keyCounts } = explodeAliasBags(src, kdb, attrs, stageRow)
|
|
463
|
-
progress("aliases", `${nAlias.toLocaleString()} aliases`)
|
|
464
|
-
|
|
465
|
-
// --- pass 3: region abbreviations (place_abbr) ---
|
|
466
|
-
let nAbbr = 0
|
|
467
|
-
kdb.exec("BEGIN")
|
|
468
|
-
|
|
469
|
-
for (const r of src.prepare("SELECT id, abbr FROM place_abbr").iterate()) {
|
|
470
|
-
const a = attrs.get(Number(r.id))
|
|
471
|
-
|
|
472
|
-
if (!a) continue
|
|
473
|
-
const k = normalizeLocalityForKey(String(r.abbr ?? ""))
|
|
474
|
-
|
|
475
|
-
if (!k) continue
|
|
476
|
-
stageRow(k, a, Number(r.id), 1)
|
|
477
|
-
|
|
478
|
-
nAbbr++
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
kdb.exec("COMMIT")
|
|
482
|
-
progress("abbrevs", `${nAbbr.toLocaleString()} abbrevs`)
|
|
483
|
-
|
|
484
|
-
// --- pass 3b: name roles (#1730 prototype — stampNameRoles owns the detectors) ---
|
|
485
|
-
// Independent of the sidecar below: this writes `cand_stage.name_role`, that writes the ancestor and
|
|
486
|
-
// interval tables, and neither reads the other's output. Ordered by label only.
|
|
487
|
-
const roles = stampNameRoles({
|
|
488
|
-
src,
|
|
489
|
-
out: kdb,
|
|
490
|
-
attrs,
|
|
491
|
-
keyCounts,
|
|
492
|
-
glossThreshold: opts.glossKeyThreshold ?? GLOSS_KEY_THRESHOLD,
|
|
493
|
-
ptcodes,
|
|
494
|
-
ccodes,
|
|
495
|
-
progress,
|
|
496
|
-
})
|
|
497
|
-
|
|
498
|
-
// --- pass 3c: the ancestors sidecar (candidate-ancestors-schema.ts owns the encoding decision) ---
|
|
499
|
-
const sidecar = await buildAncestorsSidecar({ src, out: kdb, attrs, ptID, progress })
|
|
500
|
-
|
|
501
|
-
// --- pass 4 + 4b: postcode and locality extracts (foldExtract owns the per-extract loop) ---
|
|
502
|
-
let nPostcode = 0
|
|
503
|
-
let nPostcodeAlias = 0
|
|
504
|
-
|
|
505
|
-
for (const pcDB of opts.postcodes ?? []) {
|
|
506
|
-
const folded = foldExtract({
|
|
507
|
-
out: kdb,
|
|
508
|
-
extractPath: pcDB,
|
|
509
|
-
extractPlacetype: "postalcode",
|
|
510
|
-
ccID,
|
|
511
|
-
ptID,
|
|
512
|
-
stageRow,
|
|
513
|
-
progress,
|
|
514
|
-
})
|
|
515
|
-
|
|
516
|
-
nPostcode += folded.primaries
|
|
517
|
-
nPostcodeAlias += folded.aliases
|
|
518
|
-
}
|
|
519
|
-
|
|
520
|
-
if (nPostcode > 0) {
|
|
521
|
-
progress("postcodes", `${nPostcode.toLocaleString()} postcodes; ${nPostcodeAlias.toLocaleString()} aliases`)
|
|
522
|
-
}
|
|
523
|
-
|
|
524
|
-
let nLocality = 0
|
|
525
|
-
let nLocalityScoped = 0
|
|
526
|
-
let nLocalityAncestor = 0
|
|
527
|
-
|
|
528
|
-
for (const locDB of opts.localities ?? []) {
|
|
529
|
-
const folded = foldExtract({
|
|
530
|
-
out: kdb,
|
|
531
|
-
extractPath: locDB,
|
|
532
|
-
extractPlacetype: "locality",
|
|
533
|
-
ccID,
|
|
534
|
-
ptID,
|
|
535
|
-
stageRow,
|
|
536
|
-
attrs,
|
|
537
|
-
progress,
|
|
538
|
-
})
|
|
539
|
-
|
|
540
|
-
nLocality += folded.primaries
|
|
541
|
-
nLocalityScoped += folded.scoped
|
|
542
|
-
nLocalityAncestor += folded.ancestorRows
|
|
543
|
-
}
|
|
544
|
-
|
|
545
|
-
if (nLocality > 0) {
|
|
546
|
-
progress(
|
|
547
|
-
"localities",
|
|
548
|
-
`${nLocality.toLocaleString()} extract localities folded` +
|
|
549
|
-
(nLocalityScoped
|
|
550
|
-
? `; ${nLocalityScoped.toLocaleString()} carry a region scope (${nLocalityAncestor.toLocaleString()} closure rows)`
|
|
551
|
-
: "")
|
|
552
|
-
)
|
|
553
|
-
}
|
|
554
|
-
|
|
555
|
-
// --- code dictionaries: typed batch inserts via kdb (a few hundred rows — Kysely is clean here) ---
|
|
556
|
-
if (ccodes.size) {
|
|
557
|
-
await kdb
|
|
558
|
-
.insertInto("country_codes")
|
|
559
|
-
.values([...ccodes].map(([code, id]) => ({ id, code })))
|
|
560
|
-
.execute()
|
|
561
|
-
}
|
|
562
|
-
|
|
563
|
-
if (ptcodes.size) {
|
|
564
|
-
await kdb
|
|
565
|
-
.insertInto("placetype_codes")
|
|
566
|
-
.values([...ptcodes].map(([placetype, id]) => ({ id, placetype })))
|
|
567
|
-
.execute()
|
|
568
|
-
}
|
|
569
|
-
|
|
570
|
-
// --- capital-status reference (#1880's distribution home) — cold, small (~3.7k rows), typed ---
|
|
571
|
-
if (opts.capitals?.length) {
|
|
572
|
-
await createCapitalTable<CandidateDatabase>(kdb)
|
|
573
|
-
|
|
574
|
-
await kdb
|
|
575
|
-
.insertInto("capital")
|
|
576
|
-
.values(
|
|
577
|
-
opts.capitals.map((entry) => ({
|
|
578
|
-
country: entry.country,
|
|
579
|
-
latitude: entry.latitude,
|
|
580
|
-
longitude: entry.longitude,
|
|
581
|
-
level: entry.level,
|
|
582
|
-
keys: JSON.stringify(entry.k),
|
|
583
|
-
}))
|
|
584
|
-
)
|
|
585
|
-
.execute()
|
|
586
|
-
|
|
587
|
-
progress("capitals", `${opts.capitals.length.toLocaleString()} capital-reference rows carried in-artifact`)
|
|
588
|
-
}
|
|
589
|
-
|
|
590
|
-
// --- materialize the clustered WITHOUT ROWID table (sorted insert → contiguous leaves) ---
|
|
591
|
-
progress("cluster", "building clustered candidate table + VACUUM")
|
|
592
|
-
// Column list + clustered-key order are sourced from CANDIDATE_COLUMNS (the first 6 ARE the PRIMARY
|
|
593
|
-
// KEY) so the SELECT, the ORDER BY, and the table can't drift. The table comes from the shared
|
|
594
|
-
// createCandidateTable().
|
|
595
|
-
const cols = CANDIDATE_COLUMNS.join(", ")
|
|
596
|
-
const keyOrder = CANDIDATE_COLUMNS.slice(0, 6).join(", ")
|
|
597
|
-
await createCandidateTable(kdb)
|
|
598
|
-
// OR IGNORE: an abbrev/alias can normalize to a place's primary key (same place, same rank) → any one
|
|
599
|
-
// row. The bulk sorted INSERT…SELECT (clustered materialization) stays raw — a single hot bulk statement.
|
|
600
|
-
kdb.exec(`INSERT OR IGNORE INTO candidate (${cols}) SELECT ${cols} FROM cand_stage ORDER BY ${keyOrder};`)
|
|
601
|
-
await kdb.schema.dropTable("cand_stage").execute()
|
|
602
|
-
// Typo-tolerant fallback index (the unified gazetteer's second mode): the exact name_key probe can't
|
|
603
|
-
// recover misspellings, so FTS5-trigram over `name` lets the reader fuzzy-match on an exact+strip miss.
|
|
604
|
-
progress("fts", "building FTS5-trigram fuzzy index")
|
|
605
|
-
createCandidateFTS(kdb)
|
|
606
|
-
// page_size MUST be set right before VACUUM: node:sqlite initializes the file at the 4096 default on
|
|
607
|
-
// `new DatabaseSync`, so the creation-time pragma is a no-op — only a VACUUM rebuilds at the new size.
|
|
608
|
-
// 8192 matches the sql.js-httpvfs 64 KiB request chunk cleanly (8 pages) and shallows the B-tree.
|
|
609
|
-
kdb.exec("PRAGMA page_size=8192")
|
|
610
|
-
kdb.exec("VACUUM")
|
|
611
|
-
|
|
612
|
-
const { n: rows } = await kdb
|
|
613
|
-
.selectFrom("candidate")
|
|
614
|
-
.select((eb) => eb.fn.countAll<number>().as("n"))
|
|
615
|
-
.executeTakeFirstOrThrow()
|
|
616
|
-
|
|
617
|
-
// closes the underlying `out` connection
|
|
618
|
-
return {
|
|
619
|
-
rows,
|
|
620
|
-
places: attrs.size,
|
|
621
|
-
primaries: nPrim,
|
|
622
|
-
aliases: nAlias,
|
|
623
|
-
abbrevs: nAbbr,
|
|
624
|
-
postcodes: nPostcode,
|
|
625
|
-
postcodeAliases: nPostcodeAlias,
|
|
626
|
-
ancestorRows: sidecar.ancestorRows + nLocalityAncestor,
|
|
627
|
-
ancestorPlaces: sidecar.ancestorPlaces + nLocalityScoped,
|
|
628
|
-
intervalPlaces: sidecar.intervalPlaces,
|
|
629
|
-
...roles,
|
|
630
|
-
...(importance ? { importanceScored: importance.matched, importanceFiltered: importance.refused } : {}),
|
|
631
|
-
}
|
|
632
|
-
}
|