@mailwoman/resolver-wof-sqlite 10.0.0 → 10.2.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 +9 -8
- package/lib/address/point/interpolation.ts +45 -39
- package/lib/address/point/schema.ts +17 -40
- package/lib/address/point.ts +316 -0
- package/lib/{address/index.ts → address.ts} +1 -1
- package/lib/ancestry/backfill.ts +20 -86
- package/lib/{ancestry/index.ts → ancestry.ts} +19 -15
- package/lib/build/candidate.ts +122 -174
- package/lib/build/population-corrections.ts +64 -0
- package/lib/build/slim.ts +44 -114
- package/lib/candidate/alias-bags.ts +16 -17
- package/lib/candidate/ancestors/schema.ts +77 -61
- package/lib/candidate/ancestors/sidecar.ts +15 -22
- package/lib/candidate/country-display-names.ts +6 -20
- package/lib/candidate/extract-fold.ts +10 -55
- package/lib/candidate/fts.ts +5 -23
- package/lib/candidate/importance.ts +65 -68
- package/lib/candidate/lookup.ts +124 -415
- package/lib/candidate/name-roles.ts +15 -33
- package/lib/candidate/own-name.ts +35 -32
- package/lib/candidate/place-attrs.ts +20 -13
- package/lib/candidate/schema.ts +29 -65
- package/lib/candidate/scoring.ts +54 -93
- package/lib/capital-schema.ts +21 -17
- package/lib/capitals.ts +29 -27
- package/lib/coincident-roles.ts +85 -55
- package/lib/convention/schema.ts +15 -16
- package/lib/convention.ts +144 -0
- package/lib/coverage-manifest-schema.ts +40 -45
- package/lib/currency-backfill.ts +34 -79
- package/lib/env.ts +1 -1
- package/lib/exact-match.ts +32 -21
- package/lib/extracts.ts +97 -74
- package/lib/fst/autocomplete.ts +54 -37
- package/lib/fst/builder.ts +37 -41
- package/lib/fst/deserialize-web.ts +16 -17
- package/lib/fst/format.ts +41 -23
- package/lib/fst/freshness.ts +43 -120
- package/lib/fst/matcher.ts +7 -5
- package/lib/fst/serialize.ts +54 -74
- package/lib/fst/types.ts +78 -75
- package/lib/{fst/index.ts → fst.ts} +1 -0
- package/lib/fts/query.ts +5 -36
- package/lib/fts.ts +280 -0
- package/lib/geonames/aliases.ts +73 -144
- package/lib/geonames/postal.ts +162 -71
- package/lib/index.ts +9 -8
- package/lib/interpolation.ts +48 -100
- package/lib/lookup.ts +155 -270
- package/lib/name-score.ts +9 -7
- package/lib/nsul/lookup.ts +37 -54
- package/lib/nsul/schema.ts +16 -43
- package/lib/paths.ts +159 -0
- package/lib/place-importance-schema.ts +62 -169
- package/lib/poi/lookup.ts +104 -121
- package/lib/poi/schema.ts +42 -26
- package/lib/polygon-schema.ts +10 -6
- package/lib/postal/city/alias/lookup.ts +22 -40
- package/lib/postal/city/alias/schema.ts +14 -10
- package/lib/postal/city/candidate-schema.ts +26 -20
- package/lib/postcode-point-lookup.ts +8 -27
- package/lib/primary-preference.ts +46 -126
- package/lib/proximity-rerank.ts +44 -33
- package/lib/ranking-weights.ts +105 -78
- package/lib/region-keys.ts +70 -42
- package/lib/reverse.ts +149 -86
- package/lib/schema.ts +73 -50
- package/lib/search-fetch.ts +90 -68
- package/lib/sqlite/convention-source.ts +15 -23
- package/lib/sqlite/utils.ts +19 -22
- package/lib/street/centroid/schema.ts +37 -42
- package/lib/street/{centroid/index.ts → centroid.ts} +16 -25
- package/lib/street/morphology/fst/builder.ts +60 -42
- package/lib/street/morphology/fst/loader.ts +40 -32
- package/lib/street/name-lookup.ts +45 -41
- package/lib/street/normalize.ts +88 -219
- package/lib/street/segment-schema.ts +116 -43
- package/lib/{street/index.ts → street.ts} +1 -1
- package/lib/types.ts +109 -150
- package/lib/unified-schema.ts +29 -29
- package/lib/uprn/existence.ts +30 -19
- package/lib/uprn/lookup.ts +29 -68
- package/lib/uprn/schema.ts +18 -40
- package/lib/weights-overlay-linker.ts +236 -252
- package/out/address/point/interpolation.d.ts +20 -23
- package/out/address/point/interpolation.d.ts.map +1 -1
- package/out/address/point/interpolation.js +42 -39
- package/out/address/point/interpolation.js.map +1 -1
- package/out/address/point/schema.d.ts +12 -35
- package/out/address/point/schema.d.ts.map +1 -1
- package/out/address/point/schema.js +3 -19
- package/out/address/point/schema.js.map +1 -1
- package/out/address/point.d.ts +57 -0
- package/out/address/point.d.ts.map +1 -0
- package/out/address/point.js +206 -0
- package/out/address/point.js.map +1 -0
- package/out/{address/index.js → address.d.ts} +2 -2
- package/out/address.d.ts.map +1 -0
- package/out/{address/index.d.ts → address.js} +2 -2
- 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 +14 -74
- 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 +82 -112
- package/out/build/candidate.d.ts.map +1 -1
- package/out/build/candidate.js +17 -100
- package/out/build/candidate.js.map +1 -1
- 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 +8 -44
- package/out/build/slim.d.ts.map +1 -1
- package/out/build/slim.js +33 -94
- package/out/build/slim.js.map +1 -1
- package/out/candidate/alias-bags.d.ts +6 -6
- package/out/candidate/alias-bags.d.ts.map +1 -1
- package/out/candidate/alias-bags.js +16 -17
- package/out/candidate/alias-bags.js.map +1 -1
- package/out/candidate/ancestors/schema.d.ts +73 -57
- package/out/candidate/ancestors/schema.d.ts.map +1 -1
- package/out/candidate/ancestors/schema.js +56 -48
- package/out/candidate/ancestors/schema.js.map +1 -1
- package/out/candidate/ancestors/sidecar.d.ts +3 -8
- package/out/candidate/ancestors/sidecar.d.ts.map +1 -1
- package/out/candidate/ancestors/sidecar.js +15 -22
- package/out/candidate/ancestors/sidecar.js.map +1 -1
- 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 +7 -15
- package/out/candidate/extract-fold.d.ts.map +1 -1
- package/out/candidate/extract-fold.js +5 -47
- package/out/candidate/extract-fold.js.map +1 -1
- package/out/candidate/fts.d.ts +5 -23
- package/out/candidate/fts.d.ts.map +1 -1
- package/out/candidate/fts.js +5 -23
- package/out/candidate/fts.js.map +1 -1
- package/out/candidate/importance.d.ts +60 -65
- package/out/candidate/importance.d.ts.map +1 -1
- package/out/candidate/importance.js +56 -63
- package/out/candidate/importance.js.map +1 -1
- package/out/candidate/lookup.d.ts +18 -55
- package/out/candidate/lookup.d.ts.map +1 -1
- package/out/candidate/lookup.js +82 -385
- package/out/candidate/lookup.js.map +1 -1
- package/out/candidate/name-roles.d.ts +9 -21
- 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 +35 -32
- 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 +27 -62
- package/out/candidate/schema.d.ts.map +1 -1
- package/out/candidate/schema.js +10 -22
- package/out/candidate/schema.js.map +1 -1
- package/out/candidate/scoring.d.ts +7 -8
- package/out/candidate/scoring.d.ts.map +1 -1
- package/out/candidate/scoring.js +52 -89
- package/out/candidate/scoring.js.map +1 -1
- 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 +27 -25
- 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 +72 -50
- package/out/coincident-roles.js.map +1 -1
- package/out/convention/schema.d.ts +14 -14
- 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 +34 -39
- package/out/coverage-manifest-schema.d.ts.map +1 -1
- package/out/coverage-manifest-schema.js +25 -34
- package/out/coverage-manifest-schema.js.map +1 -1
- package/out/currency-backfill.d.ts +16 -38
- package/out/currency-backfill.d.ts.map +1 -1
- package/out/currency-backfill.js +19 -46
- package/out/currency-backfill.js.map +1 -1
- package/out/env.d.ts +1 -1
- 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 +32 -21
- package/out/exact-match.js.map +1 -1
- package/out/extracts.d.ts +66 -55
- 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 +29 -21
- package/out/fst/autocomplete.d.ts.map +1 -1
- package/out/fst/autocomplete.js +39 -28
- package/out/fst/autocomplete.js.map +1 -1
- package/out/fst/builder.d.ts.map +1 -1
- package/out/fst/builder.js +37 -41
- package/out/fst/builder.js.map +1 -1
- package/out/fst/deserialize-web.d.ts +4 -4
- package/out/fst/deserialize-web.d.ts.map +1 -1
- package/out/fst/deserialize-web.js +15 -16
- 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 +19 -81
- package/out/fst/freshness.d.ts.map +1 -1
- package/out/fst/freshness.js +31 -94
- package/out/fst/freshness.js.map +1 -1
- package/out/fst/matcher.d.ts +5 -4
- 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 +28 -28
- package/out/fst/serialize.d.ts.map +1 -1
- package/out/fst/serialize.js +53 -71
- package/out/fst/serialize.js.map +1 -1
- package/out/fst/types.d.ts +78 -74
- 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} +2 -1
- package/out/fst.d.ts.map +1 -0
- package/out/{fst/index.d.ts → fst.js} +2 -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 -62
- package/out/geonames/aliases.d.ts.map +1 -1
- package/out/geonames/aliases.js +45 -117
- package/out/geonames/aliases.js.map +1 -1
- package/out/geonames/postal.d.ts +84 -44
- package/out/geonames/postal.d.ts.map +1 -1
- package/out/geonames/postal.js +115 -73
- 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 +9 -8
- package/out/index.d.ts.map +1 -1
- package/out/index.js +6 -5
- package/out/index.js.map +1 -1
- package/out/interpolation.d.ts +18 -55
- package/out/interpolation.d.ts.map +1 -1
- package/out/interpolation.js +27 -69
- package/out/interpolation.js.map +1 -1
- package/out/lookup.d.ts +23 -51
- package/out/lookup.d.ts.map +1 -1
- package/out/lookup.js +136 -234
- 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 +51 -143
- package/out/place-importance-schema.d.ts.map +1 -1
- package/out/place-importance-schema.js +28 -143
- package/out/place-importance-schema.js.map +1 -1
- package/out/poi/lookup.d.ts +40 -47
- package/out/poi/lookup.d.ts.map +1 -1
- package/out/poi/lookup.js +69 -96
- 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 +20 -23
- package/out/postal/city/alias/lookup.d.ts.map +1 -1
- package/out/postal/city/alias/lookup.js +17 -30
- package/out/postal/city/alias/lookup.js.map +1 -1
- package/out/postal/city/alias/schema.d.ts +14 -10
- package/out/postal/city/alias/schema.d.ts.map +1 -1
- package/out/postal/city/alias/schema.js +10 -8
- package/out/postal/city/alias/schema.js.map +1 -1
- package/out/postal/city/candidate-schema.d.ts +25 -19
- package/out/postal/city/candidate-schema.d.ts.map +1 -1
- package/out/postal/city/candidate-schema.js +21 -17
- package/out/postal/city/candidate-schema.js.map +1 -1
- package/out/{postal/index.d.ts → postal.d.ts} +1 -1
- package/out/postal.d.ts.map +1 -0
- package/out/{postal/index.js → postal.js} +1 -1
- package/out/postal.js.map +1 -0
- package/out/postcode-point-lookup.d.ts +6 -24
- 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 +38 -92
- package/out/primary-preference.d.ts.map +1 -1
- package/out/primary-preference.js +15 -99
- package/out/primary-preference.js.map +1 -1
- package/out/proximity-rerank.d.ts +41 -30
- 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 +98 -64
- 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 +50 -31
- 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 +78 -50
- package/out/reverse.d.ts.map +1 -1
- package/out/reverse.js +98 -61
- package/out/reverse.js.map +1 -1
- package/out/schema.d.ts +72 -49
- 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 +12 -16
- package/out/sqlite/convention-source.d.ts.map +1 -1
- package/out/sqlite/convention-source.js +13 -22
- package/out/sqlite/convention-source.js.map +1 -1
- package/out/sqlite/utils.d.ts +19 -19
- package/out/sqlite/utils.d.ts.map +1 -1
- package/out/sqlite/utils.js +14 -15
- package/out/sqlite/utils.js.map +1 -1
- package/out/street/centroid/schema.d.ts +37 -42
- package/out/street/centroid/schema.d.ts.map +1 -1
- package/out/street/centroid/schema.js +9 -21
- package/out/street/centroid/schema.js.map +1 -1
- package/out/street/centroid.d.ts +30 -0
- package/out/street/centroid.d.ts.map +1 -0
- package/out/street/{centroid/index.js → centroid.js} +13 -24
- package/out/street/centroid.js.map +1 -0
- package/out/street/morphology/fst/builder.d.ts +20 -10
- package/out/street/morphology/fst/builder.d.ts.map +1 -1
- package/out/street/morphology/fst/builder.js +44 -34
- package/out/street/morphology/fst/builder.js.map +1 -1
- package/out/street/morphology/fst/loader.d.ts +33 -26
- package/out/street/morphology/fst/loader.d.ts.map +1 -1
- package/out/street/morphology/fst/loader.js +23 -22
- package/out/street/morphology/fst/loader.js.map +1 -1
- 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 +34 -35
- 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 +77 -194
- 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.js → street.d.ts} +2 -2
- package/out/street.d.ts.map +1 -0
- package/out/{street/index.d.ts → street.js} +2 -2
- package/out/street.js.map +1 -0
- package/out/types.d.ts +84 -149
- 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 +21 -57
- 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 +138 -144
- package/out/weights-overlay-linker.d.ts.map +1 -1
- package/out/weights-overlay-linker.js +105 -159
- package/out/weights-overlay-linker.js.map +1 -1
- package/package.json +91 -596
- package/lib/address/point/index.ts +0 -328
- package/lib/convention/index.ts +0 -154
- package/lib/fts/index.ts +0 -361
- package/out/address/index.d.ts.map +0 -1
- package/out/address/index.js.map +0 -1
- package/out/address/point/index.d.ts +0 -53
- package/out/address/point/index.d.ts.map +0 -1
- package/out/address/point/index.js +0 -222
- package/out/address/point/index.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/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/index.d.ts.map +0 -1
- package/out/postal/index.js.map +0 -1
- package/out/street/centroid/index.d.ts +0 -40
- package/out/street/centroid/index.d.ts.map +0 -1
- package/out/street/centroid/index.js.map +0 -1
- package/out/street/index.d.ts.map +0 -1
- package/out/street/index.js.map +0 -1
- package/out/uprn/index.d.ts.map +0 -1
- package/out/uprn/index.js.map +0 -1
- /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/{postal/index.ts → postal.ts} +0 -0
- /package/lib/{uprn/index.ts → uprn.ts} +0 -0
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
import type { DatabaseClient } from "@mailwoman/sqlite/client"
|
|
9
9
|
|
|
10
|
-
import { placetypeDepth } from "#ancestry
|
|
10
|
+
import { placetypeDepth } from "#ancestry"
|
|
11
11
|
import {
|
|
12
12
|
CANDIDATE_ANCESTOR_COLUMNS,
|
|
13
13
|
CANDIDATE_ANCESTOR_TABLE,
|
|
@@ -21,14 +21,9 @@ import type { CandidateDatabase } from "#candidate/schema"
|
|
|
21
21
|
import type { WOFDatabase } from "#schema"
|
|
22
22
|
|
|
23
23
|
/**
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
* depth)` insert is sorted — the contiguous-leaves discipline of the candidate table itself.
|
|
28
|
-
*
|
|
29
|
-
* Excluded by policy: self rows, and placetypes outside the containment ladder (continent, empire, …: `placetypeDepth` 0)
|
|
30
|
-
* — they discriminate nothing a consumer of this sidecar checks. An edge to a parent with no current `spr` row has no
|
|
31
|
-
* name to denormalize; it is dropped and counted rather than stored blind.
|
|
24
|
+
* Build the ancestors sidecar (closure rows plus interval labels) from the source
|
|
25
|
+
* `ancestors` table, excluding self rows and placetypes outside the containment ladder.
|
|
26
|
+
* an edge to a parent with no current `spr` row is dropped and counted.
|
|
32
27
|
*/
|
|
33
28
|
export async function buildAncestorsSidecar(ctx: {
|
|
34
29
|
src: DatabaseClient<WOFDatabase>
|
|
@@ -47,9 +42,8 @@ export async function buildAncestorsSidecar(ctx: {
|
|
|
47
42
|
`INSERT INTO ${CANDIDATE_ANCESTOR_TABLE} VALUES (${CANDIDATE_ANCESTOR_COLUMNS.map(() => "?").join(", ")})`
|
|
48
43
|
)
|
|
49
44
|
|
|
50
|
-
//
|
|
51
|
-
//
|
|
52
|
-
// convention). ALL parents stay in the closure rows; only the interval tree canonicalizes.
|
|
45
|
+
// One parent per place (the finest containment tier, lowest ancestor id) canonicalizes the interval tree.
|
|
46
|
+
// All parents stay in the closure rows.
|
|
53
47
|
const canonicalParentOf = new Map<number, number>()
|
|
54
48
|
const childrenOf = new Map<number, number[]>()
|
|
55
49
|
const forest = new Set<number>()
|
|
@@ -58,15 +52,15 @@ export async function buildAncestorsSidecar(ctx: {
|
|
|
58
52
|
let ancestorPlaces = 0
|
|
59
53
|
let droppedParents = 0
|
|
60
54
|
|
|
61
|
-
//
|
|
55
|
+
// The stream is grouped by child id, so each flush owns one place.
|
|
62
56
|
let childID = -1
|
|
63
57
|
let edges: Array<{ aid: number; apt: string }> = []
|
|
64
58
|
|
|
65
59
|
const flush = (): void => {
|
|
66
60
|
if (childID < 0 || !edges.length) return
|
|
67
61
|
|
|
68
|
-
// Deterministic nearest-first: containment depth descending, then ancestor id ascending
|
|
69
|
-
// the FTS backend's `ancestorLineage` ordering
|
|
62
|
+
// Deterministic nearest-first: containment depth descending, then ancestor id ascending,
|
|
63
|
+
// matching the FTS backend's `ancestorLineage` ordering across rebuilds.
|
|
70
64
|
edges.sort((a, b) => placetypeDepth(b.apt) - placetypeDepth(a.apt) || a.aid - b.aid)
|
|
71
65
|
|
|
72
66
|
if (edges.length > MAX_ANCESTOR_DEPTH) {
|
|
@@ -132,8 +126,8 @@ export async function buildAncestorsSidecar(ctx: {
|
|
|
132
126
|
flush()
|
|
133
127
|
out.exec("COMMIT")
|
|
134
128
|
|
|
135
|
-
// Interval labels
|
|
136
|
-
// order
|
|
129
|
+
// Interval labels are a pre/post-order DFS over the canonical-parent forest, with root
|
|
130
|
+
// and child order id-ascending so labels stay stable across rebuilds.
|
|
137
131
|
const preOf = new Map<number, number>()
|
|
138
132
|
const postOf = new Map<number, number>()
|
|
139
133
|
|
|
@@ -160,8 +154,8 @@ export async function buildAncestorsSidecar(ctx: {
|
|
|
160
154
|
if (kids && top.next < kids.length) {
|
|
161
155
|
const kid = kids[top.next++]!
|
|
162
156
|
|
|
163
|
-
// Each child holds exactly one canonical parent, so a labeled node
|
|
164
|
-
//
|
|
157
|
+
// Each child holds exactly one canonical parent, so a labeled node means upstream grouping broke.
|
|
158
|
+
// Skip rather than corrupt the numbering.
|
|
165
159
|
if (preOf.has(kid)) continue
|
|
166
160
|
|
|
167
161
|
preOf.set(kid, counter++)
|
|
@@ -173,9 +167,8 @@ export async function buildAncestorsSidecar(ctx: {
|
|
|
173
167
|
}
|
|
174
168
|
}
|
|
175
169
|
|
|
176
|
-
// A canonical-parent
|
|
177
|
-
//
|
|
178
|
-
// absence semantics the schema module states. Counted so a jump is visible across rebuilds.
|
|
170
|
+
// A canonical-parent cycle leaves its members unreachable from any root,
|
|
171
|
+
// so they receive no label and containment against them reads unverifiable.
|
|
179
172
|
const cycleSkipped = forest.size - preOf.size
|
|
180
173
|
|
|
181
174
|
const insInterval = out.prepare(`INSERT INTO ${CANDIDATE_INTERVAL_TABLE} VALUES (?, ?, ?)`)
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
* @copyright Sister Software
|
|
3
3
|
* @license AGPL-3.0
|
|
4
4
|
* @author Teffen Ellis, et al.
|
|
5
|
-
* @file Pass 1b of the candidate build — fold ICU's country display names onto the country rows.
|
|
6
5
|
*/
|
|
7
6
|
|
|
8
7
|
import { enumerateCountryDisplayNames } from "@mailwoman/codex/country"
|
|
@@ -11,22 +10,13 @@ import type { PlaceAttrs, StageRow } from "#candidate/place-attrs"
|
|
|
11
10
|
import { normalizeLocalityForKey } from "#street/normalize"
|
|
12
11
|
|
|
13
12
|
/**
|
|
14
|
-
*
|
|
13
|
+
* Stages each country name that `Intl.DisplayNames` renders in the codex display-name
|
|
14
|
+
* locales as a non-primary key on that country's candidate row.
|
|
15
15
|
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* 237 country rows are synthetic and carry a canonical English name and nothing else; WOF holds no Chinese country
|
|
19
|
-
* names at all; and the GeoNames alias fold filters through a Latin-script regex, so neither existing source could ever
|
|
20
|
-
* supply them.
|
|
16
|
+
* The row's display name is unchanged, so a query such as `格鲁吉亚` resolves to the existing Georgia row.
|
|
17
|
+
* When several country rows share an ISO code, the most populous one receives the keys.
|
|
21
18
|
*
|
|
22
|
-
*
|
|
23
|
-
* every other locale-sensitive operation. No download, no vendored corpus, no snapshot to drift.
|
|
24
|
-
*
|
|
25
|
-
* `is_primary = 0`: these are NAMES THE WORLD USES, not the country's canonical name. The display `name` stays whatever
|
|
26
|
-
* the gazetteer already had, so resolving `格鲁吉亚` answers with the Georgia country row rather than renaming it.
|
|
27
|
-
*
|
|
28
|
-
* Returns the row count so the caller can report it — a zero means ICU supplied nothing, which is a different fact from
|
|
29
|
-
* the pass not having run.
|
|
19
|
+
* @returns The number of rows staged.
|
|
30
20
|
*/
|
|
31
21
|
export function stageCountryDisplayNames(ctx: {
|
|
32
22
|
attrs: Map<number, PlaceAttrs>
|
|
@@ -35,8 +25,6 @@ export function stageCountryDisplayNames(ctx: {
|
|
|
35
25
|
stageRow: StageRow
|
|
36
26
|
tx: { exec(sql: string): void }
|
|
37
27
|
}): number {
|
|
38
|
-
// One country row per ISO2. Where a code has several (historic rows surviving the is_current filter), the most
|
|
39
|
-
// populous wins — the same tiebreak the ranking uses everywhere else.
|
|
40
28
|
const countryByISO2 = new Map<string, { sid: number; a: PlaceAttrs }>()
|
|
41
29
|
|
|
42
30
|
for (const [sid, a] of ctx.attrs) {
|
|
@@ -48,7 +36,7 @@ export function stageCountryDisplayNames(ctx: {
|
|
|
48
36
|
|
|
49
37
|
const held = countryByISO2.get(iso2)
|
|
50
38
|
|
|
51
|
-
if (!held || a.pop > held.a.pop) {
|
|
39
|
+
if (!held || (a.pop ?? 0) > (held.a.pop ?? 0)) {
|
|
52
40
|
countryByISO2.set(iso2, { sid, a })
|
|
53
41
|
}
|
|
54
42
|
}
|
|
@@ -64,8 +52,6 @@ export function stageCountryDisplayNames(ctx: {
|
|
|
64
52
|
|
|
65
53
|
const k = normalizeLocalityForKey(name)
|
|
66
54
|
|
|
67
|
-
// The country's own key is already staged as its primary; INSERT OR IGNORE at materialization dedupes the
|
|
68
|
-
// rest, so this only skips the obvious self-alias.
|
|
69
55
|
if (!k || k === target.a.pkey) continue
|
|
70
56
|
|
|
71
57
|
ctx.stageRow(k, target.a, target.sid, 0)
|
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
* @copyright Sister Software
|
|
3
3
|
* @license AGPL-3.0
|
|
4
4
|
* @author Teffen Ellis, et al.
|
|
5
|
-
* @file Pass 4 of the candidate build — fold a postcode or locality extract into the staging table.
|
|
6
5
|
*/
|
|
7
6
|
|
|
8
7
|
import { DatabaseClient } from "@mailwoman/sqlite/client"
|
|
9
8
|
import { tableExists } from "@mailwoman/sqlite/introspection"
|
|
9
|
+
import type { PathBuilderLike } from "path-ts"
|
|
10
10
|
|
|
11
11
|
import { CANDIDATE_ANCESTOR_COLUMNS, CANDIDATE_ANCESTOR_TABLE } from "#candidate/ancestors/schema"
|
|
12
12
|
import type { PlaceAttrs, StageRow } from "#candidate/place-attrs"
|
|
@@ -14,15 +14,6 @@ import type { CandidateDatabase } from "#candidate/schema"
|
|
|
14
14
|
import type { WOFDatabase } from "#schema"
|
|
15
15
|
import { normalizeLocalityForKey } from "#street/normalize"
|
|
16
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
17
|
function extractRegionAncestry(pc: DatabaseClient<WOFDatabase>, attrs: Map<number, PlaceAttrs>): Map<number, number> {
|
|
27
18
|
const regionOf = new Map<number, number>()
|
|
28
19
|
|
|
@@ -46,28 +37,21 @@ function extractRegionAncestry(pc: DatabaseClient<WOFDatabase>, attrs: Map<numbe
|
|
|
46
37
|
}
|
|
47
38
|
|
|
48
39
|
/**
|
|
49
|
-
*
|
|
50
|
-
*
|
|
40
|
+
* Stages one postcode or locality extract's located `spr` rows and their `names`
|
|
41
|
+
* aliases into the candidate table.
|
|
51
42
|
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
* {@link extractRegionAncestry}), so the pick's stamped ancestors agree with the scope it was found under.
|
|
43
|
+
* When `attrs` is given and the extract's `ancestors` table places a row in a known region,
|
|
44
|
+
* the row also gets that region's scope and the region's ancestor chain.
|
|
45
|
+
* The stored ancestors then agree with the row's scope.
|
|
56
46
|
*/
|
|
57
47
|
export function foldExtract(ctx: {
|
|
58
|
-
/**
|
|
59
|
-
* The staging connection. The extract itself is opened read-only here and closed before returning.
|
|
60
|
-
*/
|
|
61
48
|
out: DatabaseClient<CandidateDatabase>
|
|
62
|
-
extractPath:
|
|
49
|
+
extractPath: PathBuilderLike
|
|
63
50
|
extractPlacetype: "postalcode" | "locality"
|
|
64
51
|
ccID: (code: string | null) => number
|
|
65
52
|
ptID: (pt: string | null) => number
|
|
66
53
|
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
|
-
*/
|
|
54
|
+
|
|
71
55
|
attrs?: Map<number, PlaceAttrs>
|
|
72
56
|
progress: (phase: string, message: string) => void
|
|
73
57
|
}): { primaries: number; aliases: number; scoped: number; ancestorRows: number } {
|
|
@@ -77,8 +61,7 @@ export function foldExtract(ctx: {
|
|
|
77
61
|
|
|
78
62
|
using pc = new DatabaseClient<WOFDatabase>(extractPath, { readOnly: true })
|
|
79
63
|
const pcPtid = ptID(extractPlacetype)
|
|
80
|
-
|
|
81
|
-
// below has nothing to join against unless this primary loop records what it staged.
|
|
64
|
+
|
|
82
65
|
const pcAttrs = new Map<number, PlaceAttrs>()
|
|
83
66
|
const regionOf = ctx.attrs ? extractRegionAncestry(pc, ctx.attrs) : new Map<number, number>()
|
|
84
67
|
|
|
@@ -86,10 +69,6 @@ export function foldExtract(ctx: {
|
|
|
86
69
|
`INSERT OR IGNORE INTO ${CANDIDATE_ANCESTOR_TABLE} VALUES (${CANDIDATE_ANCESTOR_COLUMNS.map(() => "?").join(", ")})`
|
|
87
70
|
)
|
|
88
71
|
|
|
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
72
|
const regionChain = out.prepare(
|
|
94
73
|
`SELECT depth, parent_spr_id, parent_placetype_id, parent_name, parent_name_key FROM ${CANDIDATE_ANCESTOR_TABLE} WHERE spr_id = ? ORDER BY depth`
|
|
95
74
|
)
|
|
@@ -118,9 +97,6 @@ export function foldExtract(ctx: {
|
|
|
118
97
|
const id = Number(r.id)
|
|
119
98
|
const rid = regionOf.get(id) ?? 0
|
|
120
99
|
|
|
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).
|
|
124
100
|
const a: PlaceAttrs = {
|
|
125
101
|
cid: ccID(r.country as string | null),
|
|
126
102
|
rid,
|
|
@@ -135,9 +111,7 @@ export function foldExtract(ctx: {
|
|
|
135
111
|
pop: 0,
|
|
136
112
|
neg: 0,
|
|
137
113
|
pkey: key,
|
|
138
|
-
|
|
139
|
-
// the score source carries no `postalcode` rows to join against anyway. NULL is the truthful
|
|
140
|
-
// value: unmeasured, so the ranking key leaves postcode rows exactly where they were.
|
|
114
|
+
|
|
141
115
|
imp: null,
|
|
142
116
|
}
|
|
143
117
|
|
|
@@ -172,20 +146,6 @@ export function foldExtract(ctx: {
|
|
|
172
146
|
|
|
173
147
|
out.exec("COMMIT")
|
|
174
148
|
|
|
175
|
-
// Fold postcode aliases into the staged candidate names.
|
|
176
|
-
//
|
|
177
|
-
// The delivery-city names GeoNames supplies for a ZIP ("Brooklyn" for 11201) are written into
|
|
178
|
-
// the extract's `names` table by `postcode/centroid-fills.ts`'s `geonamesNameFill`. Everything
|
|
179
|
-
// downstream of `names` picked them up EXCEPT this build: `fts.ts` unions `spr.name` with every
|
|
180
|
-
// `names` row into `place_search.alt_names`, so the FTS backend resolved "Brooklyn" → 11201
|
|
181
|
-
// while the candidate backend — whose every row IS an exact-tier row — had no key for it at
|
|
182
|
-
// all. Pass 2 does the equivalent fold for admin places, but reads the ADMIN `place_search`,
|
|
183
|
-
// and `attrs` holds admin ids only, so a postcode extract could never reach it.
|
|
184
|
-
//
|
|
185
|
-
// Same discipline as pass 2: `is_primary = 0` (so `rankByPrimaryPreference` treats it as an
|
|
186
|
-
// alias, not a canonical postcode name), the row stays denormalized onto the POSTCODE's own
|
|
187
|
-
// spr_id/coords/bbox, and the display `name` stays the postcode — resolving "brooklyn" answers
|
|
188
|
-
// with place 11201, it does not rename the place to its delivery city.
|
|
189
149
|
const hasNames = tableExists(pc, "names")
|
|
190
150
|
|
|
191
151
|
if (hasNames) {
|
|
@@ -198,8 +158,6 @@ export function foldExtract(ctx: {
|
|
|
198
158
|
|
|
199
159
|
const k = normalizeLocalityForKey(String(r.name ?? ""))
|
|
200
160
|
|
|
201
|
-
// The postcode's own key is already staged as the primary; `INSERT OR IGNORE` at
|
|
202
|
-
// materialization dedupes repeats, so this only skips the obvious self-alias.
|
|
203
161
|
if (!k || k === a.pkey) continue
|
|
204
162
|
|
|
205
163
|
stageRow(k, a, Number(r.id), 0)
|
|
@@ -209,9 +167,6 @@ export function foldExtract(ctx: {
|
|
|
209
167
|
|
|
210
168
|
out.exec("COMMIT")
|
|
211
169
|
} else {
|
|
212
|
-
// Never a silent zero: real extracts come from `createUnifiedSchema`, which always creates
|
|
213
|
-
// `names`. A extract without it has no alias surface to lose, but say so rather than reporting
|
|
214
|
-
// "0 aliases" from a table that was never read.
|
|
215
170
|
progress("postcode-aliases", `${extractPath} has no \`names\` table — no delivery-city aliases to fold`)
|
|
216
171
|
}
|
|
217
172
|
|
package/lib/candidate/fts.ts
CHANGED
|
@@ -3,38 +3,20 @@
|
|
|
3
3
|
* @license AGPL-3.0
|
|
4
4
|
* @author Teffen Ellis, et al.
|
|
5
5
|
*
|
|
6
|
-
* FTS5-
|
|
7
|
-
* the exact `name_key` B-tree probe structurally can't do (a misspelling breaks the normalized
|
|
8
|
-
* key, so the contiguous-probe lookup returns nothing). It indexes the NORMALIZED key (not the
|
|
9
|
-
* raw `name`), so a diacritic-stripped query (`munchen`) trigram-matches the stored `munchen`
|
|
10
|
-
* rather than missing a raw `München`. The trigram tokenizer makes MATCH a substring/fuzzy
|
|
11
|
-
* operation; the reader ({@link WOFCandidateTableLookup}) OR's the query's trigrams to fetch a
|
|
12
|
-
* loose set, then re-ranks it with a WORD-level similarity. The trigram index is the candidate
|
|
13
|
-
* GENERATOR, not the scorer — trigram Jaccard scores a true transposition correction BELOW a wrong
|
|
14
|
-
* answer, because shared generic suffixes count as evidence and transpositions count against it.
|
|
15
|
-
* The receipts are on `candidate-lookup.ts`'s `FUZZY_FETCH` and `WORD_FUZZY_MIN`.
|
|
16
|
-
*
|
|
17
|
-
* This is what unifies the two gazetteers: the candidate B-tree stays the common,
|
|
18
|
-
* byte-range-optimal fast path (the browser's contiguous probe), and FTS5 is consulted ONLY on an
|
|
19
|
-
* exact+strip miss — so its scattered postings cost is rare/amortized, and one DB serves both the
|
|
20
|
-
* browser and the server.
|
|
21
|
-
*
|
|
22
|
-
* Raw SQL on purpose: Kysely can't express `CREATE VIRTUAL TABLE … USING fts5` (the repo's
|
|
23
|
-
* FTS5-stays-raw rule). Indexing DISTINCT `name_key` keeps the index to unique normalized forms
|
|
24
|
-
* (a name_key fans out to many candidate rows — placetypes, regions, aliases — but the fuzzy
|
|
25
|
-
* fallback only needs to recover the name_key, then re-probes the B-tree for its rows).
|
|
6
|
+
* FTS5-trigram fuzzy index over the candidate gazetteer's normalized `name_key`; raw SQL on purpose, since Kysely cannot express `create virtual table … using fts5`.
|
|
26
7
|
*/
|
|
27
8
|
|
|
28
9
|
import type { DatabaseClient } from "@mailwoman/sqlite/client"
|
|
29
10
|
|
|
30
11
|
/**
|
|
31
|
-
* Name of the FTS5 trigram virtual table this module owns
|
|
12
|
+
* Name of the FTS5 trigram virtual table this module owns, on which the reader
|
|
13
|
+
* conditions its fuzzy fallback.
|
|
32
14
|
*/
|
|
33
15
|
export const CANDIDATE_FTS_TABLE = "candidate_fts"
|
|
34
16
|
|
|
35
17
|
/**
|
|
36
|
-
* Build (or rebuild) {@link CANDIDATE_FTS_TABLE} from the materialized `candidate` table.
|
|
37
|
-
* B-tree is populated
|
|
18
|
+
* Build (or rebuild) {@link CANDIDATE_FTS_TABLE} from the materialized `candidate` table. call
|
|
19
|
+
* after the candidate B-tree is populated or against an existing candidate DB.
|
|
38
20
|
*/
|
|
39
21
|
export function createCandidateFTS<DB>(db: DatabaseClient<DB>): void {
|
|
40
22
|
db.exec(`DROP TABLE IF EXISTS ${CANDIDATE_FTS_TABLE}`)
|
|
@@ -3,46 +3,35 @@
|
|
|
3
3
|
* @license AGPL-3.0
|
|
4
4
|
* @author Teffen Ellis, et al.
|
|
5
5
|
*
|
|
6
|
-
* The score side of the candidate build
|
|
7
|
-
*
|
|
6
|
+
* The score side of the candidate build: load a WOF admin database's `place_importance` into a
|
|
7
|
+
* lookup the candidate builder probes per place, so every candidate row can include the
|
|
8
8
|
* toponym-fame prior a bare city name is decided on.
|
|
9
9
|
*
|
|
10
|
-
*
|
|
10
|
+
* The join key is `(name_key, country, placetype)`, the same {@link normalizeLocalityForKey} the
|
|
11
|
+
* candidate build uses for its probe key, so the two sides fold identically. An id join is wrong
|
|
12
|
+
* here, because the score source (`admin-global-priority-importance.db`) and the candidate build's
|
|
13
|
+
* admin source are different snapshots whose ids disagree. An id join silently drops rows and
|
|
14
|
+
* leaves the ranking inert on the queries the prior exists for.
|
|
11
15
|
*
|
|
12
|
-
* The
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* them, which means it drops precisely the FOREIGN HOMONYMS the fame prior exists to demote, and it
|
|
18
|
-
* drops them invisibly: the build succeeds, the column is populated, and the ranking is inert on
|
|
19
|
-
* the queries it was built for.
|
|
16
|
+
* The key by itself is not enough. `(warwick, US, locality)` covers eleven different places.
|
|
17
|
+
* The group's maximum would give every Warwick in America the fame of Warwick, Rhode Island.
|
|
18
|
+
* The join picks the nearest centroid when it falls within {@link IMPORTANCE_JOIN_RADIUS_KM}.
|
|
19
|
+
* Two artifacts describing the same settlement put its centroid
|
|
20
|
+
* in almost the same place. Two towns with the same name in one country do not.
|
|
20
21
|
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
22
|
+
* `place_importance.importance` lands in the column verbatim, the pre-split conflation that is
|
|
23
|
+
* encyclopedia-derived where the concordance matched and a population-derived proxy everywhere
|
|
24
|
+
* else. See {@link CandidateTable.importance} for why the split `encyclopedic` channel is not what
|
|
25
|
+
* is written here.
|
|
23
26
|
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
* Warwick in America the fame of Warwick, Rhode Island, which is the fan-out defect
|
|
28
|
-
* `importance-fanout.ts` documents one layer up, re-introduced at the join. So the group is
|
|
29
|
-
* disambiguated GEOGRAPHICALLY: the nearest centroid wins, and only within
|
|
30
|
-
* {@link IMPORTANCE_JOIN_RADIUS_KM}. Two artifacts describing the same settlement put its centroid in
|
|
31
|
-
* almost the same place; two same-named towns in one country do not.
|
|
32
|
-
*
|
|
33
|
-
* ## What lands in the column
|
|
34
|
-
*
|
|
35
|
-
* `place_importance.importance` VERBATIM — the pre-split conflation (encyclopedia-derived where
|
|
36
|
-
* the concordance matched, a population-derived proxy everywhere else). See
|
|
37
|
-
* {@link CandidateTable.importance} for why the split `encyclopedic` channel is deliberately NOT
|
|
38
|
-
* what is written here, with the measurement that settled it.
|
|
39
|
-
*
|
|
40
|
-
* A place with no match gets NULL. NULL is UNMEASURED, never zero — the consumer
|
|
41
|
-
* (`resolver/toponym-prior.ts`) leaves an unmeasured candidate exactly where population put it.
|
|
27
|
+
* A place with no match gets NULL. NULL means the place's fame is unmeasured.
|
|
28
|
+
* The consumer (`resolver/toponym-prior.ts`) leaves an unmeasured candidate at the position
|
|
29
|
+
* population assigned it.
|
|
42
30
|
*/
|
|
43
31
|
|
|
44
32
|
import { haversineKm } from "@mailwoman/spatial"
|
|
45
33
|
import { DatabaseClient } from "@mailwoman/sqlite/client"
|
|
34
|
+
import type { PathBuilderLike } from "path-ts"
|
|
46
35
|
|
|
47
36
|
import type { CandidateDatabase } from "#candidate/schema"
|
|
48
37
|
import { normalizeLocalityForKey } from "#street/normalize"
|
|
@@ -50,26 +39,20 @@ import { normalizeLocalityForKey } from "#street/normalize"
|
|
|
50
39
|
/**
|
|
51
40
|
* How far apart two artifacts may put the same place's centroid and still be read as the same place.
|
|
52
41
|
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
* snapshots agree to the bit — and 656,755 (64.4%) are inside 500 m.
|
|
42
|
+
* Measured from two admin snapshots.
|
|
43
|
+
* The nearest-centroid distances separate into a mode where the two snapshots agree
|
|
44
|
+
* and a background rate of two different towns wearing one name in one country.
|
|
57
45
|
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
* places/km from 30 km out to 100 km and beyond. That plateau is the background rate of two DIFFERENT towns wearing one
|
|
61
|
-
* name in one country, and it does not decay with distance because there is no reason it should. 10 km is the floor
|
|
62
|
-
* between the two populations. Admitting it scores 679,163 places (66.6% of the matched set); pushing the radius to 25
|
|
63
|
-
* km buys 8,722 more, and by then better than half of each additional kilometre is the wrong town.
|
|
46
|
+
* The background rate does not decay with distance.
|
|
47
|
+
* Ten kilometers separates the two populations.
|
|
64
48
|
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
* of "this is the same place"; widening it past the floor starts handing one town's fame to another.
|
|
49
|
+
* That distance matches the join's purpose.
|
|
50
|
+
* A radius above the floor starts handing one town's fame to another.
|
|
68
51
|
*/
|
|
69
52
|
export const IMPORTANCE_JOIN_RADIUS_KM = 10
|
|
70
53
|
|
|
71
54
|
/**
|
|
72
|
-
* One scored place from the source
|
|
55
|
+
* One scored place from the source, with its coordinates and importance score.
|
|
73
56
|
*/
|
|
74
57
|
interface ScoredPlace {
|
|
75
58
|
lat: number
|
|
@@ -78,8 +61,10 @@ interface ScoredPlace {
|
|
|
78
61
|
}
|
|
79
62
|
|
|
80
63
|
/**
|
|
81
|
-
* What {@link loadImportanceIndex} measured while reading the source.
|
|
82
|
-
*
|
|
64
|
+
* What {@link loadImportanceIndex} measured while reading the source.
|
|
65
|
+
*
|
|
66
|
+
* Reported by the builder so a run reports how much of the gazetteer it actually scored,
|
|
67
|
+
* rather than leaving the caller to infer it from a column full of nulls.
|
|
83
68
|
*/
|
|
84
69
|
export interface ImportanceIndexStats {
|
|
85
70
|
/**
|
|
@@ -91,14 +76,17 @@ export interface ImportanceIndexStats {
|
|
|
91
76
|
*/
|
|
92
77
|
keys: number
|
|
93
78
|
/**
|
|
94
|
-
* Places whose name folded to the empty key and could never be joined
|
|
79
|
+
* Places whose name folded to the empty key and could never be joined
|
|
80
|
+
* (non-Latin punctuation-only names, blanks).
|
|
95
81
|
*/
|
|
96
82
|
unkeyable: number
|
|
97
83
|
}
|
|
98
84
|
|
|
99
85
|
/**
|
|
100
|
-
* `(name_key, country, placetype)` → the scored places under it.
|
|
101
|
-
*
|
|
86
|
+
* `(name_key, country, placetype)` → the scored places under it.
|
|
87
|
+
*
|
|
88
|
+
* The separator is U+0000, a character no WOF name contains and no fold can produce,
|
|
89
|
+
* so the three fields cannot smear into one another.
|
|
102
90
|
*/
|
|
103
91
|
function groupKey(nameKey: string, country: string | null, placetype: string | null): string {
|
|
104
92
|
return `${nameKey}\u0000${(country ?? "").toUpperCase()}\u0000${placetype ?? ""}`
|
|
@@ -115,11 +103,15 @@ export class ImportanceIndex {
|
|
|
115
103
|
*/
|
|
116
104
|
matched = 0
|
|
117
105
|
/**
|
|
118
|
-
* Places {@link find}
|
|
106
|
+
* Places {@link find} refused.
|
|
107
|
+
*
|
|
108
|
+
* The key matched a scored group, but the nearest member was outside the radius,
|
|
119
109
|
* so it is a different place wearing the same name.
|
|
120
110
|
*
|
|
121
|
-
* This is the number worth watching across rebuilds.
|
|
122
|
-
*
|
|
111
|
+
* This is the number worth watching across rebuilds.
|
|
112
|
+
* A jump means the score source and the admin source have drifted apart
|
|
113
|
+
* and the join is being asked to guess.
|
|
114
|
+
* It does not mean the radius is too tight.
|
|
123
115
|
*/
|
|
124
116
|
refused = 0
|
|
125
117
|
|
|
@@ -129,12 +121,15 @@ export class ImportanceIndex {
|
|
|
129
121
|
}
|
|
130
122
|
|
|
131
123
|
/**
|
|
132
|
-
* The importance of the scored place nearest `(lat, lon)` sharing `name`'s folded key, `country`
|
|
133
|
-
* null when there is no such place within {@link IMPORTANCE_JOIN_RADIUS_KM}.
|
|
124
|
+
* The importance of the scored place nearest `(lat, lon)` sharing `name`'s folded key, `country`
|
|
125
|
+
* and `placetype`, or null when there is no such place within {@link IMPORTANCE_JOIN_RADIUS_KM}.
|
|
126
|
+
*
|
|
127
|
+
* Null is unmeasured.
|
|
128
|
+
* Keep the value null.
|
|
129
|
+
* Do not substitute zero or a population-derived value here.
|
|
134
130
|
*
|
|
135
|
-
*
|
|
136
|
-
*
|
|
137
|
-
* indistinguishable from a measurement.
|
|
131
|
+
* The source column already contains a population-derived fallback where one exists.
|
|
132
|
+
* A second fallback would make an absence indistinguishable from a measurement.
|
|
138
133
|
*/
|
|
139
134
|
find(name: string, country: string | null, placetype: string | null, lat: number, lon: number): number | null {
|
|
140
135
|
const nameKey = normalizeLocalityForKey(name)
|
|
@@ -145,7 +140,7 @@ export class ImportanceIndex {
|
|
|
145
140
|
|
|
146
141
|
if (!group) return null
|
|
147
142
|
|
|
148
|
-
let best: ScoredPlace |
|
|
143
|
+
let best: ScoredPlace | null = null
|
|
149
144
|
let bestKm = Infinity
|
|
150
145
|
|
|
151
146
|
for (const place of group) {
|
|
@@ -170,18 +165,20 @@ export class ImportanceIndex {
|
|
|
170
165
|
}
|
|
171
166
|
|
|
172
167
|
/**
|
|
173
|
-
* Read `place_importance`
|
|
174
|
-
*
|
|
168
|
+
* Read `place_importance` from a WOF admin database and join it to `spr`.
|
|
169
|
+
*
|
|
170
|
+
* The join retrieves each place's name, country, placetype, plus its centroid.
|
|
171
|
+
* These fields populate an {@link ImportanceIndex}.
|
|
172
|
+
*
|
|
173
|
+
* Only current, non-deprecated places are indexed.
|
|
174
|
+
* A superseded row's score belongs to a place the gazetteer no longer contains.
|
|
175
175
|
*
|
|
176
|
-
*
|
|
177
|
-
* carries, and letting it win the nearest-centroid contest would hand a live place a dead one's fame.
|
|
176
|
+
* If that row won the nearest-centroid contest, a live place would inherit the superseded place's fame.
|
|
178
177
|
*
|
|
179
|
-
* The whole table is held in memory on purpose
|
|
180
|
-
* and the
|
|
181
|
-
* against a 3.7 GB database. Measured end to end, loading the index plus probing all 4.48 M locality-tier places takes
|
|
182
|
-
* 25 s.
|
|
178
|
+
* The whole table is held in memory on purpose, because the build probes it once for every place
|
|
179
|
+
* and the alternative is a prepared statement per place against a multi-gigabyte database.
|
|
183
180
|
*/
|
|
184
|
-
export function loadImportanceIndex(databasePath:
|
|
181
|
+
export function loadImportanceIndex(databasePath: PathBuilderLike): ImportanceIndex {
|
|
185
182
|
using db = new DatabaseClient<CandidateDatabase>(databasePath, { readOnly: true })
|
|
186
183
|
|
|
187
184
|
const groups = new Map<string, ScoredPlace[]>()
|