@mailwoman/resolver-wof-sqlite 9.2.0 → 9.3.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 +16 -19
- package/lib/address/index.ts +9 -0
- package/{address-point-interpolation.ts → lib/address/point-interpolation.ts} +24 -22
- package/{address-point-schema.ts → lib/address/point-schema.ts} +18 -4
- package/{address-point.ts → lib/address/point.ts} +89 -27
- package/{ancestry-backfill.ts → lib/ancestry/backfill.ts} +14 -19
- package/{ancestry.ts → lib/ancestry/index.ts} +5 -3
- package/{build-candidate.ts → lib/build-candidate.ts} +84 -77
- package/{build-slim.ts → lib/build-slim.ts} +150 -165
- package/{candidate → lib/candidate}/alias-bags.ts +8 -6
- package/{candidate → lib/candidate}/ancestors-sidecar.ts +13 -15
- package/{candidate → lib/candidate}/country-display-names.ts +2 -2
- package/{candidate/shard-fold.ts → lib/candidate/extract-fold.ts} +26 -25
- package/{candidate → lib/candidate}/name-roles.ts +17 -16
- package/{candidate → lib/candidate}/own-name.ts +1 -1
- package/{candidate → lib/candidate}/place-attrs.ts +3 -3
- package/{candidate-ancestors-schema.ts → lib/candidate-ancestors-schema.ts} +4 -4
- package/{candidate-fts.ts → lib/candidate-fts.ts} +3 -3
- package/{candidate-importance.ts → lib/candidate-importance.ts} +53 -57
- package/{candidate-lookup.ts → lib/candidate-lookup.ts} +76 -73
- package/{candidate-schema.ts → lib/candidate-schema.ts} +5 -5
- package/{candidate-scoring.ts → lib/candidate-scoring.ts} +15 -22
- package/{capital-schema.ts → lib/capital-schema.ts} +5 -6
- package/{capitals.ts → lib/capitals.ts} +2 -2
- package/{coincident-roles.ts → lib/coincident-roles.ts} +8 -11
- package/{convention.ts → lib/convention/index.ts} +3 -3
- package/{convention-schema.ts → lib/convention/schema.ts} +2 -2
- package/{coverage-manifest-schema.ts → lib/coverage-manifest-schema.ts} +9 -10
- package/{currency-backfill.ts → lib/currency-backfill.ts} +126 -41
- package/lib/env.ts +50 -0
- package/{exact-match.ts → lib/exact-match.ts} +23 -14
- package/{sharding.ts → lib/extracts.ts} +61 -58
- package/{fst-autocomplete.ts → lib/fst/autocomplete.ts} +4 -4
- package/{fst-builder.ts → lib/fst/builder.ts} +17 -16
- package/{fst-deserialize-web.ts → lib/fst/deserialize-web.ts} +27 -87
- package/lib/fst/format.ts +106 -0
- package/{fst-freshness.ts → lib/fst/freshness.ts} +41 -74
- package/lib/fst/index.ts +14 -0
- package/{fst-matcher.ts → lib/fst/matcher.ts} +1 -1
- package/{fst-serialize.ts → lib/fst/serialize.ts} +37 -115
- package/{fst-types.ts → lib/fst/types.ts} +4 -2
- package/{fts.ts → lib/fts/index.ts} +24 -19
- package/{fts-query.ts → lib/fts/query.ts} +1 -1
- package/{geonames-aliases.ts → lib/geonames/aliases.ts} +13 -13
- package/lib/geonames/index.ts +8 -0
- package/{geonames-postal.ts → lib/geonames/postal.ts} +14 -13
- package/{index.ts → lib/index.ts} +37 -37
- package/{interpolation.ts → lib/interpolation.ts} +27 -25
- package/{lookup.ts → lib/lookup.ts} +129 -141
- package/lib/nsul/index.ts +8 -0
- package/lib/nsul/lookup.ts +144 -0
- package/lib/nsul/schema.ts +130 -0
- package/{place-importance-schema.ts → lib/place-importance-schema.ts} +7 -8
- package/lib/poi/index.ts +8 -0
- package/{poi-lookup.ts → lib/poi/lookup.ts} +22 -28
- package/{poi-schema.ts → lib/poi/schema.ts} +3 -4
- package/{postal-city-alias-lookup.ts → lib/postal/city-alias-lookup.ts} +14 -20
- package/{postal-city-candidate-schema.ts → lib/postal/city-candidate-schema.ts} +2 -2
- package/lib/postal/index.ts +9 -0
- package/{postcode-point-lookup.ts → lib/postcode-point-lookup.ts} +17 -15
- package/{primary-preference.ts → lib/primary-preference.ts} +29 -10
- package/{proximity-rerank.ts → lib/proximity-rerank.ts} +2 -2
- package/{ranking-weights.ts → lib/ranking-weights.ts} +30 -4
- package/{region-keys.ts → lib/region-keys.ts} +9 -9
- package/{reverse.ts → lib/reverse.ts} +34 -46
- package/{schema.ts → lib/schema.ts} +10 -0
- package/{search-fetch.ts → lib/search-fetch.ts} +29 -28
- package/{sqlite-convention-source.ts → lib/sqlite-convention-source.ts} +9 -10
- package/{sqlite-utils.ts → lib/sqlite-utils.ts} +14 -18
- package/{street-centroid-schema.ts → lib/street/centroid-schema.ts} +5 -5
- package/{street-centroid.ts → lib/street/centroid.ts} +14 -21
- package/lib/street/index.ts +13 -0
- package/{street-morphology-fst-builder.ts → lib/street/morphology-fst-builder.ts} +24 -14
- package/{street-morphology-fst-loader.ts → lib/street/morphology-fst-loader.ts} +14 -12
- package/{street-name-lookup.ts → lib/street/name-lookup.ts} +14 -30
- package/{street-normalize.ts → lib/street/normalize.ts} +44 -19
- package/{street-segment-schema.ts → lib/street/segment-schema.ts} +7 -7
- package/{types.ts → lib/types.ts} +18 -12
- package/{unified-schema.ts → lib/unified-schema.ts} +20 -24
- package/lib/uprn/existence.ts +84 -0
- package/lib/uprn/index.ts +9 -0
- package/{uprn-lookup.ts → lib/uprn/lookup.ts} +14 -18
- package/{uprn-schema.ts → lib/uprn/schema.ts} +3 -3
- package/lib/weights-overlay-linker.ts +878 -0
- package/out/address/index.d.ts +9 -0
- package/out/address/index.d.ts.map +1 -0
- package/out/address/index.js +9 -0
- package/out/address/index.js.map +1 -0
- package/out/{address-point-interpolation.d.ts → address/point-interpolation.d.ts} +11 -10
- package/out/address/point-interpolation.d.ts.map +1 -0
- package/out/{address-point-interpolation.js → address/point-interpolation.js} +18 -18
- package/out/address/point-interpolation.js.map +1 -0
- package/out/{address-point-schema.d.ts → address/point-schema.d.ts} +16 -6
- package/out/address/point-schema.d.ts.map +1 -0
- package/out/{address-point-schema.js → address/point-schema.js} +7 -3
- package/out/address/point-schema.js.map +1 -0
- package/out/{address-point.d.ts → address/point.d.ts} +16 -8
- package/out/address/point.d.ts.map +1 -0
- package/out/{address-point.js → address/point.js} +71 -20
- package/out/address/point.js.map +1 -0
- package/out/{ancestry-backfill.d.ts → ancestry/backfill.d.ts} +7 -5
- package/out/ancestry/backfill.d.ts.map +1 -0
- package/out/{ancestry-backfill.js → ancestry/backfill.js} +12 -16
- package/out/ancestry/backfill.js.map +1 -0
- package/out/{ancestry.d.ts → ancestry/index.d.ts} +4 -3
- package/out/ancestry/index.d.ts.map +1 -0
- package/out/{ancestry.js → ancestry/index.js} +3 -2
- package/out/ancestry/index.js.map +1 -0
- package/out/build-candidate.d.ts +30 -24
- package/out/build-candidate.d.ts.map +1 -1
- package/out/build-candidate.js +50 -52
- package/out/build-candidate.js.map +1 -1
- package/out/build-slim.d.ts +4 -4
- package/out/build-slim.d.ts.map +1 -1
- package/out/build-slim.js +124 -139
- package/out/build-slim.js.map +1 -1
- package/out/candidate/alias-bags.d.ts +5 -3
- package/out/candidate/alias-bags.d.ts.map +1 -1
- package/out/candidate/alias-bags.js +2 -2
- package/out/candidate/alias-bags.js.map +1 -1
- package/out/candidate/ancestors-sidecar.d.ts +9 -10
- package/out/candidate/ancestors-sidecar.d.ts.map +1 -1
- package/out/candidate/ancestors-sidecar.js +7 -7
- package/out/candidate/ancestors-sidecar.js.map +1 -1
- package/out/candidate/country-display-names.d.ts +1 -1
- package/out/candidate/country-display-names.d.ts.map +1 -1
- package/out/candidate/country-display-names.js +1 -1
- package/out/candidate/country-display-names.js.map +1 -1
- package/out/candidate/extract-fold.d.ts +32 -0
- package/out/candidate/extract-fold.d.ts.map +1 -0
- package/out/candidate/{shard-fold.js → extract-fold.js} +20 -20
- package/out/candidate/extract-fold.js.map +1 -0
- package/out/candidate/name-roles.d.ts +13 -11
- package/out/candidate/name-roles.d.ts.map +1 -1
- package/out/candidate/name-roles.js +11 -10
- package/out/candidate/name-roles.js.map +1 -1
- package/out/candidate/own-name.d.ts +1 -1
- package/out/candidate/own-name.d.ts.map +1 -1
- package/out/candidate/own-name.js +1 -1
- package/out/candidate/own-name.js.map +1 -1
- package/out/candidate/place-attrs.d.ts +3 -3
- package/out/candidate/place-attrs.d.ts.map +1 -1
- package/out/candidate/place-attrs.js +1 -1
- package/out/candidate/place-attrs.js.map +1 -1
- package/out/candidate-ancestors-schema.d.ts +4 -4
- package/out/candidate-ancestors-schema.d.ts.map +1 -1
- package/out/candidate-ancestors-schema.js +2 -2
- package/out/candidate-ancestors-schema.js.map +1 -1
- package/out/candidate-fts.d.ts +3 -3
- package/out/candidate-fts.d.ts.map +1 -1
- package/out/candidate-fts.js +1 -1
- package/out/candidate-fts.js.map +1 -1
- package/out/candidate-importance.d.ts +16 -16
- package/out/candidate-importance.d.ts.map +1 -1
- package/out/candidate-importance.js +46 -51
- package/out/candidate-importance.js.map +1 -1
- package/out/candidate-lookup.d.ts +11 -9
- package/out/candidate-lookup.d.ts.map +1 -1
- package/out/candidate-lookup.js +61 -58
- package/out/candidate-lookup.js.map +1 -1
- package/out/candidate-schema.d.ts +5 -5
- package/out/candidate-schema.d.ts.map +1 -1
- package/out/candidate-schema.js.map +1 -1
- package/out/candidate-scoring.d.ts +7 -7
- package/out/candidate-scoring.d.ts.map +1 -1
- package/out/candidate-scoring.js +10 -14
- package/out/candidate-scoring.js.map +1 -1
- package/out/capital-schema.d.ts +3 -3
- package/out/capital-schema.d.ts.map +1 -1
- package/out/capital-schema.js +2 -2
- package/out/capital-schema.js.map +1 -1
- package/out/capitals.d.ts +1 -1
- package/out/capitals.d.ts.map +1 -1
- package/out/capitals.js +2 -2
- package/out/capitals.js.map +1 -1
- package/out/coincident-roles.d.ts +6 -5
- package/out/coincident-roles.d.ts.map +1 -1
- package/out/coincident-roles.js +2 -4
- package/out/coincident-roles.js.map +1 -1
- package/out/{convention.d.ts → convention/index.d.ts} +4 -4
- package/out/convention/index.d.ts.map +1 -0
- package/out/{convention.js → convention/index.js} +2 -2
- package/out/convention/index.js.map +1 -0
- package/out/{convention-schema.d.ts → convention/schema.d.ts} +2 -2
- package/out/convention/schema.d.ts.map +1 -0
- package/out/{convention-schema.js → convention/schema.js} +3 -3
- package/out/convention/schema.js.map +1 -0
- package/out/coverage-manifest-schema.d.ts +8 -8
- package/out/coverage-manifest-schema.d.ts.map +1 -1
- package/out/coverage-manifest-schema.js +4 -4
- package/out/coverage-manifest-schema.js.map +1 -1
- package/out/currency-backfill.d.ts +51 -7
- package/out/currency-backfill.d.ts.map +1 -1
- package/out/currency-backfill.js +65 -49
- package/out/currency-backfill.js.map +1 -1
- package/out/env.d.ts +43 -0
- package/out/env.d.ts.map +1 -0
- package/out/env.js +48 -0
- package/out/env.js.map +1 -0
- package/out/exact-match.d.ts +5 -5
- package/out/exact-match.d.ts.map +1 -1
- package/out/exact-match.js +10 -11
- package/out/exact-match.js.map +1 -1
- package/out/extracts.d.ts +114 -0
- package/out/extracts.d.ts.map +1 -0
- package/out/{sharding.js → extracts.js} +38 -38
- package/out/extracts.js.map +1 -0
- package/out/{fst-autocomplete.d.ts → fst/autocomplete.d.ts} +2 -2
- package/out/fst/autocomplete.d.ts.map +1 -0
- package/out/{fst-autocomplete.js → fst/autocomplete.js} +3 -3
- package/out/fst/autocomplete.js.map +1 -0
- package/out/{fst-builder.d.ts → fst/builder.d.ts} +5 -5
- package/out/fst/builder.d.ts.map +1 -0
- package/out/{fst-builder.js → fst/builder.js} +14 -13
- package/out/fst/builder.js.map +1 -0
- package/out/{fst-deserialize-web.d.ts → fst/deserialize-web.d.ts} +3 -3
- package/out/fst/deserialize-web.d.ts.map +1 -0
- package/out/{fst-deserialize-web.js → fst/deserialize-web.js} +10 -74
- package/out/fst/deserialize-web.js.map +1 -0
- package/out/fst/format.d.ts +80 -0
- package/out/fst/format.d.ts.map +1 -0
- package/out/fst/format.js +91 -0
- package/out/fst/format.js.map +1 -0
- package/out/{fst-freshness.d.ts → fst/freshness.d.ts} +10 -13
- package/out/fst/freshness.d.ts.map +1 -0
- package/out/{fst-freshness.js → fst/freshness.js} +41 -73
- package/out/fst/freshness.js.map +1 -0
- package/out/fst/index.d.ts +14 -0
- package/out/fst/index.d.ts.map +1 -0
- package/out/fst/index.js +14 -0
- package/out/fst/index.js.map +1 -0
- package/out/{fst-matcher.d.ts → fst/matcher.d.ts} +2 -2
- package/out/fst/matcher.d.ts.map +1 -0
- package/out/{fst-matcher.js → fst/matcher.js} +1 -1
- package/out/fst/matcher.js.map +1 -0
- package/out/{fst-serialize.d.ts → fst/serialize.d.ts} +5 -10
- package/out/fst/serialize.d.ts.map +1 -0
- package/out/{fst-serialize.js → fst/serialize.js} +18 -98
- package/out/fst/serialize.js.map +1 -0
- package/out/{fst-types.d.ts → fst/types.d.ts} +4 -3
- package/out/fst/types.d.ts.map +1 -0
- package/out/{fst-types.js → fst/types.js} +1 -1
- package/out/fst/types.js.map +1 -0
- package/out/{fts.d.ts → fts/index.d.ts} +16 -9
- package/out/fts/index.d.ts.map +1 -0
- package/out/{fts.js → fts/index.js} +16 -11
- package/out/fts/index.js.map +1 -0
- package/out/{fts-query.d.ts → fts/query.d.ts} +2 -2
- package/out/fts/query.d.ts.map +1 -0
- package/out/{fts-query.js → fts/query.js} +1 -1
- package/out/fts/query.js.map +1 -0
- package/out/{geonames-aliases.d.ts → geonames/aliases.d.ts} +8 -6
- package/out/geonames/aliases.d.ts.map +1 -0
- package/out/{geonames-aliases.js → geonames/aliases.js} +7 -7
- package/out/geonames/aliases.js.map +1 -0
- package/out/geonames/index.d.ts +8 -0
- package/out/geonames/index.d.ts.map +1 -0
- package/out/geonames/index.js +8 -0
- package/out/geonames/index.js.map +1 -0
- package/out/{geonames-postal.d.ts → geonames/postal.d.ts} +10 -8
- package/out/geonames/postal.d.ts.map +1 -0
- package/out/{geonames-postal.js → geonames/postal.js} +9 -9
- package/out/geonames/postal.js.map +1 -0
- package/out/index.d.ts +32 -32
- package/out/index.d.ts.map +1 -1
- package/out/index.js +24 -24
- package/out/index.js.map +1 -1
- package/out/interpolation.d.ts +10 -9
- package/out/interpolation.d.ts.map +1 -1
- package/out/interpolation.js +20 -20
- package/out/interpolation.js.map +1 -1
- package/out/lookup.d.ts +21 -20
- package/out/lookup.d.ts.map +1 -1
- package/out/lookup.js +102 -112
- package/out/lookup.js.map +1 -1
- package/out/name-score.d.ts.map +1 -1
- package/out/name-score.js.map +1 -1
- package/out/nsul/index.d.ts +8 -0
- package/out/nsul/index.d.ts.map +1 -0
- package/out/nsul/index.js +8 -0
- package/out/nsul/index.js.map +1 -0
- package/out/nsul/lookup.d.ts +83 -0
- package/out/nsul/lookup.d.ts.map +1 -0
- package/out/nsul/lookup.js +80 -0
- package/out/nsul/lookup.js.map +1 -0
- package/out/nsul/schema.d.ts +100 -0
- package/out/nsul/schema.d.ts.map +1 -0
- package/out/nsul/schema.js +78 -0
- package/out/nsul/schema.js.map +1 -0
- package/out/place-importance-schema.d.ts +5 -5
- package/out/place-importance-schema.d.ts.map +1 -1
- package/out/place-importance-schema.js +3 -3
- package/out/place-importance-schema.js.map +1 -1
- package/out/poi/index.d.ts +8 -0
- package/out/poi/index.d.ts.map +1 -0
- package/out/poi/index.js +8 -0
- package/out/poi/index.js.map +1 -0
- package/out/{poi-lookup.d.ts → poi/lookup.d.ts} +8 -10
- package/out/poi/lookup.d.ts.map +1 -0
- package/out/{poi-lookup.js → poi/lookup.js} +14 -18
- package/out/poi/lookup.js.map +1 -0
- package/out/{poi-schema.d.ts → poi/schema.d.ts} +4 -4
- package/out/poi/schema.d.ts.map +1 -0
- package/out/{poi-schema.js → poi/schema.js} +1 -1
- package/out/poi/schema.js.map +1 -0
- package/out/polygon-schema.d.ts.map +1 -1
- package/out/polygon-schema.js.map +1 -1
- package/out/{postal-city-alias-lookup.d.ts → postal/city-alias-lookup.d.ts} +6 -5
- package/out/postal/city-alias-lookup.d.ts.map +1 -0
- package/out/{postal-city-alias-lookup.js → postal/city-alias-lookup.js} +11 -15
- package/out/postal/city-alias-lookup.js.map +1 -0
- package/out/{postal-city-alias-schema.d.ts → postal/city-alias-schema.d.ts} +1 -1
- package/out/{postal-city-alias-schema.d.ts.map → postal/city-alias-schema.d.ts.map} +1 -1
- package/out/{postal-city-alias-schema.js → postal/city-alias-schema.js} +1 -1
- package/out/{postal-city-alias-schema.js.map → postal/city-alias-schema.js.map} +1 -1
- package/out/{postal-city-candidate-schema.d.ts → postal/city-candidate-schema.d.ts} +3 -3
- package/out/{postal-city-candidate-schema.d.ts.map → postal/city-candidate-schema.d.ts.map} +1 -1
- package/out/{postal-city-candidate-schema.js → postal/city-candidate-schema.js} +2 -2
- package/out/{postal-city-candidate-schema.js.map → postal/city-candidate-schema.js.map} +1 -1
- package/out/postal/index.d.ts +9 -0
- package/out/postal/index.d.ts.map +1 -0
- package/out/postal/index.js +9 -0
- package/out/postal/index.js.map +1 -0
- package/out/postcode-point-lookup.d.ts +10 -10
- package/out/postcode-point-lookup.d.ts.map +1 -1
- package/out/postcode-point-lookup.js +13 -13
- package/out/postcode-point-lookup.js.map +1 -1
- package/out/primary-preference.d.ts +13 -5
- package/out/primary-preference.d.ts.map +1 -1
- package/out/primary-preference.js +16 -8
- package/out/primary-preference.js.map +1 -1
- package/out/proximity-rerank.d.ts +2 -2
- package/out/proximity-rerank.d.ts.map +1 -1
- package/out/proximity-rerank.js +2 -2
- package/out/proximity-rerank.js.map +1 -1
- package/out/ranking-weights.d.ts +15 -3
- package/out/ranking-weights.d.ts.map +1 -1
- package/out/ranking-weights.js +21 -3
- package/out/ranking-weights.js.map +1 -1
- package/out/region-keys.d.ts +8 -8
- package/out/region-keys.d.ts.map +1 -1
- package/out/region-keys.js +9 -9
- package/out/region-keys.js.map +1 -1
- package/out/reverse.d.ts +6 -6
- package/out/reverse.d.ts.map +1 -1
- package/out/reverse.js +20 -29
- package/out/reverse.js.map +1 -1
- package/out/schema.d.ts +9 -0
- package/out/schema.d.ts.map +1 -1
- package/out/schema.js.map +1 -1
- package/out/search-fetch.d.ts +12 -10
- package/out/search-fetch.d.ts.map +1 -1
- package/out/search-fetch.js +21 -20
- package/out/search-fetch.js.map +1 -1
- package/out/sqlite-convention-source.d.ts +7 -7
- package/out/sqlite-convention-source.d.ts.map +1 -1
- package/out/sqlite-convention-source.js +5 -5
- package/out/sqlite-convention-source.js.map +1 -1
- package/out/sqlite-utils.d.ts +8 -8
- package/out/sqlite-utils.d.ts.map +1 -1
- package/out/sqlite-utils.js +7 -8
- package/out/sqlite-utils.js.map +1 -1
- package/out/{street-centroid-schema.d.ts → street/centroid-schema.d.ts} +6 -6
- package/out/{street-centroid-schema.d.ts.map → street/centroid-schema.d.ts.map} +1 -1
- package/out/{street-centroid-schema.js → street/centroid-schema.js} +5 -5
- package/out/{street-centroid-schema.js.map → street/centroid-schema.js.map} +1 -1
- package/out/{street-centroid.d.ts → street/centroid.d.ts} +6 -6
- package/out/street/centroid.d.ts.map +1 -0
- package/out/{street-centroid.js → street/centroid.js} +13 -19
- package/out/street/centroid.js.map +1 -0
- package/out/street/index.d.ts +13 -0
- package/out/street/index.d.ts.map +1 -0
- package/out/street/index.js +13 -0
- package/out/street/index.js.map +1 -0
- package/out/{street-morphology-fst-builder.d.ts → street/morphology-fst-builder.d.ts} +4 -4
- package/out/street/morphology-fst-builder.d.ts.map +1 -0
- package/out/{street-morphology-fst-builder.js → street/morphology-fst-builder.js} +18 -13
- package/out/street/morphology-fst-builder.js.map +1 -0
- package/out/{street-morphology-fst-loader.d.ts → street/morphology-fst-loader.d.ts} +5 -5
- package/out/street/morphology-fst-loader.d.ts.map +1 -0
- package/out/{street-morphology-fst-loader.js → street/morphology-fst-loader.js} +11 -10
- package/out/street/morphology-fst-loader.js.map +1 -0
- package/out/{street-name-lookup.d.ts → street/name-lookup.d.ts} +4 -7
- package/out/street/name-lookup.d.ts.map +1 -0
- package/out/{street-name-lookup.js → street/name-lookup.js} +9 -23
- package/out/street/name-lookup.js.map +1 -0
- package/out/{street-normalize.d.ts → street/normalize.d.ts} +25 -14
- package/out/street/normalize.d.ts.map +1 -0
- package/out/{street-normalize.js → street/normalize.js} +36 -17
- package/out/street/normalize.js.map +1 -0
- package/out/{street-segment-schema.d.ts → street/segment-schema.d.ts} +8 -8
- package/out/{street-segment-schema.d.ts.map → street/segment-schema.d.ts.map} +1 -1
- package/out/{street-segment-schema.js → street/segment-schema.js} +4 -4
- package/out/{street-segment-schema.js.map → street/segment-schema.js.map} +1 -1
- package/out/types.d.ts +18 -12
- package/out/types.d.ts.map +1 -1
- package/out/types.js.map +1 -1
- package/out/unified-schema.d.ts +5 -4
- package/out/unified-schema.d.ts.map +1 -1
- package/out/unified-schema.js +15 -18
- package/out/unified-schema.js.map +1 -1
- package/out/uprn/existence.d.ts +53 -0
- package/out/uprn/existence.d.ts.map +1 -0
- package/out/uprn/existence.js +61 -0
- package/out/uprn/existence.js.map +1 -0
- package/out/uprn/index.d.ts +9 -0
- package/out/uprn/index.d.ts.map +1 -0
- package/out/uprn/index.js +9 -0
- package/out/uprn/index.js.map +1 -0
- package/out/{uprn-lookup.d.ts → uprn/lookup.d.ts} +4 -4
- package/out/uprn/lookup.d.ts.map +1 -0
- package/out/{uprn-lookup.js → uprn/lookup.js} +11 -14
- package/out/uprn/lookup.js.map +1 -0
- package/out/{uprn-schema.d.ts → uprn/schema.d.ts} +4 -4
- package/out/uprn/schema.d.ts.map +1 -0
- package/out/{uprn-schema.js → uprn/schema.js} +4 -4
- package/out/uprn/schema.js.map +1 -0
- package/out/weights-overlay-linker.d.ts +184 -26
- package/out/weights-overlay-linker.d.ts.map +1 -1
- package/out/weights-overlay-linker.js +338 -86
- package/out/weights-overlay-linker.js.map +1 -1
- package/package.json +465 -269
- package/out/address-point-interpolation.d.ts.map +0 -1
- package/out/address-point-interpolation.js.map +0 -1
- package/out/address-point-schema.d.ts.map +0 -1
- package/out/address-point-schema.js.map +0 -1
- package/out/address-point.d.ts.map +0 -1
- package/out/address-point.js.map +0 -1
- package/out/ancestry-backfill.d.ts.map +0 -1
- package/out/ancestry-backfill.js.map +0 -1
- package/out/ancestry.d.ts.map +0 -1
- package/out/ancestry.js.map +0 -1
- package/out/candidate/shard-fold.d.ts +0 -31
- package/out/candidate/shard-fold.d.ts.map +0 -1
- package/out/candidate/shard-fold.js.map +0 -1
- package/out/convention-schema.d.ts.map +0 -1
- package/out/convention-schema.js.map +0 -1
- package/out/convention.d.ts.map +0 -1
- package/out/convention.js.map +0 -1
- package/out/fst-autocomplete.d.ts.map +0 -1
- package/out/fst-autocomplete.js.map +0 -1
- package/out/fst-builder.d.ts.map +0 -1
- package/out/fst-builder.js.map +0 -1
- package/out/fst-deserialize-web.d.ts.map +0 -1
- package/out/fst-deserialize-web.js.map +0 -1
- package/out/fst-freshness.d.ts.map +0 -1
- package/out/fst-freshness.js.map +0 -1
- package/out/fst-matcher.d.ts.map +0 -1
- package/out/fst-matcher.js.map +0 -1
- package/out/fst-serialize.d.ts.map +0 -1
- package/out/fst-serialize.js.map +0 -1
- package/out/fst-types.d.ts.map +0 -1
- package/out/fst-types.js.map +0 -1
- package/out/fts-query.d.ts.map +0 -1
- package/out/fts-query.js.map +0 -1
- package/out/fts.d.ts.map +0 -1
- package/out/fts.js.map +0 -1
- package/out/geonames-aliases.d.ts.map +0 -1
- package/out/geonames-aliases.js.map +0 -1
- package/out/geonames-postal.d.ts.map +0 -1
- package/out/geonames-postal.js.map +0 -1
- package/out/poi-lookup.d.ts.map +0 -1
- package/out/poi-lookup.js.map +0 -1
- package/out/poi-schema.d.ts.map +0 -1
- package/out/poi-schema.js.map +0 -1
- package/out/postal-city-alias-lookup.d.ts.map +0 -1
- package/out/postal-city-alias-lookup.js.map +0 -1
- package/out/sharding.d.ts +0 -114
- package/out/sharding.d.ts.map +0 -1
- package/out/sharding.js.map +0 -1
- package/out/street-centroid.d.ts.map +0 -1
- package/out/street-centroid.js.map +0 -1
- package/out/street-morphology-fst-builder.d.ts.map +0 -1
- package/out/street-morphology-fst-builder.js.map +0 -1
- package/out/street-morphology-fst-loader.d.ts.map +0 -1
- package/out/street-morphology-fst-loader.js.map +0 -1
- package/out/street-name-lookup.d.ts.map +0 -1
- package/out/street-name-lookup.js.map +0 -1
- package/out/street-normalize.d.ts.map +0 -1
- package/out/street-normalize.js.map +0 -1
- package/out/uprn-lookup.d.ts.map +0 -1
- package/out/uprn-lookup.js.map +0 -1
- package/out/uprn-schema.d.ts.map +0 -1
- package/out/uprn-schema.js.map +0 -1
- package/weights-overlay-linker.ts +0 -377
- /package/{name-score.ts → lib/name-score.ts} +0 -0
- /package/{polygon-schema.ts → lib/polygon-schema.ts} +0 -0
- /package/{postal-city-alias-schema.ts → lib/postal/city-alias-schema.ts} +0 -0
|
@@ -10,16 +10,14 @@
|
|
|
10
10
|
* See `docs/plan/phases/PHASE_4_2_wof_sqlite.md` for the design rationale.
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
import { DatabaseSync, type SQLInputValue } from "node:sqlite"
|
|
14
|
-
|
|
15
|
-
import { SqliteDialect } from "@mailwoman/core/kysley/dialect"
|
|
16
13
|
import { expandPlacetypeFilter, type Ancestor, type CoincidentLocality } from "@mailwoman/resolver"
|
|
17
14
|
import { haversineKm } from "@mailwoman/spatial"
|
|
18
|
-
import {
|
|
15
|
+
import type { SQLInputValue } from "@mailwoman/sqlite/client"
|
|
16
|
+
import { DatabaseClient } from "@mailwoman/sqlite/client"
|
|
19
17
|
|
|
20
|
-
import { ancestorLineage } from "
|
|
21
|
-
import { candidateFromSearchRow, rankCandidates } from "
|
|
22
|
-
import { loadCoincidentLocalities } from "
|
|
18
|
+
import { ancestorLineage } from "#ancestry/index"
|
|
19
|
+
import { candidateFromSearchRow, rankCandidates } from "#candidate-scoring"
|
|
20
|
+
import { loadCoincidentLocalities } from "#coincident-roles"
|
|
23
21
|
import {
|
|
24
22
|
ADDRESS_CONVENTION_TABLE,
|
|
25
23
|
resolveConvention,
|
|
@@ -28,8 +26,14 @@ import {
|
|
|
28
26
|
type ConventionSource,
|
|
29
27
|
type ResolvedConvention,
|
|
30
28
|
type Strategy,
|
|
31
|
-
} from "
|
|
32
|
-
import {
|
|
29
|
+
} from "#convention/index"
|
|
30
|
+
import {
|
|
31
|
+
pickExtractForPlacetype,
|
|
32
|
+
pickExtractsForPlacetype,
|
|
33
|
+
resolveExtracts,
|
|
34
|
+
type ResolvedExtract,
|
|
35
|
+
type ExtractConfig,
|
|
36
|
+
} from "#extracts"
|
|
33
37
|
import {
|
|
34
38
|
buildPlaceSearchFTS,
|
|
35
39
|
PLACE_BBOX_TABLE,
|
|
@@ -38,53 +42,47 @@ import {
|
|
|
38
42
|
placeBboxExists,
|
|
39
43
|
placePopulationExists,
|
|
40
44
|
placeSearchFTSExists,
|
|
41
|
-
} from "
|
|
42
|
-
import {
|
|
43
|
-
import {
|
|
44
|
-
import
|
|
45
|
-
import {
|
|
46
|
-
import type
|
|
47
|
-
import {
|
|
48
|
-
import {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
type ResolvedShard,
|
|
53
|
-
type ShardConfig,
|
|
54
|
-
} from "./sharding.ts"
|
|
55
|
-
import { SqliteConventionSource } from "./sqlite-convention-source.ts"
|
|
56
|
-
import { allRows } from "./sqlite-utils.ts"
|
|
57
|
-
import type { FindPlaceQuery, PlaceCandidate, PlaceLookup, WOFPlacetype } from "./types.ts"
|
|
45
|
+
} from "#fts/index"
|
|
46
|
+
import { normalizePlacetypes, sanitizeFTSQuery } from "#fts/query"
|
|
47
|
+
import { cfNormalize, softNameScore } from "#name-score"
|
|
48
|
+
import { encyclopedicClauses } from "#place-importance-schema"
|
|
49
|
+
import type { WOFPostalCityAliasLookup } from "#postal/city-alias-lookup"
|
|
50
|
+
import { DEFAULT_WEIGHTS, populationScaleTerm, type RankingWeights } from "#ranking-weights"
|
|
51
|
+
import type { WOFDatabase } from "#schema"
|
|
52
|
+
import { fetchSearchRows, type RawSearchRow } from "#search-fetch"
|
|
53
|
+
import { SqliteConventionSource } from "#sqlite-convention-source"
|
|
54
|
+
import { allRows } from "#sqlite-utils"
|
|
55
|
+
import type { FindPlaceQuery, PlaceCandidate, PlaceLookup, WOFPlacetype } from "#types"
|
|
58
56
|
|
|
59
57
|
export interface WOFSQLitePlaceLookupOpts {
|
|
60
58
|
/**
|
|
61
59
|
* Path to the WOF SQLite distribution on disk. Mutually exclusive with `database`.
|
|
62
60
|
*
|
|
63
|
-
* **Single string** — opens that one DB as the main
|
|
61
|
+
* **Single string** — opens that one DB as the main extract.
|
|
64
62
|
*
|
|
65
63
|
* **Array** — opens the first entry as main, then ATTACHes each subsequent entry as a separate SQLite schema. Schema
|
|
66
64
|
* names are derived from the filename (`whosonfirst-data-postalcode- us-latest.db` → `postalcode_us`); override with
|
|
67
|
-
* `
|
|
65
|
+
* `ExtractConfig.schemaName` when the filename doesn't follow WOF convention. See `extracts.ts` for the derivation
|
|
68
66
|
* rules.
|
|
69
67
|
*
|
|
70
|
-
* Routing: queries with a `placetype` matching a
|
|
71
|
-
* everything else hits main. Cross-
|
|
72
|
-
* corpora.
|
|
68
|
+
* Routing: queries with a `placetype` matching a extract's name (or explicit `placetypes` hint) are sent to that
|
|
69
|
+
* extract; everything else hits main. Cross-extract UNION is NOT done — BM25 isn't comparable across
|
|
70
|
+
* separately-indexed corpora.
|
|
73
71
|
*/
|
|
74
|
-
databasePath?: string | ReadonlyArray<string |
|
|
72
|
+
databasePath?: string | ReadonlyArray<string | ExtractConfig>
|
|
75
73
|
/**
|
|
76
|
-
* Pre-opened
|
|
77
|
-
* Multi-
|
|
74
|
+
* Pre-opened connection — primarily for tests against an inline fixture DB. Mutually exclusive with `databasePath`.
|
|
75
|
+
* Multi-extract requires `databasePath` (so the lookup owns the ATTACH).
|
|
78
76
|
*/
|
|
79
|
-
database?:
|
|
77
|
+
database?: DatabaseClient<WOFDatabase>
|
|
80
78
|
/**
|
|
81
79
|
* If true, build the FTS5 `place_search` virtual table on construction if it doesn't already exist. The upstream WOF
|
|
82
80
|
* distribution does NOT ship FTS5, so callers either set this once on first open or pre-build it via the
|
|
83
81
|
* operator-side CLI documented in the README. Default false — the resolver assumes the index already exists and
|
|
84
82
|
* errors loudly if it doesn't.
|
|
85
83
|
*
|
|
86
|
-
* With multi-
|
|
87
|
-
* `mailwoman gazetteer build fts` — operator script for predictable cost.
|
|
84
|
+
* With multi-extract, `buildFTS: true` builds the index on the **main** extract only. Other extracts must be
|
|
85
|
+
* pre-built via `mailwoman gazetteer build fts` — operator script for predictable cost.
|
|
88
86
|
*/
|
|
89
87
|
buildFTS?: boolean
|
|
90
88
|
/**
|
|
@@ -98,7 +96,8 @@ export interface WOFSQLitePlaceLookupOpts {
|
|
|
98
96
|
* Opt-in postal-city alias reader (#475). When supplied, the coordinate-first locality scorer treats an observed
|
|
99
97
|
* `postal_city` ("Antioch", postcode 37013) as a name-match alias for the geographic locality the postcode sits in
|
|
100
98
|
* ("Nashville"), recovering the chronic postal-vs- geographic-city mismatch. Absent (the default), the resolver is
|
|
101
|
-
* byte-identical — every alias code path is
|
|
99
|
+
* byte-identical — every alias code path is conditioned on this being non-null, so an unprovided reader changes no
|
|
100
|
+
* score.
|
|
102
101
|
*/
|
|
103
102
|
postalCityAliases?: WOFPostalCityAliasLookup
|
|
104
103
|
}
|
|
@@ -109,9 +108,9 @@ export interface WOFSQLitePlaceLookupOpts {
|
|
|
109
108
|
* - Nearby localities with WOF alt-name aliases.
|
|
110
109
|
*/
|
|
111
110
|
/**
|
|
112
|
-
* The placetypes `
|
|
113
|
-
* purpose-built
|
|
114
|
-
* to enumerate the gazetteer.
|
|
111
|
+
* The placetypes `pickExtractsForPlacetype`'s substring rule can route by name. Not every WOF placetype — only the ones
|
|
112
|
+
* a purpose-built extract is ever named for — so the diagnostic below can say "this name routes nowhere" without
|
|
113
|
+
* claiming to enumerate the gazetteer.
|
|
115
114
|
*/
|
|
116
115
|
const KNOWN_ROUTED_PLACETYPES: ReadonlyArray<string> = [
|
|
117
116
|
"postalcode",
|
|
@@ -139,44 +138,47 @@ const CF_PC_DECAY_KM = 8
|
|
|
139
138
|
const CF_MISMATCH_KM = 50
|
|
140
139
|
|
|
141
140
|
export class WOFSQLitePlaceLookup implements PlaceLookup, Disposable {
|
|
142
|
-
readonly #db:
|
|
143
|
-
|
|
144
|
-
|
|
141
|
+
readonly #db: DatabaseClient<WOFDatabase>
|
|
142
|
+
/**
|
|
143
|
+
* Resources this instance opened. A connection handed in by a caller is NOT in here, so disposal cannot reach it —
|
|
144
|
+
* ownership is membership rather than a flag a later branch has to check.
|
|
145
|
+
*/
|
|
146
|
+
readonly #resources = new DisposableStack()
|
|
145
147
|
readonly #weights: RankingWeights
|
|
146
148
|
/**
|
|
147
149
|
* Cached at construction so we don't `sqlite_master` query on every findPlace call. Bbox + near- with-radius queries
|
|
148
150
|
* fall back to no-filter when this is false, preserving compatibility with DBs that were FTS-built before the R*Tree
|
|
149
151
|
* shipped.
|
|
150
152
|
*
|
|
151
|
-
* Per-
|
|
153
|
+
* Per-extract: a extract is only considered to have the bbox index if its own R*Tree table exists.
|
|
152
154
|
*/
|
|
153
155
|
readonly #hasBboxIndex: Map<string, boolean>
|
|
154
156
|
/**
|
|
155
|
-
* Per-
|
|
157
|
+
* Per-extract probe for the `place_population` aux table. When false, the LEFT JOIN is omitted from the SELECT and
|
|
156
158
|
* population boost is 0 for every row — preserves compatibility with DBs built before this feature shipped.
|
|
157
159
|
*/
|
|
158
160
|
readonly #hasPopulationIndex: Map<string, boolean>
|
|
159
161
|
/**
|
|
160
|
-
* Per-
|
|
161
|
-
* built once at construction. Degrades to `NULL AS encyclopedic` with no join on a pre-split
|
|
162
|
-
*
|
|
162
|
+
* Per-extract SELECT term + LEFT JOIN for the two-score split's `encyclopedic` carry (ROAD_TO_V9 §2 R1), probed and
|
|
163
|
+
* built once at construction. Degrades to `NULL AS encyclopedic` with no join on a pre-split extract — every shipped
|
|
164
|
+
* extract today. See {@link encyclopedicClauses} for why the probe is a column and not a table.
|
|
163
165
|
*/
|
|
164
166
|
readonly #encyclopedicClauses: Map<string, { select: string; join: string }>
|
|
165
167
|
/**
|
|
166
|
-
* Per-
|
|
168
|
+
* Per-extract probe for the `postcode_locality` table (the coordinate-first candidate table, built by
|
|
167
169
|
* scripts/build-postcode-locality.ts). Cached at construction; null'd out when absent so the coord-first path
|
|
168
170
|
* silently no-ops on a deployment that didn't ship the table.
|
|
169
171
|
*/
|
|
170
|
-
readonly #
|
|
172
|
+
readonly #postcodeLocalityExtract: string | null
|
|
171
173
|
/**
|
|
172
|
-
* Resolved
|
|
173
|
-
* override) schema names.
|
|
174
|
+
* Resolved extract list. Always at least one entry; first is `main`. Multi-extract adds extras with their own derived
|
|
175
|
+
* (or override) schema names.
|
|
174
176
|
*/
|
|
175
|
-
readonly #
|
|
177
|
+
readonly #extracts: ResolvedExtract[]
|
|
176
178
|
/**
|
|
177
|
-
* #920: per-schema probed country sets for country-aware
|
|
179
|
+
* #920: per-schema probed country sets for country-aware extract routing (non-main extracts only).
|
|
178
180
|
*/
|
|
179
|
-
readonly #
|
|
181
|
+
readonly #extractCountries: Map<string, ReadonlySet<string>>
|
|
180
182
|
/**
|
|
181
183
|
* The Geographic Rule Engine (Direction E, #289). `#conventionSource` supplies per-WOF-polygon resolution profiles;
|
|
182
184
|
* `#strategies` is the named-primitive registry the merged convention dispatches. Empty source → every query resolves
|
|
@@ -201,7 +203,7 @@ export class WOFSQLitePlaceLookup implements PlaceLookup, Disposable {
|
|
|
201
203
|
readonly #ancestorsCache = new Map<number, Ancestor[]>()
|
|
202
204
|
/**
|
|
203
205
|
* Opt-in postal-city alias reader (#475). `null` unless `opts.postalCityAliases` was supplied — every alias code path
|
|
204
|
-
* is
|
|
206
|
+
* is conditioned on this, so the default resolver is byte-identical.
|
|
205
207
|
*/
|
|
206
208
|
readonly #postalCityAliases: WOFPostalCityAliasLookup | null
|
|
207
209
|
|
|
@@ -216,23 +218,21 @@ export class WOFSQLitePlaceLookup implements PlaceLookup, Disposable {
|
|
|
216
218
|
|
|
217
219
|
if (opts.database) {
|
|
218
220
|
this.#db = opts.database
|
|
219
|
-
this.#
|
|
220
|
-
this.#shards = [{ path: ":memory:", schemaName: "main", placetypes: [] }]
|
|
221
|
+
this.#extracts = [{ path: ":memory:", schemaName: "main", placetypes: [] }]
|
|
221
222
|
} else {
|
|
222
|
-
const
|
|
223
|
-
this.#
|
|
224
|
-
// Read-only by default — shipped gazetteer
|
|
223
|
+
const extracts = resolveExtracts(opts.databasePath!)
|
|
224
|
+
this.#extracts = extracts
|
|
225
|
+
// Read-only by default — shipped gazetteer extracts are sealed 0444 and Docker `:ro` mounts
|
|
225
226
|
// forbid write-mode opens, so a writable open fails there. The only code path that writes to
|
|
226
|
-
// the main
|
|
227
|
+
// the main extract is `#ensureFTS()` (FTS5 index build), conditioned on `opts.buildFTS`; open writable
|
|
227
228
|
// ONLY when that build was explicitly requested. Every read query (FTS5 MATCH, the aux-table
|
|
228
229
|
// SELECTs, ATTACH, and the `busy_timeout` PRAGMA) works read-only. See the docker read-only
|
|
229
230
|
// mount limitation (#1213).
|
|
230
|
-
this.#db = new
|
|
231
|
-
this.#ownsDB = true
|
|
231
|
+
this.#db = this.#resources.use(new DatabaseClient<WOFDatabase>(extracts[0]!.path, { readOnly: !opts.buildFTS }))
|
|
232
232
|
|
|
233
|
-
// ATTACH each non-main
|
|
233
|
+
// ATTACH each non-main extract. Schema names were validated by resolveExtracts, so safe to
|
|
234
234
|
// interpolate directly (SQLite ATTACH doesn't accept parameters for the schema name).
|
|
235
|
-
for (const s of
|
|
235
|
+
for (const s of extracts.slice(1)) {
|
|
236
236
|
this.#db.exec(`ATTACH DATABASE '${s.path.replaceAll("'", "''")}' AS ${s.schemaName}`)
|
|
237
237
|
}
|
|
238
238
|
}
|
|
@@ -246,56 +246,52 @@ export class WOFSQLitePlaceLookup implements PlaceLookup, Disposable {
|
|
|
246
246
|
this.#assertFTSExists()
|
|
247
247
|
}
|
|
248
248
|
|
|
249
|
-
this.#kysely = new Kysely<WOFDatabase>({
|
|
250
|
-
dialect: new SqliteDialect({ database: this.#db }),
|
|
251
|
-
})
|
|
252
|
-
|
|
253
249
|
this.#weights = { ...DEFAULT_WEIGHTS, ...weights }
|
|
254
250
|
|
|
255
|
-
// Probe each
|
|
251
|
+
// Probe each extract's aux-table presence — driven by per-extract table existence in
|
|
256
252
|
// sqlite_master. Cached at construction so findPlace doesn't query sqlite_master per call.
|
|
257
253
|
this.#hasBboxIndex = new Map()
|
|
258
254
|
this.#hasPopulationIndex = new Map()
|
|
259
255
|
this.#encyclopedicClauses = new Map()
|
|
260
256
|
|
|
261
|
-
for (const s of this.#
|
|
262
|
-
this.#hasBboxIndex.set(s.schemaName, this.#
|
|
263
|
-
this.#hasPopulationIndex.set(s.schemaName, this.#
|
|
257
|
+
for (const s of this.#extracts) {
|
|
258
|
+
this.#hasBboxIndex.set(s.schemaName, this.#extractHasTable(s.schemaName, PLACE_BBOX_TABLE))
|
|
259
|
+
this.#hasPopulationIndex.set(s.schemaName, this.#extractHasTable(s.schemaName, PLACE_POPULATION_TABLE))
|
|
264
260
|
this.#encyclopedicClauses.set(s.schemaName, encyclopedicClauses(this.#db, s.schemaName))
|
|
265
261
|
}
|
|
266
262
|
|
|
267
|
-
// Every lookup path here reaches `place_search`, and a
|
|
263
|
+
// Every lookup path here reaches `place_search`, and a extract without it fails in one of two ways
|
|
268
264
|
// that are both hard to read: an unroutable name returns zero hits (indistinguishable from "this
|
|
269
265
|
// country has no places") and a routable one throws mid-query from deep inside a SELECT. The
|
|
270
|
-
// unroutable half is the worse of the two — a
|
|
266
|
+
// unroutable half is the worse of the two — a extract reaches routing only through the name
|
|
271
267
|
// `deriveSchemaName` derives from its FILENAME, so a file spelled one letter off the placetype it
|
|
272
268
|
// serves answers with nothing while holding every row that was asked for.
|
|
273
269
|
//
|
|
274
|
-
// Two independent things bring a
|
|
275
|
-
// CLAIM to be a place
|
|
270
|
+
// Two independent things bring a extract under the guard, and it needs both. Carrying `spr` is a
|
|
271
|
+
// CLAIM to be a place extract. Carrying a name that routes is an INVITATION to be queried as one, and
|
|
276
272
|
// it is made by the filename alone — so a database with no tables at all still gets picked, still
|
|
277
273
|
// answers no query, and still dies inside a SELECT. Testing only the claim lets an empty or
|
|
278
274
|
// truncated file past construction; testing only the name would exempt a correctly-named build
|
|
279
|
-
// input. A
|
|
275
|
+
// input. A extract needs to fail neither test to be exempt.
|
|
280
276
|
//
|
|
281
277
|
// Exempt by design: `postcode-locality-<cc>.db` carries a relation table and nothing else, matches
|
|
282
|
-
// no routed placetype, and is part of the documented default
|
|
283
|
-
for (const s of this.#
|
|
278
|
+
// no routed placetype, and is part of the documented default extract list.
|
|
279
|
+
for (const s of this.#extracts) {
|
|
284
280
|
if (s.schemaName === "main") continue
|
|
285
281
|
|
|
286
282
|
const routes = KNOWN_ROUTED_PLACETYPES.some(
|
|
287
283
|
(pt) => s.schemaName === pt || s.schemaName.startsWith(`${pt}_`) || s.schemaName.endsWith(`_${pt}`)
|
|
288
284
|
)
|
|
289
285
|
|
|
290
|
-
const
|
|
286
|
+
const claimsPlaceExtract = this.#extractHasTable(s.schemaName, "spr")
|
|
291
287
|
|
|
292
|
-
if (!routes && !
|
|
288
|
+
if (!routes && !claimsPlaceExtract) continue
|
|
293
289
|
|
|
294
|
-
if (this.#
|
|
290
|
+
if (this.#extractHasTable(s.schemaName, PLACE_SEARCH_TABLE)) continue
|
|
295
291
|
|
|
296
292
|
throw new Error(
|
|
297
293
|
`WOFSQLitePlaceLookup: ${s.path} ` +
|
|
298
|
-
(
|
|
294
|
+
(claimsPlaceExtract
|
|
299
295
|
? `carries "spr" but no "${PLACE_SEARCH_TABLE}" table, so it cannot serve a lookup.`
|
|
300
296
|
: `is named for a routed placetype but carries neither "spr" nor "${PLACE_SEARCH_TABLE}", so every ` +
|
|
301
297
|
`query routed to it would die mid-SELECT. An empty or truncated file reads exactly like this.`) +
|
|
@@ -307,14 +303,14 @@ export class WOFSQLitePlaceLookup implements PlaceLookup, Disposable {
|
|
|
307
303
|
)
|
|
308
304
|
}
|
|
309
305
|
|
|
310
|
-
// #920 country-aware
|
|
311
|
-
// construction (they're small, purpose-built
|
|
306
|
+
// #920 country-aware extract routing: probe each NON-MAIN extract's country set once at
|
|
307
|
+
// construction (they're small, purpose-built extracts — postcode/locality slices; main is the
|
|
312
308
|
// multi-GB admin DB and is the fallback anyway, so it is deliberately NOT scanned). Feeds
|
|
313
|
-
//
|
|
314
|
-
// route by the query's country instead of first-match starving the second
|
|
315
|
-
this.#
|
|
309
|
+
// pickExtractForPlacetype so two postcode extracts (postalcode-us + postalcode-geonames-tail)
|
|
310
|
+
// route by the query's country instead of first-match starving the second extract.
|
|
311
|
+
this.#extractCountries = new Map()
|
|
316
312
|
|
|
317
|
-
for (const sh of this.#
|
|
313
|
+
for (const sh of this.#extracts) {
|
|
318
314
|
if (sh.schemaName === "main") continue
|
|
319
315
|
|
|
320
316
|
try {
|
|
@@ -322,36 +318,36 @@ export class WOFSQLitePlaceLookup implements PlaceLookup, Disposable {
|
|
|
322
318
|
.prepare(`SELECT DISTINCT country FROM ${sh.schemaName}.spr WHERE country != ''`)
|
|
323
319
|
.all() as Array<{ country: string }>
|
|
324
320
|
|
|
325
|
-
this.#
|
|
321
|
+
this.#extractCountries.set(sh.schemaName, new Set(rows.map((r) => r.country)))
|
|
326
322
|
} catch {
|
|
327
|
-
// A
|
|
323
|
+
// A extract without spr (or an attach oddity) just doesn't participate in country routing.
|
|
328
324
|
}
|
|
329
325
|
}
|
|
330
326
|
|
|
331
|
-
// The postcode_locality table can live on any attached
|
|
332
|
-
// `postcode-locality-<cc>.db`). Find the first
|
|
333
|
-
this.#
|
|
334
|
-
this.#
|
|
327
|
+
// The postcode_locality table can live on any attached extract (typically its own
|
|
328
|
+
// `postcode-locality-<cc>.db`). Find the first extract that has it; null = coord-first disabled.
|
|
329
|
+
this.#postcodeLocalityExtract =
|
|
330
|
+
this.#extracts.find((s) => this.#extractHasTable(s.schemaName, POSTCODE_LOCALITY_TABLE))?.schemaName ?? null
|
|
335
331
|
|
|
336
|
-
// Opt-in postal-city alias reader (#475). Construction-time present-or-not is the
|
|
332
|
+
// Opt-in postal-city alias reader (#475). Construction-time present-or-not is the check: null
|
|
337
333
|
// keeps the coordinate-first scorer byte-identical to pre-#475.
|
|
338
334
|
this.#postalCityAliases = opts.postalCityAliases ?? null
|
|
339
335
|
|
|
340
336
|
// The Geographic Rule Engine convention source. Precedence: an explicit `opts.conventions`
|
|
341
337
|
// (a ready source or a seed map) wins; else the build-from-source convention asset if one is
|
|
342
|
-
// attached (auto-detected, like the postcode_locality
|
|
338
|
+
// attached (auto-detected, like the postcode_locality extract — adding conventions.db to
|
|
343
339
|
// databasePath enables it; queried on demand, not paged into memory); else empty, so EU rides
|
|
344
340
|
// WORLD_DEFAULT. The registry binds strategy NAMES to the SQL-bound primitives — adding a
|
|
345
341
|
// strategy is registering it here.
|
|
346
|
-
const
|
|
347
|
-
this.#
|
|
342
|
+
const conventionExtract =
|
|
343
|
+
this.#extracts.find((s) => this.#extractHasTable(s.schemaName, ADDRESS_CONVENTION_TABLE))?.schemaName ?? null
|
|
348
344
|
|
|
349
345
|
this.#conventionSource = opts.conventions
|
|
350
346
|
? "get" in opts.conventions && typeof opts.conventions.get === "function"
|
|
351
347
|
? opts.conventions
|
|
352
348
|
: new SeedConventionSource(opts.conventions as Record<number, Convention>)
|
|
353
|
-
:
|
|
354
|
-
? new SqliteConventionSource(this.#db,
|
|
349
|
+
: conventionExtract
|
|
350
|
+
? new SqliteConventionSource(this.#db, conventionExtract)
|
|
355
351
|
: new SeedConventionSource()
|
|
356
352
|
|
|
357
353
|
this.#strategies = new Map<string, Strategy>([
|
|
@@ -360,8 +356,8 @@ export class WOFSQLitePlaceLookup implements PlaceLookup, Disposable {
|
|
|
360
356
|
])
|
|
361
357
|
}
|
|
362
358
|
|
|
363
|
-
#
|
|
364
|
-
// For main, the existing helpers work directly. For attached
|
|
359
|
+
#extractHasTable(schemaName: string, tableName: string): boolean {
|
|
360
|
+
// For main, the existing helpers work directly. For attached extracts we have to ask via the
|
|
365
361
|
// schema-qualified `sqlite_master` view.
|
|
366
362
|
if (schemaName === "main") {
|
|
367
363
|
if (tableName === PLACE_BBOX_TABLE) return placeBboxExists(this.#db)
|
|
@@ -410,9 +406,9 @@ export class WOFSQLitePlaceLookup implements PlaceLookup, Disposable {
|
|
|
410
406
|
// #924: NL postcode retry ladder. The WOF NL postalcode repo stores full codes UNSPACED
|
|
411
407
|
// ('1012LG') plus 4-digit stems ('1012'), while Dutch addresses carry the spaced form
|
|
412
408
|
// ('1012 LG') — two FTS tokens that can never match the one-token doc (the #920 name law,
|
|
413
|
-
// resurfacing in a WOF-built
|
|
409
|
+
// resurfacing in a WOF-built extract). On a postcode-typed NL-shape miss, retry ONCE with the
|
|
414
410
|
// whitespace-joined form (block-level precision when the full-code row exists), then the
|
|
415
|
-
// 4-digit stem (area-level). Country-
|
|
411
|
+
// 4-digit stem (area-level). Country-restricted to NL — the same digits+letters shape elsewhere
|
|
416
412
|
// must not silently coarsen to a different system's code. Each retry only fires when its
|
|
417
413
|
// text differs from the current one, so the ladder terminates by construction.
|
|
418
414
|
if (
|
|
@@ -501,31 +497,31 @@ export class WOFSQLitePlaceLookup implements PlaceLookup, Disposable {
|
|
|
501
497
|
}
|
|
502
498
|
|
|
503
499
|
/**
|
|
504
|
-
* Strategy `postcode_area_resolution` — the coordinate-first locality path, strictly
|
|
505
|
-
* postcode_locality table AND a locality query). Returns `null` — so the dispatcher falls through to the next
|
|
506
|
-
* strategy — when the
|
|
500
|
+
* Strategy `postcode_area_resolution` — the coordinate-first locality path, strictly conditioned (a sibling postcode
|
|
501
|
+
* AND a postcode_locality table AND a locality query). Returns `null` — so the dispatcher falls through to the next
|
|
502
|
+
* strategy — when the condition is unmet or the postcode isn't in the table; otherwise the soft-scored postcode∪name
|
|
507
503
|
* candidate set.
|
|
508
504
|
*/
|
|
509
505
|
#postcodeAreaResolution(query: FindPlaceQuery, convention: ResolvedConvention): Promise<PlaceCandidate[] | null> {
|
|
510
|
-
if (!(query.postcode && this.#
|
|
506
|
+
if (!(query.postcode && this.#postcodeLocalityExtract && this.#isLocalityQuery(query))) {
|
|
511
507
|
return Promise.resolve(null)
|
|
512
508
|
}
|
|
513
509
|
|
|
514
|
-
return this.#findLocalityCoordFirst(query, this.#
|
|
510
|
+
return this.#findLocalityCoordFirst(query, this.#postcodeLocalityExtract, convention)
|
|
515
511
|
}
|
|
516
512
|
|
|
517
513
|
/**
|
|
518
514
|
* Strategy `fallback_fuzzy_name_match` — the BM25 FTS name-match over the gazetteer, the universal fallback. Always
|
|
519
515
|
* returns an array (never null), so it terminates the dispatch chain.
|
|
520
516
|
*/
|
|
521
|
-
async #fuzzyNameMatch(query: FindPlaceQuery,
|
|
517
|
+
async #fuzzyNameMatch(query: FindPlaceQuery, forceExtract?: ResolvedExtract): Promise<PlaceCandidate[]> {
|
|
522
518
|
const limit = query.limit ?? 10
|
|
523
519
|
|
|
524
520
|
// Expand the placetype filter through the shared equivalence table (core/resolver): a
|
|
525
521
|
// `locality` query must also reach `borough` / `localadmin` rows — Brooklyn-the-borough
|
|
526
522
|
// (pop 2.5M) is a borough, not a locality, and a strict filter made it unreachable so the
|
|
527
523
|
// fuzzy "Brooklyn Park, MN" won instead. Order-preserving: the FIRST entry stays the
|
|
528
|
-
// requested placetype, which is what
|
|
524
|
+
// requested placetype, which is what extract routing keys off below.
|
|
529
525
|
const placetypes = expandPlacetypeFilter(normalizePlacetypes(query.placetype)) as WOFPlacetype[] | null
|
|
530
526
|
// Postcode-typed queries keep the #920 fused name-law shape; everything else splits on
|
|
531
527
|
// intra-token punctuation so hyphenated names reach the FTS as their real terms (#945).
|
|
@@ -533,20 +529,20 @@ export class WOFSQLitePlaceLookup implements PlaceLookup, Disposable {
|
|
|
533
529
|
|
|
534
530
|
if (!ftsQuery) return []
|
|
535
531
|
|
|
536
|
-
// Pick the
|
|
537
|
-
// `placetype` always goes to main. (Mixed-placetype queries with multiple
|
|
532
|
+
// Pick the extract for this query. Multi-extract routing is placetype-driven; a query without
|
|
533
|
+
// `placetype` always goes to main. (Mixed-placetype queries with multiple extracts aren't
|
|
538
534
|
// supported in v1 — caller can issue two findPlace calls and merge in TS if needed.)
|
|
539
535
|
const firstPlacetype = placetypes?.[0]
|
|
540
536
|
|
|
541
537
|
// Bias fan-out (#58/proximity-bias): a country-less query WITH proximity hints must see the
|
|
542
|
-
// cross-
|
|
543
|
-
// postalcode-intl, and single-
|
|
544
|
-
// (self-recursion with a
|
|
545
|
-
// keys the per-
|
|
538
|
+
// cross-extract ambiguity the hints exist to resolve — "48026" lives in postalcode-us AND
|
|
539
|
+
// postalcode-intl, and single-extract routing would hide one side. Query every matching extract
|
|
540
|
+
// (self-recursion with a extract pin), merge by id, and re-sort by the same (exact, prominence)
|
|
541
|
+
// keys the per-extract tier sort used. Bounded: hints + no country + >1 matching extract only.
|
|
546
542
|
const hasBiasHints = !!query.near || (query.bias?.length ?? 0) > 0
|
|
547
543
|
|
|
548
|
-
if (!
|
|
549
|
-
const matching =
|
|
544
|
+
if (!forceExtract && hasBiasHints && !query.country) {
|
|
545
|
+
const matching = pickExtractsForPlacetype(this.#extracts, firstPlacetype)
|
|
550
546
|
|
|
551
547
|
if (matching.length > 1) {
|
|
552
548
|
const pools: PlaceCandidate[][] = []
|
|
@@ -576,15 +572,15 @@ export class WOFSQLitePlaceLookup implements PlaceLookup, Disposable {
|
|
|
576
572
|
}
|
|
577
573
|
}
|
|
578
574
|
|
|
579
|
-
const
|
|
580
|
-
|
|
581
|
-
|
|
575
|
+
const extract =
|
|
576
|
+
forceExtract ??
|
|
577
|
+
pickExtractForPlacetype(this.#extracts, firstPlacetype, {
|
|
582
578
|
country: query.country,
|
|
583
|
-
countriesBySchema: this.#
|
|
579
|
+
countriesBySchema: this.#extractCountries,
|
|
584
580
|
})
|
|
585
581
|
|
|
586
582
|
// bare schema name; safe to interpolate (validated at construction)
|
|
587
|
-
const sch =
|
|
583
|
+
const sch = extract.schemaName
|
|
588
584
|
|
|
589
585
|
const rawRows = fetchSearchRows({
|
|
590
586
|
db: this.#db,
|
|
@@ -756,7 +752,7 @@ export class WOFSQLitePlaceLookup implements PlaceLookup, Disposable {
|
|
|
756
752
|
: wofAliases
|
|
757
753
|
|
|
758
754
|
const sName = softNameScore(query.text, cand.name, aliases)
|
|
759
|
-
const sPop = cand.population
|
|
755
|
+
const sPop = populationScaleTerm(cand.population, this.#weights)
|
|
760
756
|
scored.push({ ...cand, score: w.pc * sPc + w.name * sName + w.pop * sPop, exact: sName >= 1 })
|
|
761
757
|
}
|
|
762
758
|
|
|
@@ -840,18 +836,10 @@ export class WOFSQLitePlaceLookup implements PlaceLookup, Disposable {
|
|
|
840
836
|
})
|
|
841
837
|
}
|
|
842
838
|
|
|
843
|
-
close(): void {
|
|
844
|
-
// Destroying the Kysely instance closes the underlying connection IF we own it. If the caller
|
|
845
|
-
// passed in a pre-opened DatabaseSync (test fixture), respect their ownership.
|
|
846
|
-
void this.#kysely.destroy()
|
|
847
|
-
|
|
848
|
-
if (this.#ownsDB) {
|
|
849
|
-
this.#db.close()
|
|
850
|
-
}
|
|
851
|
-
}
|
|
852
|
-
|
|
853
839
|
[Symbol.dispose](): void {
|
|
854
|
-
this
|
|
840
|
+
// Only when we opened it. A caller who passed a pre-opened client keeps using it after this returns — the FTS
|
|
841
|
+
// build this lookup performed lives on their connection, and closing it would take that with us.
|
|
842
|
+
this.#resources[Symbol.dispose]()
|
|
855
843
|
}
|
|
856
844
|
|
|
857
845
|
/**
|
|
@@ -870,4 +858,4 @@ export class WOFSQLitePlaceLookup implements PlaceLookup, Disposable {
|
|
|
870
858
|
}
|
|
871
859
|
}
|
|
872
860
|
|
|
873
|
-
export type { RankingWeights } from "
|
|
861
|
+
export type { RankingWeights } from "#ranking-weights"
|