@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
package/lib/lookup.ts
CHANGED
|
@@ -3,21 +3,18 @@
|
|
|
3
3
|
* @license AGPL-3.0
|
|
4
4
|
* @author Teffen Ellis, et al.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
* query layer where the queries are non-trivial, and raw SQL where they aren't (FTS5 MATCH, the
|
|
8
|
-
* FTS index build).
|
|
9
|
-
*
|
|
10
|
-
* See `docs/plan/phases/PHASE_4_2_wof_sqlite.md` for the design rationale.
|
|
6
|
+
* `WOFSQLitePlaceLookup` — the resolver implementation backed by `node:sqlite` plus a Kysely-typed query layer where the queries are non-trivial and raw SQL where they are not.
|
|
11
7
|
*/
|
|
12
8
|
|
|
13
9
|
import { expandPlacetypeFilter } from "@mailwoman/codex/placetype-map"
|
|
14
10
|
import type { Ancestor, CoincidentLocality } from "@mailwoman/core/resolver"
|
|
15
11
|
import { allRows } from "@mailwoman/core/utils"
|
|
16
12
|
import { haversineKm } from "@mailwoman/spatial"
|
|
17
|
-
import type { SQLInputValue } from "@mailwoman/sqlite/client"
|
|
18
|
-
import {
|
|
13
|
+
import type { DatabaseClient, SQLInputValue } from "@mailwoman/sqlite/client"
|
|
14
|
+
import { SQLiteLookup } from "@mailwoman/sqlite/lookup"
|
|
15
|
+
import type { PathBuilderLike } from "path-ts"
|
|
19
16
|
|
|
20
|
-
import { ancestorLineage } from "#ancestry
|
|
17
|
+
import { ancestorLineage } from "#ancestry"
|
|
21
18
|
import { candidateFromSearchRow, rankCandidates } from "#candidate/scoring"
|
|
22
19
|
import { loadCoincidentLocalities } from "#coincident-roles"
|
|
23
20
|
import {
|
|
@@ -28,7 +25,7 @@ import {
|
|
|
28
25
|
type ConventionSource,
|
|
29
26
|
type ResolvedConvention,
|
|
30
27
|
type Strategy,
|
|
31
|
-
} from "#convention
|
|
28
|
+
} from "#convention"
|
|
32
29
|
import {
|
|
33
30
|
pickExtractForPlacetype,
|
|
34
31
|
pickExtractsForPlacetype,
|
|
@@ -44,7 +41,7 @@ import {
|
|
|
44
41
|
placeBboxExists,
|
|
45
42
|
placePopulationExists,
|
|
46
43
|
placeSearchFTSExists,
|
|
47
|
-
} from "#fts
|
|
44
|
+
} from "#fts"
|
|
48
45
|
import { normalizePlacetypes, sanitizeFTSQuery } from "#fts/query"
|
|
49
46
|
import { cfNormalize, softNameScore } from "#name-score"
|
|
50
47
|
import { encyclopedicClauses } from "#place-importance-schema"
|
|
@@ -57,61 +54,39 @@ import type { FindPlaceQuery, PlaceCandidate, PlaceLookup, WOFPlacetype } from "
|
|
|
57
54
|
|
|
58
55
|
export interface WOFSQLitePlaceLookupOpts {
|
|
59
56
|
/**
|
|
60
|
-
* Path to the WOF SQLite distribution on disk
|
|
61
|
-
*
|
|
62
|
-
* **Single string** — opens that one DB as the main extract.
|
|
63
|
-
*
|
|
64
|
-
* **Array** — opens the first entry as main, then ATTACHes each subsequent entry as a separate SQLite schema. Schema
|
|
65
|
-
* names are derived from the filename (`whosonfirst-data-postalcode- us-latest.db` → `postalcode_us`); override with
|
|
66
|
-
* `ExtractConfig.schemaName` when the filename doesn't follow WOF convention. See `extracts.ts` for the derivation
|
|
67
|
-
* rules.
|
|
68
|
-
*
|
|
69
|
-
* Routing: queries with a `placetype` matching a extract's name (or explicit `placetypes` hint) are sent to that
|
|
70
|
-
* extract; everything else hits main. Cross-extract UNION is NOT done — BM25 isn't comparable across
|
|
71
|
-
* separately-indexed corpora.
|
|
57
|
+
* Path to the WOF SQLite distribution on disk, mutually exclusive with `database`;
|
|
58
|
+
* an array opens the first entry as main and ATTACHes the rest.
|
|
72
59
|
*/
|
|
73
|
-
databasePath?:
|
|
60
|
+
databasePath?: PathBuilderLike | ReadonlyArray<PathBuilderLike | ExtractConfig>
|
|
74
61
|
/**
|
|
75
|
-
* Pre-opened connection
|
|
76
|
-
* Multi-extract requires `databasePath` (so the lookup owns the ATTACH).
|
|
62
|
+
* Pre-opened connection, mutually exclusive with `databasePath`; multi-extract requires `databasePath`.
|
|
77
63
|
*/
|
|
78
64
|
database?: DatabaseClient<WOFDatabase>
|
|
79
65
|
/**
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
* operator-side CLI documented in the README. Default false — the resolver assumes the index already exists and
|
|
83
|
-
* errors loudly if it doesn't.
|
|
66
|
+
* When true, build the FTS5 `place_search` virtual table on construction if
|
|
67
|
+
* it is missing, on the main extract only.
|
|
84
68
|
*
|
|
85
|
-
*
|
|
86
|
-
* pre-built via `mailwoman gazetteer build fts` — operator script for predictable cost.
|
|
69
|
+
* @defaultValue `false`
|
|
87
70
|
*/
|
|
88
71
|
buildFTS?: boolean
|
|
89
72
|
/**
|
|
90
|
-
* Geographic Rule Engine convention source
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
*
|
|
73
|
+
* Geographic Rule Engine convention source, either a ready `ConventionSource`
|
|
74
|
+
* or a `{ wofID: Convention }` seed map.
|
|
75
|
+
*
|
|
76
|
+
* @defaultValue `[]`. An empty list resolves every query to `WORLD_DEFAULT`.
|
|
94
77
|
*/
|
|
95
78
|
conventions?: ConventionSource | Record<number, Convention>
|
|
96
79
|
/**
|
|
97
|
-
* Opt-in postal-city alias reader
|
|
98
|
-
*
|
|
99
|
-
*
|
|
100
|
-
* byte-identical — every alias code path is conditioned on this being non-null, so an unprovided reader changes no
|
|
101
|
-
* score.
|
|
80
|
+
* Opt-in postal-city alias reader.
|
|
81
|
+
*
|
|
82
|
+
* Absent, every alias code path is skipped and the resolver is byte-identical.
|
|
102
83
|
*/
|
|
103
84
|
postalCityAliases?: WOFPostalCityAliasLookup
|
|
104
85
|
}
|
|
105
86
|
|
|
106
87
|
/**
|
|
107
|
-
* The
|
|
108
|
-
*
|
|
109
|
-
* - Nearby localities with WOF alt-name aliases.
|
|
110
|
-
*/
|
|
111
|
-
/**
|
|
112
|
-
* The placetypes `pickExtractsForPlacetype`'s substring rule can route by name. Not every WOF placetype — only the ones
|
|
113
|
-
* a purpose-built extract is ever named for — so the diagnostic below can say "this name routes nowhere" without
|
|
114
|
-
* claiming to enumerate the gazetteer.
|
|
88
|
+
* The placetypes `pickExtractsForPlacetype`'s substring rule can route by name,
|
|
89
|
+
* a subset of the WOF placetypes.
|
|
115
90
|
*/
|
|
116
91
|
const KNOWN_ROUTED_PLACETYPES: ReadonlyArray<string> = [
|
|
117
92
|
"postalcode",
|
|
@@ -125,86 +100,71 @@ const KNOWN_ROUTED_PLACETYPES: ReadonlyArray<string> = [
|
|
|
125
100
|
const POSTCODE_LOCALITY_TABLE = "postcode_locality"
|
|
126
101
|
|
|
127
102
|
/**
|
|
128
|
-
* Tunables for the coordinate-first locality soft-score
|
|
129
|
-
*
|
|
130
|
-
* EU values), so a locale can retune them as data. PC_DECAY_KM sets how fast S_pc falls with distance.
|
|
103
|
+
* Tunables for the coordinate-first locality soft-score
|
|
104
|
+
* `Score = pc·S_pc + name·S_name + pop·S_pop` (each S in [0,1]).
|
|
131
105
|
*/
|
|
132
106
|
const CF_PC_DECAY_KM = 8
|
|
133
107
|
/**
|
|
134
|
-
* The chosen locality must be within this distance of the postcode's containing
|
|
135
|
-
*
|
|
136
|
-
* Generous enough that a city-state Ortsteil (~15km from the city centroid) and an abutting town (~few km) are NOT
|
|
137
|
-
* flagged, tight enough to catch a wrong city (hundreds of km).
|
|
108
|
+
* The chosen locality must be within this distance of the postcode's containing
|
|
109
|
+
* locality or the `mismatch` flag fires.
|
|
138
110
|
*/
|
|
139
111
|
const CF_MISMATCH_KM = 50
|
|
140
112
|
|
|
141
|
-
export class WOFSQLitePlaceLookup implements PlaceLookup
|
|
142
|
-
readonly #db: DatabaseClient<WOFDatabase>
|
|
143
|
-
/**
|
|
144
|
-
* Resources this instance opened. A connection handed in by a caller is NOT in here, so disposal cannot reach it —
|
|
145
|
-
* ownership is membership rather than a flag a later branch has to check.
|
|
146
|
-
*/
|
|
147
|
-
readonly #resources = new DisposableStack()
|
|
113
|
+
export class WOFSQLitePlaceLookup extends SQLiteLookup<WOFDatabase> implements PlaceLookup {
|
|
148
114
|
readonly #weights: RankingWeights
|
|
149
115
|
/**
|
|
150
|
-
* Cached at construction
|
|
151
|
-
* fall back to no-filter when this is false, preserving compatibility with DBs that were FTS-built before the R*Tree
|
|
152
|
-
* shipped.
|
|
116
|
+
* Cached at construction.
|
|
153
117
|
*
|
|
154
|
-
*
|
|
118
|
+
* An extract is considered to have the bbox index only if its own R*Tree table exists.
|
|
155
119
|
*/
|
|
156
120
|
readonly #hasBboxIndex: Map<string, boolean>
|
|
157
121
|
/**
|
|
158
|
-
* Per-extract probe for the `place_population` aux table.
|
|
159
|
-
*
|
|
122
|
+
* Per-extract probe for the `place_population` aux table.
|
|
123
|
+
*
|
|
124
|
+
* When false, the left join is omitted and the population boost is 0 for every row.
|
|
160
125
|
*/
|
|
161
126
|
readonly #hasPopulationIndex: Map<string, boolean>
|
|
162
127
|
/**
|
|
163
|
-
* Per-extract
|
|
164
|
-
* built once at construction.
|
|
165
|
-
* extract today. See {@link encyclopedicClauses} for why the probe is a column and not a table.
|
|
128
|
+
* Per-extract select term and left join for the two-score split's `encyclopedic` value,
|
|
129
|
+
* probed and built once at construction.
|
|
166
130
|
*/
|
|
167
131
|
readonly #encyclopedicClauses: Map<string, { select: string; join: string }>
|
|
168
132
|
/**
|
|
169
|
-
* Per-extract probe for the `postcode_locality` table
|
|
170
|
-
*
|
|
171
|
-
* silently no-ops on a deployment that didn't ship the table.
|
|
133
|
+
* Per-extract probe for the `postcode_locality` table, cached at construction
|
|
134
|
+
* and null when absent so the coord-first path no-ops.
|
|
172
135
|
*/
|
|
173
136
|
readonly #postcodeLocalityExtract: string | null
|
|
174
137
|
/**
|
|
175
|
-
* Resolved extract list
|
|
176
|
-
* (or override) schema names.
|
|
138
|
+
* Resolved extract list, always at least one entry with `main` first.
|
|
177
139
|
*/
|
|
178
140
|
readonly #extracts: ResolvedExtract[]
|
|
179
141
|
/**
|
|
180
|
-
*
|
|
142
|
+
* Per-schema probed country sets for country-aware extract routing, non-main extracts only.
|
|
181
143
|
*/
|
|
182
144
|
readonly #extractCountries: Map<string, ReadonlySet<string>>
|
|
183
145
|
/**
|
|
184
|
-
*
|
|
185
|
-
*
|
|
186
|
-
*
|
|
187
|
-
* country-code
|
|
188
|
-
* cached).
|
|
146
|
+
* `#conventionSource` supplies per-WOF-polygon profiles for the Geographic Rule Engine.
|
|
147
|
+
*
|
|
148
|
+
* `#strategies` maps primitive names to strategies.
|
|
149
|
+
* `#countryWOFIdCache` memoizes country-code to country-WOF-id lookups.
|
|
189
150
|
*/
|
|
190
151
|
readonly #conventionSource: ConventionSource
|
|
191
152
|
readonly #strategies: Map<string, Strategy>
|
|
192
153
|
readonly #countryWOFIdCache = new Map<string, number | null>()
|
|
193
154
|
/**
|
|
194
|
-
* Strategy names already warned about
|
|
155
|
+
* Strategy names already warned about, so an unknown name surfaces once.
|
|
195
156
|
*/
|
|
196
157
|
readonly #warnedUnknownStrategies = new Set<string>()
|
|
197
158
|
/**
|
|
198
|
-
* Lazily-built `admin_id → coincident localities` map
|
|
159
|
+
* Lazily-built `admin_id → coincident localities` map, null until first use.
|
|
199
160
|
*/
|
|
200
161
|
#coincidentRolesCache: Map<number, CoincidentLocality[]> | null = null
|
|
201
162
|
/**
|
|
202
|
-
* Per-id memoized ancestor lineages
|
|
163
|
+
* Per-id memoized ancestor lineages, queried once per chain.
|
|
203
164
|
*/
|
|
204
165
|
readonly #ancestorsCache = new Map<number, Ancestor[]>()
|
|
205
166
|
/**
|
|
206
|
-
* Opt-in postal-city alias reader
|
|
207
|
-
* is conditioned on this, so the default resolver is byte-identical.
|
|
167
|
+
* Opt-in postal-city alias reader, `null` unless supplied, so every alias path is skipped by default.
|
|
208
168
|
*/
|
|
209
169
|
readonly #postalCityAliases: WOFPostalCityAliasLookup | null
|
|
210
170
|
|
|
@@ -217,29 +177,26 @@ export class WOFSQLitePlaceLookup implements PlaceLookup, Disposable {
|
|
|
217
177
|
throw new Error("WOFSQLitePlaceLookup: one of `database` or `databasePath` is required")
|
|
218
178
|
}
|
|
219
179
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
this.#db.exec(`ATTACH DATABASE '${s.path.replaceAll("'", "''")}' AS ${s.schemaName}`)
|
|
238
|
-
}
|
|
180
|
+
const extracts: ResolvedExtract[] = opts.database
|
|
181
|
+
? [{ path: ":memory:", schemaName: "main", placetypes: [] }]
|
|
182
|
+
: resolveExtracts(opts.databasePath!)
|
|
183
|
+
|
|
184
|
+
// Read-only by default: shipped extracts are sealed 0444 and Docker `:ro`
|
|
185
|
+
// mounts forbid a write-mode open.
|
|
186
|
+
// Only `buildFTS` opens writable.
|
|
187
|
+
super(opts.database ? { database: opts.database } : { databasePath: extracts[0]!.path }, {
|
|
188
|
+
readOnly: !opts.buildFTS,
|
|
189
|
+
})
|
|
190
|
+
|
|
191
|
+
this.#extracts = extracts
|
|
192
|
+
|
|
193
|
+
// Schema names were validated by resolveExtracts, so interpolating them is safe.
|
|
194
|
+
// SQLite attach accepts no parameter for a schema name.
|
|
195
|
+
for (const s of extracts.slice(1)) {
|
|
196
|
+
this.database.exec(`ATTACH DATABASE '${s.path.replaceAll("'", "''")}' AS ${s.schemaName}`)
|
|
239
197
|
}
|
|
240
198
|
|
|
241
|
-
|
|
242
|
-
this.#db.exec("PRAGMA busy_timeout = 5000")
|
|
199
|
+
this.database.exec("PRAGMA busy_timeout = 5000")
|
|
243
200
|
|
|
244
201
|
if (opts.buildFTS) {
|
|
245
202
|
this.#ensureFTS()
|
|
@@ -249,8 +206,6 @@ export class WOFSQLitePlaceLookup implements PlaceLookup, Disposable {
|
|
|
249
206
|
|
|
250
207
|
this.#weights = { ...DEFAULT_WEIGHTS, ...weights }
|
|
251
208
|
|
|
252
|
-
// Probe each extract's aux-table presence — driven by per-extract table existence in
|
|
253
|
-
// sqlite_master. Cached at construction so findPlace doesn't query sqlite_master per call.
|
|
254
209
|
this.#hasBboxIndex = new Map()
|
|
255
210
|
this.#hasPopulationIndex = new Map()
|
|
256
211
|
this.#encyclopedicClauses = new Map()
|
|
@@ -258,25 +213,12 @@ export class WOFSQLitePlaceLookup implements PlaceLookup, Disposable {
|
|
|
258
213
|
for (const s of this.#extracts) {
|
|
259
214
|
this.#hasBboxIndex.set(s.schemaName, this.#extractHasTable(s.schemaName, PLACE_BBOX_TABLE))
|
|
260
215
|
this.#hasPopulationIndex.set(s.schemaName, this.#extractHasTable(s.schemaName, PLACE_POPULATION_TABLE))
|
|
261
|
-
this.#encyclopedicClauses.set(s.schemaName, encyclopedicClauses(this
|
|
216
|
+
this.#encyclopedicClauses.set(s.schemaName, encyclopedicClauses(this.database, s.schemaName))
|
|
262
217
|
}
|
|
263
218
|
|
|
264
|
-
//
|
|
265
|
-
//
|
|
266
|
-
//
|
|
267
|
-
// unroutable half is the worse of the two — a extract reaches routing only through the name
|
|
268
|
-
// `deriveSchemaName` derives from its FILENAME, so a file spelled one letter off the placetype it
|
|
269
|
-
// serves answers with nothing while holding every row that was asked for.
|
|
270
|
-
//
|
|
271
|
-
// Two independent things bring a extract under the guard, and it needs both. Carrying `spr` is a
|
|
272
|
-
// CLAIM to be a place extract. Carrying a name that routes is an INVITATION to be queried as one, and
|
|
273
|
-
// it is made by the filename alone — so a database with no tables at all still gets picked, still
|
|
274
|
-
// answers no query, and still dies inside a SELECT. Testing only the claim lets an empty or
|
|
275
|
-
// truncated file past construction; testing only the name would exempt a correctly-named build
|
|
276
|
-
// input. A extract needs to fail neither test to be exempt.
|
|
277
|
-
//
|
|
278
|
-
// Exempt by design: `postcode-locality-<cc>.db` carries a relation table and nothing else, matches
|
|
279
|
-
// no routed placetype, and is part of the documented default extract list.
|
|
219
|
+
// An extract is guarded when its path identifies a routed placetype
|
|
220
|
+
// or it contains `spr`, and must then contain `place_search`; testing only one of
|
|
221
|
+
// those would let an empty file through or exempt a build input.
|
|
280
222
|
for (const s of this.#extracts) {
|
|
281
223
|
if (s.schemaName === "main") continue
|
|
282
224
|
|
|
@@ -304,42 +246,31 @@ export class WOFSQLitePlaceLookup implements PlaceLookup, Disposable {
|
|
|
304
246
|
)
|
|
305
247
|
}
|
|
306
248
|
|
|
307
|
-
//
|
|
308
|
-
//
|
|
309
|
-
// multi-GB admin DB and is the fallback anyway, so it is deliberately NOT scanned). Feeds
|
|
310
|
-
// pickExtractForPlacetype so two postcode extracts (postalcode-us + postalcode-geonames-tail)
|
|
311
|
-
// route by the query's country instead of first-match starving the second extract.
|
|
249
|
+
// Probe each non-main extract's country set once at construction so two postcode
|
|
250
|
+
// extracts route by the query's country instead of first-match starving the second.
|
|
312
251
|
this.#extractCountries = new Map()
|
|
313
252
|
|
|
314
253
|
for (const sh of this.#extracts) {
|
|
315
254
|
if (sh.schemaName === "main") continue
|
|
316
255
|
|
|
317
256
|
try {
|
|
318
|
-
const rows = this
|
|
257
|
+
const rows = this.database
|
|
319
258
|
.prepare(`SELECT DISTINCT country FROM ${sh.schemaName}.spr WHERE country != ''`)
|
|
320
259
|
.all() as Array<{ country: string }>
|
|
321
260
|
|
|
322
261
|
this.#extractCountries.set(sh.schemaName, new Set(rows.map((r) => r.country)))
|
|
323
262
|
} catch {
|
|
324
|
-
//
|
|
263
|
+
// An extract without spr (or an attach oddity) just doesn't participate in country routing.
|
|
325
264
|
}
|
|
326
265
|
}
|
|
327
266
|
|
|
328
|
-
// The postcode_locality table can live on any attached extract (typically its own
|
|
329
|
-
// `postcode-locality-<cc>.db`). Find the first extract that has it; null = coord-first disabled.
|
|
330
267
|
this.#postcodeLocalityExtract =
|
|
331
268
|
this.#extracts.find((s) => this.#extractHasTable(s.schemaName, POSTCODE_LOCALITY_TABLE))?.schemaName ?? null
|
|
332
269
|
|
|
333
|
-
// Opt-in postal-city alias reader (#475). Construction-time present-or-not is the check: null
|
|
334
|
-
// keeps the coordinate-first scorer byte-identical to pre-#475.
|
|
335
270
|
this.#postalCityAliases = opts.postalCityAliases ?? null
|
|
336
271
|
|
|
337
|
-
//
|
|
338
|
-
//
|
|
339
|
-
// attached (auto-detected, like the postcode_locality extract — adding conventions.db to
|
|
340
|
-
// databasePath enables it; queried on demand, not paged into memory); else empty, so EU rides
|
|
341
|
-
// WORLD_DEFAULT. The registry binds strategy NAMES to the SQL-bound primitives — adding a
|
|
342
|
-
// strategy is registering it here.
|
|
272
|
+
// Precedence: explicit `opts.conventions` wins, else an attached build-from-source
|
|
273
|
+
// convention asset, else empty so EU uses `WORLD_DEFAULT`.
|
|
343
274
|
const conventionExtract =
|
|
344
275
|
this.#extracts.find((s) => this.#extractHasTable(s.schemaName, ADDRESS_CONVENTION_TABLE))?.schemaName ?? null
|
|
345
276
|
|
|
@@ -348,7 +279,7 @@ export class WOFSQLitePlaceLookup implements PlaceLookup, Disposable {
|
|
|
348
279
|
? opts.conventions
|
|
349
280
|
: new SeedConventionSource(opts.conventions as Record<number, Convention>)
|
|
350
281
|
: conventionExtract
|
|
351
|
-
? new SqliteConventionSource(this
|
|
282
|
+
? new SqliteConventionSource(this.database, conventionExtract)
|
|
352
283
|
: new SeedConventionSource()
|
|
353
284
|
|
|
354
285
|
this.#strategies = new Map<string, Strategy>([
|
|
@@ -358,15 +289,15 @@ export class WOFSQLitePlaceLookup implements PlaceLookup, Disposable {
|
|
|
358
289
|
}
|
|
359
290
|
|
|
360
291
|
#extractHasTable(schemaName: string, tableName: string): boolean {
|
|
361
|
-
//
|
|
362
|
-
// schema-qualified `sqlite_master
|
|
292
|
+
// Main uses the existing helpers.
|
|
293
|
+
// Attached extracts need the schema-qualified `sqlite_master`.
|
|
363
294
|
if (schemaName === "main") {
|
|
364
|
-
if (tableName === PLACE_BBOX_TABLE) return placeBboxExists(this
|
|
295
|
+
if (tableName === PLACE_BBOX_TABLE) return placeBboxExists(this.database)
|
|
365
296
|
|
|
366
|
-
if (tableName === PLACE_POPULATION_TABLE) return placePopulationExists(this
|
|
297
|
+
if (tableName === PLACE_POPULATION_TABLE) return placePopulationExists(this.database)
|
|
367
298
|
}
|
|
368
299
|
|
|
369
|
-
const row = this
|
|
300
|
+
const row = this.database
|
|
370
301
|
.prepare(`SELECT name FROM ${schemaName}.sqlite_master WHERE type = 'table' AND name = ?`)
|
|
371
302
|
.get(tableName) as { name: string } | undefined
|
|
372
303
|
|
|
@@ -374,15 +305,11 @@ export class WOFSQLitePlaceLookup implements PlaceLookup, Disposable {
|
|
|
374
305
|
}
|
|
375
306
|
|
|
376
307
|
async findPlace(query: FindPlaceQuery): Promise<PlaceCandidate[]> {
|
|
377
|
-
//
|
|
378
|
-
//
|
|
379
|
-
// in order; the first to return a non-null result wins. The default list,
|
|
380
|
-
// [postcode_area_resolution, fallback_fuzzy_name_match], reproduces the pre-engine coordinate-
|
|
381
|
-
// first → FTS fall-through exactly. Unknown strategy names are skipped, so a convention may name
|
|
382
|
-
// a primitive a future phase will register.
|
|
308
|
+
// Run the effective convention's candidate strategies in order.
|
|
309
|
+
// The first non-null result wins and unknown strategy names are skipped.
|
|
383
310
|
const convention = this.#conventionFor(query)
|
|
384
311
|
|
|
385
|
-
let
|
|
312
|
+
let found: PlaceCandidate[] = []
|
|
386
313
|
|
|
387
314
|
for (const name of convention.candidateStrategies) {
|
|
388
315
|
const strategy = this.#strategies.get(name)
|
|
@@ -395,23 +322,17 @@ export class WOFSQLitePlaceLookup implements PlaceLookup, Disposable {
|
|
|
395
322
|
|
|
396
323
|
const result = await strategy(query, convention)
|
|
397
324
|
|
|
398
|
-
if (result
|
|
399
|
-
|
|
325
|
+
if (result) {
|
|
326
|
+
found = result
|
|
400
327
|
|
|
401
328
|
break
|
|
402
329
|
}
|
|
403
330
|
}
|
|
404
331
|
|
|
405
|
-
if (
|
|
332
|
+
if (found.length) return found
|
|
406
333
|
|
|
407
|
-
//
|
|
408
|
-
//
|
|
409
|
-
// ('1012 LG') — two FTS tokens that can never match the one-token doc (the #920 name law,
|
|
410
|
-
// resurfacing in a WOF-built extract). On a postcode-typed NL-shape miss, retry ONCE with the
|
|
411
|
-
// whitespace-joined form (block-level precision when the full-code row exists), then the
|
|
412
|
-
// 4-digit stem (area-level). Country-restricted to NL — the same digits+letters shape elsewhere
|
|
413
|
-
// must not silently coarsen to a different system's code. Each retry only fires when its
|
|
414
|
-
// text differs from the current one, so the ladder terminates by construction.
|
|
334
|
+
// On a postcode-typed NL-shape miss, retry with the whitespace-joined form then the 4-digit stem,
|
|
335
|
+
// restricted to NL so the same shape elsewhere cannot coarsen to another system's code.
|
|
415
336
|
if (
|
|
416
337
|
query.country?.toUpperCase() === "NL" &&
|
|
417
338
|
(normalizePlacetypes(query.placetype)?.includes("postalcode") ?? false) &&
|
|
@@ -431,14 +352,11 @@ export class WOFSQLitePlaceLookup implements PlaceLookup, Disposable {
|
|
|
431
352
|
if (stem !== trimmed) return this.findPlace({ ...query, text: stem })
|
|
432
353
|
}
|
|
433
354
|
|
|
434
|
-
return
|
|
355
|
+
return found
|
|
435
356
|
}
|
|
436
357
|
|
|
437
358
|
/**
|
|
438
|
-
* Dual-role localities coincident with an admin id,
|
|
439
|
-
* {@link ResolveOpts.hierarchyCompletion} (#405): O(1) once the relation is loaded. Returns `[]` when the relation
|
|
440
|
-
* table is absent (older DB) or the admin isn't a dual-role place, so completion degrades gracefully. The relation +
|
|
441
|
-
* `spr` join is loaded once and memoized.
|
|
359
|
+
* Dual-role localities coincident with an admin id, or `[]` when the relation table is absent.
|
|
442
360
|
*/
|
|
443
361
|
coincidentLocalitiesFor(adminID: number | string): CoincidentLocality[] {
|
|
444
362
|
const id = typeof adminID === "number" ? adminID : Number(adminID)
|
|
@@ -446,20 +364,15 @@ export class WOFSQLitePlaceLookup implements PlaceLookup, Disposable {
|
|
|
446
364
|
if (!Number.isFinite(id)) return []
|
|
447
365
|
|
|
448
366
|
if (!this.#coincidentRolesCache) {
|
|
449
|
-
this.#coincidentRolesCache = loadCoincidentLocalities(this
|
|
367
|
+
this.#coincidentRolesCache = loadCoincidentLocalities(this.database)
|
|
450
368
|
}
|
|
451
369
|
|
|
452
370
|
return this.#coincidentRolesCache.get(id) ?? []
|
|
453
371
|
}
|
|
454
372
|
|
|
455
373
|
/**
|
|
456
|
-
* The ancestor lineage of a place
|
|
457
|
-
*
|
|
458
|
-
* is excluded; memoized per id. Returns `[]` when the place has no recorded ancestry.
|
|
459
|
-
*
|
|
460
|
-
* The walk itself lives in `ancestry.ts` (shared with the reverse geocoder, #484); the ordering is its
|
|
461
|
-
* `PLACETYPE_DEPTH` table — same ranking as the previous inline SQL CASE, extended below `localadmin` so
|
|
462
|
-
* locality/neighbourhood ancestors order correctly instead of sorting last.
|
|
374
|
+
* The ancestor lineage of a place, ordered nearest-first with self excluded,
|
|
375
|
+
* or `[]` when it has no recorded ancestry.
|
|
463
376
|
*/
|
|
464
377
|
ancestors(id: number | string): Ancestor[] {
|
|
465
378
|
const pid = typeof id === "number" ? id : Number(id)
|
|
@@ -469,7 +382,7 @@ export class WOFSQLitePlaceLookup implements PlaceLookup, Disposable {
|
|
|
469
382
|
|
|
470
383
|
if (cached) return cached
|
|
471
384
|
|
|
472
|
-
const lineage: Ancestor[] = ancestorLineage(this
|
|
385
|
+
const lineage: Ancestor[] = ancestorLineage(this.database, pid).map((r) => ({
|
|
473
386
|
id: r.id,
|
|
474
387
|
placetype: r.placetype,
|
|
475
388
|
name: r.name,
|
|
@@ -481,10 +394,8 @@ export class WOFSQLitePlaceLookup implements PlaceLookup, Disposable {
|
|
|
481
394
|
}
|
|
482
395
|
|
|
483
396
|
/**
|
|
484
|
-
*
|
|
485
|
-
*
|
|
486
|
-
* throw so a convention asset built against a newer code revision (one that adds a strategy) degrades gracefully on
|
|
487
|
-
* an older build instead of taking down resolution.
|
|
397
|
+
* Warn once per unknown strategy name rather than throwing, so a convention built
|
|
398
|
+
* against a newer revision degrades instead of stopping resolution.
|
|
488
399
|
*/
|
|
489
400
|
#warnUnknownStrategy(name: string): void {
|
|
490
401
|
if (this.#warnedUnknownStrategies.has(name)) return
|
|
@@ -498,10 +409,8 @@ export class WOFSQLitePlaceLookup implements PlaceLookup, Disposable {
|
|
|
498
409
|
}
|
|
499
410
|
|
|
500
411
|
/**
|
|
501
|
-
*
|
|
502
|
-
*
|
|
503
|
-
* strategy — when the condition is unmet or the postcode isn't in the table; otherwise the soft-scored postcode∪name
|
|
504
|
-
* candidate set.
|
|
412
|
+
* The coordinate-first locality strategy, returning `null` when its postcode,
|
|
413
|
+
* table, or locality conditions are unmet.
|
|
505
414
|
*/
|
|
506
415
|
#postcodeAreaResolution(query: FindPlaceQuery, convention: ResolvedConvention): Promise<PlaceCandidate[] | null> {
|
|
507
416
|
if (!(query.postcode && this.#postcodeLocalityExtract && this.#isLocalityQuery(query))) {
|
|
@@ -512,34 +421,30 @@ export class WOFSQLitePlaceLookup implements PlaceLookup, Disposable {
|
|
|
512
421
|
}
|
|
513
422
|
|
|
514
423
|
/**
|
|
515
|
-
*
|
|
516
|
-
* returns an array (never null), so it terminates the dispatch chain.
|
|
424
|
+
* The BM25 FTS name-match fallback, always returning an array so it terminates the dispatch chain.
|
|
517
425
|
*/
|
|
518
426
|
async #fuzzyNameMatch(query: FindPlaceQuery, forceExtract?: ResolvedExtract): Promise<PlaceCandidate[]> {
|
|
519
427
|
const limit = query.limit ?? 10
|
|
520
428
|
|
|
521
|
-
// Expand the placetype filter through the shared equivalence table
|
|
522
|
-
//
|
|
523
|
-
// (pop 2.5M) is a borough, not a locality, and a strict filter made it unreachable so the
|
|
524
|
-
// fuzzy "Brooklyn Park, MN" won instead. Order-preserving: the FIRST entry stays the
|
|
525
|
-
// requested placetype, which is what extract routing keys off below.
|
|
429
|
+
// Expand the placetype filter through the shared equivalence table so a `locality`
|
|
430
|
+
// query also reaches `borough` and `localadmin` rows.
|
|
526
431
|
const placetypes = expandPlacetypeFilter(normalizePlacetypes(query.placetype)) as WOFPlacetype[] | null
|
|
527
|
-
// Postcode-typed queries keep the
|
|
528
|
-
// intra-token punctuation so hyphenated names
|
|
432
|
+
// Postcode-typed queries keep the fused name-law shape.
|
|
433
|
+
// Everything else splits on intra-token punctuation so hyphenated names
|
|
434
|
+
// reach the FTS as their real terms.
|
|
529
435
|
const ftsQuery = sanitizeFTSQuery(query.text, { fuseTokens: placetypes?.includes("postalcode") ?? false })
|
|
530
436
|
|
|
531
437
|
if (!ftsQuery) return []
|
|
532
438
|
|
|
533
|
-
//
|
|
534
|
-
// `placetype`
|
|
535
|
-
//
|
|
439
|
+
// Multi-extract routing uses placetype.
|
|
440
|
+
// Queries without `placetype` go to main.
|
|
441
|
+
// Mixed-placetype spread across extracts is unsupported.
|
|
536
442
|
const firstPlacetype = placetypes?.[0]
|
|
537
443
|
|
|
538
|
-
//
|
|
539
|
-
// cross-extract ambiguity
|
|
540
|
-
//
|
|
541
|
-
//
|
|
542
|
-
// keys the per-extract tier sort used. Bounded: hints + no country + >1 matching extract only.
|
|
444
|
+
// A country-less query with proximity hints queries every matching extract
|
|
445
|
+
// so cross-extract ambiguity is visible.
|
|
446
|
+
// The bound requires hints and an absent country.
|
|
447
|
+
// It also requires more than one matching extract.
|
|
543
448
|
const hasBiasHints = !!query.near || (query.bias?.length ?? 0) > 0
|
|
544
449
|
|
|
545
450
|
if (!forceExtract && hasBiasHints && !query.country) {
|
|
@@ -575,16 +480,16 @@ export class WOFSQLitePlaceLookup implements PlaceLookup, Disposable {
|
|
|
575
480
|
|
|
576
481
|
const extract =
|
|
577
482
|
forceExtract ??
|
|
578
|
-
pickExtractForPlacetype(this.#extracts, firstPlacetype, {
|
|
483
|
+
pickExtractForPlacetype(this.#extracts, firstPlacetype ?? null, {
|
|
579
484
|
country: query.country,
|
|
580
485
|
countriesBySchema: this.#extractCountries,
|
|
581
486
|
})
|
|
582
487
|
|
|
583
|
-
//
|
|
488
|
+
// Validated at construction, so bare interpolation is safe.
|
|
584
489
|
const sch = extract.schemaName
|
|
585
490
|
|
|
586
491
|
const rawRows = fetchSearchRows({
|
|
587
|
-
db: this
|
|
492
|
+
db: this.database,
|
|
588
493
|
schemaName: sch,
|
|
589
494
|
query,
|
|
590
495
|
placetypes,
|
|
@@ -606,7 +511,7 @@ export class WOFSQLitePlaceLookup implements PlaceLookup, Disposable {
|
|
|
606
511
|
const candidates = rawRows.map((row) => candidateFromSearchRow(row, scoring))
|
|
607
512
|
|
|
608
513
|
rankCandidates(candidates, {
|
|
609
|
-
db: this
|
|
514
|
+
db: this.database,
|
|
610
515
|
schemaName: sch,
|
|
611
516
|
query,
|
|
612
517
|
weights: this.#weights,
|
|
@@ -622,11 +527,7 @@ export class WOFSQLitePlaceLookup implements PlaceLookup, Disposable {
|
|
|
622
527
|
}
|
|
623
528
|
|
|
624
529
|
/**
|
|
625
|
-
* Resolve the effective convention for a query
|
|
626
|
-
* by WOF polygon id; for #289 it carries just the country level — resolved from `query.country` via the cached
|
|
627
|
-
* code→WOF-id lookup — so the EU locales, which have no override rows, resolve to `WORLD_DEFAULT` and dispatch is
|
|
628
|
-
* byte-identical to the pre-engine path. E4 (JP) extends the chain with the resolved locality's `ancestors` row, so a
|
|
629
|
-
* region/locality-level convention (e.g. Sapporo's grid) deep-merges over the country one.
|
|
530
|
+
* Resolve the effective convention for a query, keyed by the country's WOF polygon id.
|
|
630
531
|
*/
|
|
631
532
|
#conventionFor(query: FindPlaceQuery): ResolvedConvention {
|
|
632
533
|
const chain: number[] = []
|
|
@@ -643,8 +544,7 @@ export class WOFSQLitePlaceLookup implements PlaceLookup, Disposable {
|
|
|
643
544
|
}
|
|
644
545
|
|
|
645
546
|
/**
|
|
646
|
-
* Country ISO code
|
|
647
|
-
* country, then memoized (including a not-found `null`) so findPlace never pays for it twice.
|
|
547
|
+
* Country ISO code to its WOF polygon id, memoized including the not-found `null`.
|
|
648
548
|
*/
|
|
649
549
|
#countryWOFId(code: string): number | null {
|
|
650
550
|
const cached = this.#countryWOFIdCache.get(code)
|
|
@@ -653,7 +553,7 @@ export class WOFSQLitePlaceLookup implements PlaceLookup, Disposable {
|
|
|
653
553
|
let id: number | null
|
|
654
554
|
|
|
655
555
|
try {
|
|
656
|
-
const row = this
|
|
556
|
+
const row = this.database
|
|
657
557
|
.prepare(`SELECT id FROM main.spr WHERE placetype = 'country' AND country = ? AND is_current != 0 LIMIT 1`)
|
|
658
558
|
.get(code) as { id: number } | undefined
|
|
659
559
|
|
|
@@ -668,12 +568,9 @@ export class WOFSQLitePlaceLookup implements PlaceLookup, Disposable {
|
|
|
668
568
|
}
|
|
669
569
|
|
|
670
570
|
/**
|
|
671
|
-
* Coordinate-first locality resolution
|
|
672
|
-
*
|
|
673
|
-
*
|
|
674
|
-
* so a small town the name-match never finds is recovered by the postcode, while an unambiguous name (Berlin) still
|
|
675
|
-
* wins on name + population. Returns null when the postcode isn't in the table (→ caller falls back to the FTS
|
|
676
|
-
* path).
|
|
571
|
+
* Coordinate-first locality resolution: union the postcode's containing
|
|
572
|
+
* and nearby localities with the FTS name candidates and soft-score them,
|
|
573
|
+
* returning null when the postcode is not in the table.
|
|
677
574
|
*/
|
|
678
575
|
async #findLocalityCoordFirst(
|
|
679
576
|
query: FindPlaceQuery,
|
|
@@ -686,7 +583,7 @@ export class WOFSQLitePlaceLookup implements PlaceLookup, Disposable {
|
|
|
686
583
|
const pcParams: SQLInputValue[] = query.country ? [pc, query.country] : [pc]
|
|
687
584
|
|
|
688
585
|
const pcRows = allRows<{ id: number; aliases: string | null; dist: number; containing: number }>(
|
|
689
|
-
this
|
|
586
|
+
this.database.prepare(
|
|
690
587
|
`SELECT locality_id AS id, aliases, distance_km AS dist, is_containing AS containing
|
|
691
588
|
FROM ${sch}.${POSTCODE_LOCALITY_TABLE} WHERE ${pcWhere}`
|
|
692
589
|
),
|
|
@@ -696,7 +593,7 @@ export class WOFSQLitePlaceLookup implements PlaceLookup, Disposable {
|
|
|
696
593
|
if (!pcRows.length) return null
|
|
697
594
|
|
|
698
595
|
const limit = query.limit ?? 10
|
|
699
|
-
// Name-match candidates via the normal FTS path
|
|
596
|
+
// Name-match candidates via the normal FTS path with the postcode cleared, so this cannot recurse.
|
|
700
597
|
const ftsCands = await this.findPlace({ ...query, postcode: undefined, limit: Math.max(limit, 10) })
|
|
701
598
|
|
|
702
599
|
const pcInfo = new Map<number, { dist: number; containing: boolean; aliases: string[] }>()
|
|
@@ -705,10 +602,8 @@ export class WOFSQLitePlaceLookup implements PlaceLookup, Disposable {
|
|
|
705
602
|
pcInfo.set(r.id, { dist: r.dist, containing: r.containing === 1, aliases: r.aliases ? r.aliases.split("|") : [] })
|
|
706
603
|
}
|
|
707
604
|
|
|
708
|
-
//
|
|
709
|
-
//
|
|
710
|
-
// alias for the geographic locality the postcode sits in ("Nashville"). Empty when the reader
|
|
711
|
-
// isn't supplied → the scoring loop below is byte-identical to pre-#475.
|
|
605
|
+
// Observed postal-city aliases for this postcode keyed by geographic locality name,
|
|
606
|
+
// empty when the reader is not supplied.
|
|
712
607
|
const postalAliasByGeo = new Map<string, string[]>()
|
|
713
608
|
|
|
714
609
|
if (this.#postalCityAliases) {
|
|
@@ -743,9 +638,8 @@ export class WOFSQLitePlaceLookup implements PlaceLookup, Disposable {
|
|
|
743
638
|
for (const cand of merged.values()) {
|
|
744
639
|
const info = pcInfo.get(cand.id as number)
|
|
745
640
|
const sPc = info ? (info.containing ? 1 : Math.exp(-info.dist / CF_PC_DECAY_KM)) : 0
|
|
746
|
-
// Fold
|
|
747
|
-
//
|
|
748
|
-
// reduces to the original `info?.aliases ?? []` and the score is unchanged.
|
|
641
|
+
// Fold postal-city aliases into the soft name match.
|
|
642
|
+
// The map is empty unless the opt-in reader was supplied, so scoring is unchanged when off.
|
|
749
643
|
const wofAliases = info?.aliases ?? []
|
|
750
644
|
|
|
751
645
|
const aliases = postalAliasByGeo.size
|
|
@@ -753,30 +647,23 @@ export class WOFSQLitePlaceLookup implements PlaceLookup, Disposable {
|
|
|
753
647
|
: wofAliases
|
|
754
648
|
|
|
755
649
|
const sName = softNameScore(query.text, cand.name, aliases)
|
|
756
|
-
const sPop = populationScaleTerm(cand.population, this.#weights)
|
|
650
|
+
const sPop = populationScaleTerm(cand.population ?? null, this.#weights)
|
|
757
651
|
scored.push({ ...cand, score: w.pc * sPc + w.name * sName + w.pop * sPop, exact: sName >= 1 })
|
|
758
652
|
}
|
|
759
653
|
|
|
760
|
-
//
|
|
761
|
-
//
|
|
762
|
-
// unambiguous city ("Berlin", exact + huge population) ahead of the fine-grained Ortsteil its
|
|
763
|
-
// postcode centroid lands in, while a small town the name-match never finds (no exact tier) is
|
|
764
|
-
// still recovered by its postcode's containing locality.
|
|
654
|
+
// An exact name or alias match tiers above coordinate-only candidates,
|
|
655
|
+
// with the soft score breaking ties within a tier.
|
|
765
656
|
scored.sort((a, b) => Number(b.exact) - Number(a.exact) || b.score - a.score)
|
|
766
657
|
|
|
767
|
-
//
|
|
768
|
-
// from it,
|
|
769
|
-
// We keep the name-chosen locality but flag it — the falsehood signal a BM25 geocoder can't give.
|
|
658
|
+
// When the chosen locality is not the postcode's containing locality and sits far
|
|
659
|
+
// from it, flag `mismatch` rather than overriding the name.
|
|
770
660
|
const top = scored[0]
|
|
771
661
|
|
|
772
662
|
if (top) {
|
|
773
|
-
//
|
|
774
|
-
// resolved (some — e.g. unnamed Ortsteile — are in the postcode table but not the admin DB),
|
|
775
|
-
// prefer the containing one, else the nearest. Postcodes whose centroid falls just outside
|
|
776
|
-
// every locality polygon still anchor to the closest town.
|
|
663
|
+
// Among the postcode's candidate localities that resolved, prefer the containing one, else the nearest.
|
|
777
664
|
const anchorRow = pcRows
|
|
778
665
|
.filter((r) => merged.has(r.id))
|
|
779
|
-
// oxlint-disable-next-line unicorn/no-array-sort -- sorts a freshly-built array
|
|
666
|
+
// oxlint-disable-next-line unicorn/no-array-sort -- sorts a freshly-built array. toSorted would double-allocate on a hot path
|
|
780
667
|
.sort((a, b) => b.containing - a.containing || a.dist - b.dist)[0]
|
|
781
668
|
|
|
782
669
|
const anchor = anchorRow ? merged.get(anchorRow.id) : undefined
|
|
@@ -798,7 +685,7 @@ export class WOFSQLitePlaceLookup implements PlaceLookup, Disposable {
|
|
|
798
685
|
}
|
|
799
686
|
|
|
800
687
|
/**
|
|
801
|
-
* Fetch locality spr rows
|
|
688
|
+
* Fetch locality spr rows from main for the postcode-injected candidate ids the FTS set missed.
|
|
802
689
|
*/
|
|
803
690
|
#fetchLocalitiesByID(ids: number[]): PlaceCandidate[] {
|
|
804
691
|
if (!ids.length) return []
|
|
@@ -808,7 +695,7 @@ export class WOFSQLitePlaceLookup implements PlaceLookup, Disposable {
|
|
|
808
695
|
const ph = ids.map(() => "?").join(", ")
|
|
809
696
|
|
|
810
697
|
const rows = allRows<RawSearchRow>(
|
|
811
|
-
this
|
|
698
|
+
this.database.prepare(
|
|
812
699
|
`SELECT s.id AS id, s.name AS name, s.country AS country, s.parent_id AS parent_id,
|
|
813
700
|
s.latitude AS lat, s.longitude AS lon, s.placetype AS placetype, ${popSelect}
|
|
814
701
|
FROM main.spr s ${popJoin}
|
|
@@ -818,45 +705,43 @@ export class WOFSQLitePlaceLookup implements PlaceLookup, Disposable {
|
|
|
818
705
|
)
|
|
819
706
|
|
|
820
707
|
return rows.map((row) => {
|
|
821
|
-
|
|
708
|
+
return {
|
|
822
709
|
id: row.id,
|
|
823
710
|
name: row.name,
|
|
824
711
|
placetype: row.placetype as WOFPlacetype,
|
|
825
712
|
country: row.country ?? "",
|
|
826
713
|
lat: row.lat ?? 0,
|
|
827
714
|
lon: row.lon ?? 0,
|
|
828
|
-
parent_id: row.parent_id ??
|
|
715
|
+
parent_id: row.parent_id ?? null,
|
|
829
716
|
score: 0,
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
717
|
+
population: row.population !== null && row.population > 0 ? row.population : null,
|
|
718
|
+
distanceKm: null,
|
|
719
|
+
exactMatch: null,
|
|
720
|
+
prominence: null,
|
|
721
|
+
referential: null,
|
|
722
|
+
encyclopedic: null,
|
|
723
|
+
importance: null,
|
|
724
|
+
bbox: null,
|
|
725
|
+
mismatch: null,
|
|
726
|
+
containedByQualifier: null,
|
|
727
|
+
regionScopeMiss: null,
|
|
728
|
+
variantAliasExempted: null,
|
|
729
|
+
} satisfies PlaceCandidate
|
|
837
730
|
})
|
|
838
731
|
}
|
|
839
732
|
|
|
840
|
-
[Symbol.dispose](): void {
|
|
841
|
-
// Only when we opened it. A caller who passed a pre-opened client keeps using it after this returns — the FTS
|
|
842
|
-
// build this lookup performed lives on their connection, and closing it would take that with us.
|
|
843
|
-
this.#resources[Symbol.dispose]()
|
|
844
|
-
}
|
|
845
|
-
|
|
846
733
|
/**
|
|
847
|
-
* Build the FTS5 virtual table from the `names`
|
|
734
|
+
* Build the FTS5 virtual table from the `names` and `places` tables.
|
|
848
735
|
*/
|
|
849
736
|
#ensureFTS(): void {
|
|
850
|
-
buildPlaceSearchFTS(this
|
|
737
|
+
buildPlaceSearchFTS(this.database)
|
|
851
738
|
}
|
|
852
739
|
|
|
853
740
|
#assertFTSExists(): void {
|
|
854
|
-
if (!placeSearchFTSExists(this
|
|
741
|
+
if (!placeSearchFTSExists(this.database)) {
|
|
855
742
|
throw new Error(
|
|
856
743
|
"WOFSQLitePlaceLookup: `place_search` FTS5 table is missing. Pass `buildFTS: true` to build it on open, or run `mailwoman gazetteer build fts <path-to-wof.db>` ahead of time (see resolver-wof-sqlite/README.md)."
|
|
857
744
|
)
|
|
858
745
|
}
|
|
859
746
|
}
|
|
860
747
|
}
|
|
861
|
-
|
|
862
|
-
export type { RankingWeights } from "#ranking-weights"
|