@mailwoman/resolver-wof-sqlite 9.0.0 → 9.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 +28 -9
- package/address-point-interpolation.ts +18 -8
- package/address-point-schema.ts +18 -6
- package/address-point.ts +111 -18
- package/ancestry.ts +9 -6
- package/build-candidate.ts +287 -157
- package/build-slim.ts +3 -3
- package/candidate/alias-bags.ts +54 -0
- package/candidate/ancestors-sidecar.ts +206 -0
- package/candidate/country-display-names.ts +79 -0
- package/candidate/name-roles.ts +237 -0
- package/candidate/own-name.ts +146 -0
- package/candidate/place-attrs.ts +44 -0
- package/candidate/shard-fold.ts +137 -0
- package/candidate-ancestors-schema.ts +195 -0
- package/candidate-fts.ts +4 -2
- package/candidate-importance.ts +228 -0
- package/candidate-lookup.ts +564 -174
- package/candidate-schema.ts +60 -3
- package/candidate-scoring.ts +268 -0
- package/capital-schema.ts +90 -0
- package/capitals.ts +148 -0
- package/coincident-roles.ts +69 -10
- package/convention-schema.ts +72 -0
- package/convention.ts +2 -2
- package/coverage-manifest-schema.ts +7 -7
- package/currency-backfill.ts +249 -0
- package/exact-match.ts +104 -0
- package/fst-autocomplete.ts +105 -122
- package/fst-builder.ts +39 -47
- package/fst-deserialize-web.ts +43 -7
- package/fst-freshness.ts +2 -2
- package/fst-serialize.ts +68 -12
- package/fst-types.ts +35 -1
- package/fts-query.ts +1 -1
- package/fts.ts +16 -4
- package/geonames-postal.ts +2 -2
- package/index.ts +26 -14
- package/interpolation.ts +113 -19
- package/lookup.ts +118 -560
- package/name-score.ts +6 -4
- package/out/address-point-interpolation.d.ts.map +1 -1
- package/out/address-point-interpolation.js +13 -7
- package/out/address-point-interpolation.js.map +1 -1
- package/out/address-point-schema.d.ts +16 -6
- package/out/address-point-schema.d.ts.map +1 -1
- package/out/address-point-schema.js.map +1 -1
- package/out/address-point.d.ts.map +1 -1
- package/out/address-point.js +70 -14
- package/out/address-point.js.map +1 -1
- package/out/ancestry.d.ts +2 -2
- package/out/ancestry.d.ts.map +1 -1
- package/out/ancestry.js +5 -6
- package/out/ancestry.js.map +1 -1
- package/out/build-candidate.d.ts +108 -0
- package/out/build-candidate.d.ts.map +1 -1
- package/out/build-candidate.js +151 -120
- package/out/build-candidate.js.map +1 -1
- package/out/build-slim.d.ts +1 -1
- package/out/build-slim.js +3 -3
- package/out/build-slim.js.map +1 -1
- package/out/candidate/alias-bags.d.ts +17 -0
- package/out/candidate/alias-bags.d.ts.map +1 -0
- package/out/candidate/alias-bags.js +39 -0
- package/out/candidate/alias-bags.js.map +1 -0
- package/out/candidate/ancestors-sidecar.d.ts +33 -0
- package/out/candidate/ancestors-sidecar.d.ts.map +1 -0
- package/out/candidate/ancestors-sidecar.js +140 -0
- package/out/candidate/ancestors-sidecar.js.map +1 -0
- package/out/candidate/country-display-names.d.ts +35 -0
- package/out/candidate/country-display-names.d.ts.map +1 -0
- package/out/candidate/country-display-names.js +59 -0
- package/out/candidate/country-display-names.js.map +1 -0
- package/out/candidate/name-roles.d.ts +55 -0
- package/out/candidate/name-roles.d.ts.map +1 -0
- package/out/candidate/name-roles.js +165 -0
- package/out/candidate/name-roles.js.map +1 -0
- package/out/candidate/own-name.d.ts +50 -0
- package/out/candidate/own-name.d.ts.map +1 -0
- package/out/candidate/own-name.js +132 -0
- package/out/candidate/own-name.js.map +1 -0
- package/out/candidate/place-attrs.d.ts +43 -0
- package/out/candidate/place-attrs.d.ts.map +1 -0
- package/out/candidate/place-attrs.js +15 -0
- package/out/candidate/place-attrs.js.map +1 -0
- package/out/candidate/shard-fold.d.ts +31 -0
- package/out/candidate/shard-fold.d.ts.map +1 -0
- package/out/candidate/shard-fold.js +104 -0
- package/out/candidate/shard-fold.js.map +1 -0
- package/out/candidate-ancestors-schema.d.ts +150 -0
- package/out/candidate-ancestors-schema.d.ts.map +1 -0
- package/out/candidate-ancestors-schema.js +123 -0
- package/out/candidate-ancestors-schema.js.map +1 -0
- package/out/candidate-fts.d.ts +4 -2
- package/out/candidate-fts.d.ts.map +1 -1
- package/out/candidate-fts.js +4 -2
- package/out/candidate-fts.js.map +1 -1
- package/out/candidate-importance.d.ts +132 -0
- package/out/candidate-importance.d.ts.map +1 -0
- package/out/candidate-importance.js +174 -0
- package/out/candidate-importance.js.map +1 -0
- package/out/candidate-lookup.d.ts +22 -37
- package/out/candidate-lookup.d.ts.map +1 -1
- package/out/candidate-lookup.js +446 -132
- package/out/candidate-lookup.js.map +1 -1
- package/out/candidate-schema.d.ts +52 -4
- package/out/candidate-schema.d.ts.map +1 -1
- package/out/candidate-schema.js +8 -0
- package/out/candidate-schema.js.map +1 -1
- package/out/candidate-scoring.d.ts +34 -0
- package/out/candidate-scoring.d.ts.map +1 -0
- package/out/candidate-scoring.js +200 -0
- package/out/candidate-scoring.js.map +1 -0
- package/out/capital-schema.d.ts +51 -0
- package/out/capital-schema.d.ts.map +1 -0
- package/out/capital-schema.js +63 -0
- package/out/capital-schema.js.map +1 -0
- package/out/capitals.d.ts +69 -0
- package/out/capitals.d.ts.map +1 -0
- package/out/capitals.js +98 -0
- package/out/capitals.js.map +1 -0
- package/out/coincident-roles.d.ts +7 -0
- package/out/coincident-roles.d.ts.map +1 -1
- package/out/coincident-roles.js +42 -8
- package/out/coincident-roles.js.map +1 -1
- package/out/convention-schema.d.ts +51 -0
- package/out/convention-schema.d.ts.map +1 -0
- package/out/convention-schema.js +34 -0
- package/out/convention-schema.js.map +1 -0
- package/out/convention.d.ts +1 -1
- package/out/convention.js +2 -2
- package/out/coverage-manifest-schema.js +3 -7
- package/out/coverage-manifest-schema.js.map +1 -1
- package/out/currency-backfill.d.ts +46 -0
- package/out/currency-backfill.d.ts.map +1 -0
- package/out/currency-backfill.js +180 -0
- package/out/currency-backfill.js.map +1 -0
- package/out/exact-match.d.ts +25 -0
- package/out/exact-match.d.ts.map +1 -0
- package/out/exact-match.js +89 -0
- package/out/exact-match.js.map +1 -0
- package/out/fst-autocomplete.d.ts +24 -14
- package/out/fst-autocomplete.d.ts.map +1 -1
- package/out/fst-autocomplete.js +84 -100
- package/out/fst-autocomplete.js.map +1 -1
- package/out/fst-builder.d.ts.map +1 -1
- package/out/fst-builder.js +32 -40
- package/out/fst-builder.js.map +1 -1
- package/out/fst-deserialize-web.d.ts.map +1 -1
- package/out/fst-deserialize-web.js +36 -7
- package/out/fst-deserialize-web.js.map +1 -1
- package/out/fst-freshness.d.ts +2 -2
- package/out/fst-freshness.js +2 -2
- package/out/fst-serialize.d.ts +14 -4
- package/out/fst-serialize.d.ts.map +1 -1
- package/out/fst-serialize.js +60 -12
- package/out/fst-serialize.js.map +1 -1
- package/out/fst-types.d.ts +35 -1
- package/out/fst-types.d.ts.map +1 -1
- package/out/fts-query.js +1 -1
- package/out/fts-query.js.map +1 -1
- package/out/fts.d.ts +15 -4
- package/out/fts.d.ts.map +1 -1
- package/out/fts.js +15 -4
- package/out/fts.js.map +1 -1
- package/out/geonames-postal.d.ts +2 -2
- package/out/geonames-postal.js +2 -2
- package/out/index.d.ts +4 -2
- package/out/index.d.ts.map +1 -1
- package/out/index.js +3 -2
- package/out/index.js.map +1 -1
- package/out/interpolation.d.ts +8 -0
- package/out/interpolation.d.ts.map +1 -1
- package/out/interpolation.js +91 -19
- package/out/interpolation.js.map +1 -1
- package/out/lookup.d.ts +4 -5
- package/out/lookup.d.ts.map +1 -1
- package/out/lookup.js +102 -444
- package/out/lookup.js.map +1 -1
- package/out/name-score.d.ts +0 -10
- package/out/name-score.d.ts.map +1 -1
- package/out/name-score.js +6 -4
- package/out/name-score.js.map +1 -1
- package/out/place-importance-schema.d.ts +226 -0
- package/out/place-importance-schema.d.ts.map +1 -0
- package/out/place-importance-schema.js +288 -0
- package/out/place-importance-schema.js.map +1 -0
- package/out/poi-lookup.d.ts +1 -1
- package/out/poi-lookup.d.ts.map +1 -1
- package/out/poi-lookup.js +12 -13
- package/out/poi-lookup.js.map +1 -1
- package/out/poi-schema.d.ts +7 -3
- package/out/poi-schema.d.ts.map +1 -1
- package/out/poi-schema.js.map +1 -1
- package/out/polygon-schema.d.ts +37 -0
- package/out/polygon-schema.d.ts.map +1 -0
- package/out/polygon-schema.js +23 -0
- package/out/polygon-schema.js.map +1 -0
- package/out/postal-city-alias-lookup.d.ts +1 -1
- package/out/postal-city-alias-lookup.js +1 -1
- package/out/postal-city-candidate-schema.d.ts +2 -1
- package/out/postal-city-candidate-schema.d.ts.map +1 -1
- package/out/postal-city-candidate-schema.js.map +1 -1
- package/out/postcode-point-lookup.d.ts +1 -1
- package/out/postcode-point-lookup.js +1 -1
- package/out/primary-preference.d.ts +125 -0
- package/out/primary-preference.d.ts.map +1 -0
- package/out/primary-preference.js +138 -0
- package/out/primary-preference.js.map +1 -0
- package/out/proximity-rerank.d.ts +77 -0
- package/out/proximity-rerank.d.ts.map +1 -0
- package/out/proximity-rerank.js +86 -0
- package/out/proximity-rerank.js.map +1 -0
- package/out/region-keys.d.ts +47 -0
- package/out/region-keys.d.ts.map +1 -0
- package/out/region-keys.js +121 -0
- package/out/region-keys.js.map +1 -0
- package/out/reverse.d.ts.map +1 -1
- package/out/reverse.js +6 -9
- package/out/reverse.js.map +1 -1
- package/out/schema.d.ts +1 -1
- package/out/search-fetch.d.ts +57 -0
- package/out/search-fetch.d.ts.map +1 -0
- package/out/search-fetch.js +183 -0
- package/out/search-fetch.js.map +1 -0
- package/out/sharding.d.ts +3 -3
- package/out/sharding.js +1 -1
- package/out/sqlite-convention-source.d.ts +1 -1
- package/out/sqlite-convention-source.js +1 -1
- package/out/sqlite-utils.d.ts +31 -1
- package/out/sqlite-utils.d.ts.map +1 -1
- package/out/sqlite-utils.js +38 -0
- package/out/sqlite-utils.js.map +1 -1
- package/out/street-centroid-schema.d.ts +7 -2
- package/out/street-centroid-schema.d.ts.map +1 -1
- package/out/street-centroid-schema.js.map +1 -1
- package/out/street-centroid.d.ts.map +1 -1
- package/out/street-centroid.js +7 -7
- package/out/street-centroid.js.map +1 -1
- package/out/street-morphology-fst-builder.d.ts.map +1 -1
- package/out/street-morphology-fst-builder.js +5 -4
- package/out/street-morphology-fst-builder.js.map +1 -1
- package/out/street-normalize.d.ts +83 -9
- package/out/street-normalize.d.ts.map +1 -1
- package/out/street-normalize.js +177 -10
- package/out/street-normalize.js.map +1 -1
- package/out/street-segment-schema.d.ts +6 -2
- package/out/street-segment-schema.d.ts.map +1 -1
- package/out/street-segment-schema.js.map +1 -1
- package/out/types.d.ts +74 -1
- package/out/types.d.ts.map +1 -1
- package/out/unified-schema.d.ts +1 -1
- package/out/unified-schema.js +1 -1
- package/out/uprn-lookup.d.ts +85 -0
- package/out/uprn-lookup.d.ts.map +1 -0
- package/out/uprn-lookup.js +152 -0
- package/out/uprn-lookup.js.map +1 -0
- package/out/uprn-schema.d.ts +93 -0
- package/out/uprn-schema.d.ts.map +1 -0
- package/out/uprn-schema.js +78 -0
- package/out/uprn-schema.js.map +1 -0
- package/out/weights-overlay-linker.d.ts +141 -0
- package/out/weights-overlay-linker.d.ts.map +1 -0
- package/out/weights-overlay-linker.js +259 -0
- package/out/weights-overlay-linker.js.map +1 -0
- package/package.json +296 -16
- package/place-importance-schema.ts +402 -0
- package/poi-lookup.ts +12 -13
- package/poi-schema.ts +8 -3
- package/polygon-schema.ts +47 -0
- package/postal-city-alias-lookup.ts +1 -1
- package/postal-city-candidate-schema.ts +3 -1
- package/postcode-point-lookup.ts +1 -1
- package/primary-preference.ts +207 -0
- package/proximity-rerank.ts +120 -0
- package/region-keys.ts +144 -0
- package/reverse.ts +17 -16
- package/schema.ts +1 -1
- package/search-fetch.ts +256 -0
- package/sharding.ts +3 -3
- package/sqlite-convention-source.ts +1 -1
- package/sqlite-utils.ts +63 -1
- package/street-centroid-schema.ts +8 -2
- package/street-centroid.ts +13 -8
- package/street-morphology-fst-builder.ts +5 -4
- package/street-normalize.ts +254 -24
- package/street-segment-schema.ts +7 -2
- package/types.ts +74 -1
- package/unified-schema.ts +1 -1
- package/uprn-lookup.ts +210 -0
- package/uprn-schema.ts +124 -0
- package/weights-overlay-linker.ts +377 -0
- package/geo.ts +0 -121
- package/out/geo.d.ts +0 -74
- package/out/geo.d.ts.map +0 -1
- package/out/geo.js +0 -71
- package/out/geo.js.map +0 -1
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @copyright Sister Software
|
|
3
|
+
* @license AGPL-3.0
|
|
4
|
+
* @author Teffen Ellis, et al.
|
|
5
|
+
*
|
|
6
|
+
* The comparable-key expansion for a parsed REGION qualifier — one function, both consumers (the
|
|
7
|
+
* #861 rule). The admin-coherence verdicts (`mailwoman/admin-coherence.ts`) fold a qualifier and a
|
|
8
|
+
* winner-ancestry name through this to decide `confirmed`/`contradicted`; the candidate backend's
|
|
9
|
+
* admin-containment re-rank (#1717 stage 2, `candidate-lookup.ts`) folds the same qualifier through
|
|
10
|
+
* it to find the qualifier's own region-class rows in the candidate table. A check that says
|
|
11
|
+
* `contradicted` and a re-rank that cannot find the qualifier would otherwise be two readings of
|
|
12
|
+
* one string that silently disagree — the exact drift the shared-function rule exists to prevent.
|
|
13
|
+
*
|
|
14
|
+
* Lives HERE rather than in `mailwoman` because the dependency points this way: `mailwoman`
|
|
15
|
+
* depends on `@mailwoman/resolver-wof-sqlite` (which owns the fold and already depends on codex),
|
|
16
|
+
* never the reverse.
|
|
17
|
+
*/
|
|
18
|
+
import { matchSubdivision, matchSubdivisionIn } from "@mailwoman/codex/country";
|
|
19
|
+
import { normalizeLocalityForKey } from "./street-normalize.js";
|
|
20
|
+
/**
|
|
21
|
+
* The ancestry placetypes that answer for a parsed `region` qualifier — WOF's admin band between country and locality.
|
|
22
|
+
* Deliberately the whole band: a qualifier stated at any grain ("Lancashire", a ceremonial county; "Thüringen", a Land)
|
|
23
|
+
* may confirm against whichever level the backend stored, and `contradicted` requires the entire band to miss, so
|
|
24
|
+
* widening the band only ever makes the check more conservative.
|
|
25
|
+
*/
|
|
26
|
+
export const REGION_CLASS_PLACETYPES = new Set(["region", "macroregion", "county", "macrocounty"]);
|
|
27
|
+
/**
|
|
28
|
+
* County-style qualifier prefixes stripped to produce a comparison VARIANT. Ireland writes `Co. Westmeath` where WOF
|
|
29
|
+
* stores `Westmeath`, so the prefix defeats the fold and every Irish county qualifier read `contradicted` on the first
|
|
30
|
+
* board census (2026-08-17, five rows). The stripped form is ADDED to the key set, never substituted — `County Durham`
|
|
31
|
+
* is a real name whose stripped variant simply also matches, and a set union can only widen confirmation, so the
|
|
32
|
+
* closure is monotone: `contradicted → confirmed` is the only movement it can cause.
|
|
33
|
+
*/
|
|
34
|
+
const COUNTY_QUALIFIER_PREFIXES = ["county", "co.", "co"];
|
|
35
|
+
/**
|
|
36
|
+
* Trailing admin-qualifier words, the suffix sibling of the prefix above: `San José Province` (CR board row) folds
|
|
37
|
+
* against stored `San José` only with the word removed. Same monotone rule — the stripped form joins the set, never
|
|
38
|
+
* replaces the original.
|
|
39
|
+
*/
|
|
40
|
+
const ADMIN_QUALIFIER_SUFFIXES = ["province", "prov.", "prov"];
|
|
41
|
+
function withoutPrefix(value, prefixes) {
|
|
42
|
+
const folded = value.toLowerCase();
|
|
43
|
+
for (const prefix of prefixes) {
|
|
44
|
+
if (!folded.startsWith(prefix) || !/\s/u.test(value[prefix.length] ?? ""))
|
|
45
|
+
continue;
|
|
46
|
+
let offset = prefix.length;
|
|
47
|
+
while (/\s/u.test(value[offset] ?? "")) {
|
|
48
|
+
offset++;
|
|
49
|
+
}
|
|
50
|
+
return value.slice(offset);
|
|
51
|
+
}
|
|
52
|
+
return value;
|
|
53
|
+
}
|
|
54
|
+
function withoutSuffix(value, suffixes) {
|
|
55
|
+
const folded = value.toLowerCase();
|
|
56
|
+
for (const suffix of suffixes) {
|
|
57
|
+
const offset = value.length - suffix.length;
|
|
58
|
+
if (offset <= 0 || !folded.endsWith(suffix) || !/\s/u.test(value[offset - 1] ?? ""))
|
|
59
|
+
continue;
|
|
60
|
+
let end = offset;
|
|
61
|
+
while (end > 0 && /\s/u.test(value[end - 1] ?? "")) {
|
|
62
|
+
end--;
|
|
63
|
+
}
|
|
64
|
+
return value.slice(0, end);
|
|
65
|
+
}
|
|
66
|
+
return value;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* The comparable keys a region string expands to: its own fold (the shared candidate.db `name_key` normalizer,
|
|
70
|
+
* {@link normalizeLocalityForKey} — build side and check side agree by construction); a county-prefix-stripped variant;
|
|
71
|
+
* the codex subdivision expansions — the disjoint US+CA table always, plus the COUNTRY-SCOPED table when the caller
|
|
72
|
+
* knows a country (`WA` under AU is Western Australia; under US, Washington — the collision that keeps AU out of the
|
|
73
|
+
* unscoped table). Every expansion lands the canonical name and code folds in the set, so `IL`/`Illinois` and
|
|
74
|
+
* `WA`/`Western Australia` meet from either side.
|
|
75
|
+
*/
|
|
76
|
+
export function regionKeys(value, countryAlpha2) {
|
|
77
|
+
const keys = new Set([normalizeLocalityForKey(value)]);
|
|
78
|
+
for (const stripped of [
|
|
79
|
+
withoutPrefix(value, COUNTY_QUALIFIER_PREFIXES),
|
|
80
|
+
withoutSuffix(value, ADMIN_QUALIFIER_SUFFIXES),
|
|
81
|
+
]) {
|
|
82
|
+
if (stripped !== value && stripped.trim()) {
|
|
83
|
+
keys.add(normalizeLocalityForKey(stripped));
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
const expansions = [matchSubdivision(value), countryAlpha2 ? matchSubdivisionIn(countryAlpha2, value) : null];
|
|
87
|
+
for (const subdivision of expansions) {
|
|
88
|
+
if (subdivision) {
|
|
89
|
+
keys.add(normalizeLocalityForKey(subdivision.name));
|
|
90
|
+
keys.add(normalizeLocalityForKey(subdivision.code));
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
// The empty fold stays IN the set on purpose — the admin-coherence verdicts have always compared
|
|
94
|
+
// the empty key (two empty-folding strings intersect → `confirmed`), and this move must not shift
|
|
95
|
+
// a verdict. A consumer probing a table by key filters the empty string out itself.
|
|
96
|
+
return keys;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* The PROBE-side expansion for a region qualifier — {@link regionKeys} plus the county-PREFIXED variant of every key.
|
|
100
|
+
*
|
|
101
|
+
* The verdict machinery intersects two {@link regionKeys} SETS, so `Co. Donegal` meets stored `County Donegal` at the
|
|
102
|
+
* shared stripped key `donegal`. A table probe is one-sided: it matches the STORED fold verbatim, and WOF stores Irish
|
|
103
|
+
* counties under `county donegal` with no bare `donegal` key (measured on the shipped candidate.db — the qualifier
|
|
104
|
+
* probe missed every Irish county until this variant landed). Adding `county <key>` restores the two-sidedness for the
|
|
105
|
+
* one stored-form family with an evidenced case; the union is monotone (a wider qualifier set can only find more
|
|
106
|
+
* BEARERS, each of which must still genuinely contain a candidate before anything moves). The suffix sibling (`<key>
|
|
107
|
+
* province`) is deliberately absent — no stored-form case has been evidenced, and a lever without a board does not get
|
|
108
|
+
* built.
|
|
109
|
+
*/
|
|
110
|
+
export function regionQualifierProbeKeys(value, countryAlpha2) {
|
|
111
|
+
const keys = regionKeys(value, countryAlpha2);
|
|
112
|
+
// Snapshot before widening: the loop adds `county <key>` members that must not themselves be revisited.
|
|
113
|
+
const bare = [...keys];
|
|
114
|
+
for (const key of bare) {
|
|
115
|
+
if (key && !key.startsWith("county ")) {
|
|
116
|
+
keys.add(`county ${key}`);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return keys;
|
|
120
|
+
}
|
|
121
|
+
//# sourceMappingURL=region-keys.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"region-keys.js","sourceRoot":"","sources":["../region-keys.ts"],"names":[],"mappings":"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"}
|
package/out/reverse.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reverse.d.ts","sourceRoot":"","sources":["../reverse.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"reverse.d.ts","sourceRoot":"","sources":["../reverse.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAQ1C,OAAO,KAAK,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAY9D;;;;;;;GAOG;AACH,MAAM,MAAM,eAAe,GAAG,SAAS,GAAG,aAAa,CAAA;AAEvD,MAAM,WAAW,oBAAoB;IACpC;;;;OAIG;IACH,SAAS,EAAE,cAAc,EAAE,CAAA;IAC3B;;OAEG;IACH,WAAW,EAAE,eAAe,CAAA;CAC5B;AAED,MAAM,WAAW,sBAAsB;IACtC;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB;;OAEG;IACH,aAAa,CAAC,EAAE,YAAY,CAAA;IAC5B;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB;;OAEG;IACH,eAAe,CAAC,EAAE,YAAY,CAAA;CAC9B;AAED,MAAM,WAAW,kBAAkB;IAClC;;;OAGG;IACH,UAAU,CAAC,EAAE,YAAY,EAAE,CAAA;IAC3B;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAA;CACzB;AAkDD,qBAAa,kBAAmB,YAAW,UAAU;;gBAcxC,IAAI,EAAE,sBAAsB;IAgDxC;;;OAGG;IACG,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,GAAE,kBAAuB,GAAG,OAAO,CAAC,oBAAoB,CAAC;IAI5G;;;;;OAKG;IACH,kBAAkB,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,GAAE,kBAAuB,GAAG,oBAAoB;IA6NjG,KAAK,IAAI,IAAI;IAUb,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,IAAI;CAGxB"}
|
package/out/reverse.js
CHANGED
|
@@ -29,9 +29,10 @@
|
|
|
29
29
|
*/
|
|
30
30
|
import { DatabaseSync } from "node:sqlite";
|
|
31
31
|
import { tryParsingJSON } from "@mailwoman/core/objects";
|
|
32
|
+
import { geometryContains, haversineKm } from "@mailwoman/spatial";
|
|
32
33
|
import { ancestorLineage, placetypeDepth } from "./ancestry.js";
|
|
33
34
|
import { PLACE_BBOX_TABLE } from "./fts.js";
|
|
34
|
-
import {
|
|
35
|
+
import { allRows } from "./sqlite-utils.js";
|
|
35
36
|
/**
|
|
36
37
|
* Largest absolute latitude in WGS-84 degrees.
|
|
37
38
|
*/
|
|
@@ -254,14 +255,12 @@ export class WOFReverseGeocoder {
|
|
|
254
255
|
params.push(...opts.placetypes);
|
|
255
256
|
}
|
|
256
257
|
params.push(opts.maxCandidates ?? DEFAULT_MAX_CANDIDATES);
|
|
257
|
-
return this.#admin
|
|
258
|
-
.prepare(`SELECT spr.id AS id, spr.name AS name, spr.placetype AS placetype, spr.country AS country,
|
|
258
|
+
return allRows(this.#admin.prepare(`SELECT spr.id AS id, spr.name AS name, spr.placetype AS placetype, spr.country AS country,
|
|
259
259
|
spr.parent_id AS parent_id, spr.latitude AS lat, spr.longitude AS lon
|
|
260
260
|
FROM ${PLACE_BBOX_TABLE} bbox JOIN spr ON spr.id = bbox.id
|
|
261
261
|
WHERE ${where.join(" AND ")}
|
|
262
262
|
ORDER BY (bbox.max_lat - bbox.min_lat) * (bbox.max_lon - bbox.min_lon) ASC
|
|
263
|
-
LIMIT ?`)
|
|
264
|
-
.all(...params);
|
|
263
|
+
LIMIT ?`), ...params);
|
|
265
264
|
}
|
|
266
265
|
/**
|
|
267
266
|
* Descendants of `parentID` at one placetype tier, pre-filtered to a centroid window around the query point (a
|
|
@@ -270,13 +269,11 @@ export class WOFReverseGeocoder {
|
|
|
270
269
|
*/
|
|
271
270
|
#descendants(parentID, placetype, lat, lon, maxApproximateKm) {
|
|
272
271
|
const windowDeg = (maxApproximateKm * 4) / 111;
|
|
273
|
-
return this.#admin
|
|
274
|
-
.prepare(`SELECT s.id AS id, s.name AS name, s.placetype AS placetype, s.country AS country,
|
|
272
|
+
return allRows(this.#admin.prepare(`SELECT s.id AS id, s.name AS name, s.placetype AS placetype, s.country AS country,
|
|
275
273
|
s.parent_id AS parent_id, s.latitude AS lat, s.longitude AS lon
|
|
276
274
|
FROM ancestors a JOIN spr s ON s.id = a.id
|
|
277
275
|
WHERE a.ancestor_id = ? AND s.placetype = ? AND s.is_current != 0 AND s.is_deprecated = 0
|
|
278
|
-
AND s.latitude BETWEEN ? AND ? AND s.longitude BETWEEN ? AND ?`)
|
|
279
|
-
.all(parentID, placetype, lat - windowDeg, lat + windowDeg, lon - windowDeg, lon + windowDeg);
|
|
276
|
+
AND s.latitude BETWEEN ? AND ? AND s.longitude BETWEEN ? AND ?`), parentID, placetype, lat - windowDeg, lat + windowDeg, lon - windowDeg, lon + windowDeg);
|
|
280
277
|
}
|
|
281
278
|
/**
|
|
282
279
|
* Parsed GeoJSON geometry for a WOF id, or null when absent / unparseable / no polygon DB.
|
package/out/reverse.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reverse.js","sourceRoot":"","sources":["../reverse.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAE1C,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAA;
|
|
1
|
+
{"version":3,"file":"reverse.js","sourceRoot":"","sources":["../reverse.ts"],"names":[],"mappings":"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"}
|
package/out/schema.d.ts
CHANGED
|
@@ -149,7 +149,7 @@ export interface CoincidentRolesTable {
|
|
|
149
149
|
}
|
|
150
150
|
/**
|
|
151
151
|
* The full schema we hand to `Kysely<WOFDatabase>` / `new DatabaseClient<WOFDatabase>(...)`. Tables not listed here
|
|
152
|
-
* will fail type-checked queries — by design. The reader ({@link
|
|
152
|
+
* will fail type-checked queries — by design. The reader ({@link WOFSQLitePlaceLookup}) already consumes this; the
|
|
153
153
|
* build/augment WRITERS adopt it so a column rename is a compile error on both sides (the drift that bit the corpus
|
|
154
154
|
* TIGER adapter).
|
|
155
155
|
*/
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @copyright Sister Software
|
|
3
|
+
* @license AGPL-3.0
|
|
4
|
+
* @author Teffen Ellis, et al.
|
|
5
|
+
*
|
|
6
|
+
* The FTS5 candidate fetch behind the fuzzy name match: the schema-qualified `place_search`
|
|
7
|
+
* MATCH, its population-ordered companion fetch, and the raw row shape both of them return.
|
|
8
|
+
*
|
|
9
|
+
* Bbox and near-with-radius narrow at the SQL level through SQLite's built-in `rtree`, whose index name and schema
|
|
10
|
+
* live in `fts.ts` beside the FTS5 build. That is why this package pulls neither SpatiaLite nor turf: the R*Tree does
|
|
11
|
+
* the narrowing, and the passes downstream of it operate on ≤ a few hundred candidates per query rather than the
|
|
12
|
+
* whole corpus, so an exact haversine over the survivors is cheap.
|
|
13
|
+
*/
|
|
14
|
+
import type { DatabaseSync } from "node:sqlite";
|
|
15
|
+
import type { RankingWeights } from "./ranking-weights.ts";
|
|
16
|
+
import type { FindPlaceQuery, WOFPlacetype } from "./types.ts";
|
|
17
|
+
export interface RawSearchRow {
|
|
18
|
+
id: number;
|
|
19
|
+
name: string;
|
|
20
|
+
placetype: string;
|
|
21
|
+
country: string | null;
|
|
22
|
+
parent_id: number | null;
|
|
23
|
+
rank: number;
|
|
24
|
+
lat: number | null;
|
|
25
|
+
lon: number | null;
|
|
26
|
+
min_latitude: number | null;
|
|
27
|
+
max_latitude: number | null;
|
|
28
|
+
min_longitude: number | null;
|
|
29
|
+
max_longitude: number | null;
|
|
30
|
+
population: number | null;
|
|
31
|
+
/**
|
|
32
|
+
* From `place_importance.encyclopedic` when the shard's table carries the two-score split columns. NULL means the
|
|
33
|
+
* place has no Wikipedia article, or the shard predates the split — absence either way, and never 0 (ROAD_TO_V9 §2).
|
|
34
|
+
*/
|
|
35
|
+
encyclopedic: number | null;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Fetch the raw candidate rows for a name match on one shard: the BM25-ordered window over `place_search` (widened for
|
|
39
|
+
* short queries), plus the population-ordered companion fetch that keeps the prominent holders of a name pool-complete.
|
|
40
|
+
* `schemaName` is the routed shard's bare schema name — validated at construction, so it is interpolated directly.
|
|
41
|
+
*/
|
|
42
|
+
export declare function fetchSearchRows(options: {
|
|
43
|
+
db: DatabaseSync;
|
|
44
|
+
schemaName: string;
|
|
45
|
+
query: FindPlaceQuery;
|
|
46
|
+
placetypes: WOFPlacetype[] | null;
|
|
47
|
+
ftsQuery: string;
|
|
48
|
+
limit: number;
|
|
49
|
+
hasBboxIndex: ReadonlyMap<string, boolean>;
|
|
50
|
+
hasPopulationIndex: ReadonlyMap<string, boolean>;
|
|
51
|
+
encyclopedicClauses: ReadonlyMap<string, {
|
|
52
|
+
select: string;
|
|
53
|
+
join: string;
|
|
54
|
+
}>;
|
|
55
|
+
weights: RankingWeights;
|
|
56
|
+
}): RawSearchRow[];
|
|
57
|
+
//# sourceMappingURL=search-fetch.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search-fetch.d.ts","sourceRoot":"","sources":["../search-fetch.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAiB,MAAM,aAAa,CAAA;AAK9D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AAE1D,OAAO,KAAK,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AA0B9D,MAAM,WAAW,YAAY;IAC5B,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,IAAI,EAAE,MAAM,CAAA;IACZ,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;IAClB,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;IAClB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAA;IAC5B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAA;IAC5B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB;;;OAGG;IACH,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;CAC3B;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE;IACxC,EAAE,EAAE,YAAY,CAAA;IAChB,UAAU,EAAE,MAAM,CAAA;IAClB,KAAK,EAAE,cAAc,CAAA;IACrB,UAAU,EAAE,YAAY,EAAE,GAAG,IAAI,CAAA;IACjC,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;IACb,YAAY,EAAE,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC1C,kBAAkB,EAAE,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAChD,mBAAmB,EAAE,WAAW,CAAC,MAAM,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IAC1E,OAAO,EAAE,cAAc,CAAA;CACvB,GAAG,YAAY,EAAE,CA2KjB"}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @copyright Sister Software
|
|
3
|
+
* @license AGPL-3.0
|
|
4
|
+
* @author Teffen Ellis, et al.
|
|
5
|
+
*
|
|
6
|
+
* The FTS5 candidate fetch behind the fuzzy name match: the schema-qualified `place_search`
|
|
7
|
+
* MATCH, its population-ordered companion fetch, and the raw row shape both of them return.
|
|
8
|
+
*
|
|
9
|
+
* Bbox and near-with-radius narrow at the SQL level through SQLite's built-in `rtree`, whose index name and schema
|
|
10
|
+
* live in `fts.ts` beside the FTS5 build. That is why this package pulls neither SpatiaLite nor turf: the R*Tree does
|
|
11
|
+
* the narrowing, and the passes downstream of it operate on ≤ a few hundred candidates per query rather than the
|
|
12
|
+
* whole corpus, so an exact haversine over the survivors is cheap.
|
|
13
|
+
*/
|
|
14
|
+
import { bboxAround } from "@mailwoman/spatial";
|
|
15
|
+
import { PLACE_BBOX_TABLE, PLACE_POPULATION_TABLE } from "./fts.js";
|
|
16
|
+
import { allRows } from "./sqlite-utils.js";
|
|
17
|
+
/**
|
|
18
|
+
* Query length at or below which the FTS window is widened. A two- or three-character query is almost always a region
|
|
19
|
+
* abbreviation, where the exact match can otherwise fall outside the window behind higher-bm25 partial hits — "NY"
|
|
20
|
+
* losing to "New York".
|
|
21
|
+
*/
|
|
22
|
+
const SHORT_QUERY_MAX_LENGTH = 3;
|
|
23
|
+
/**
|
|
24
|
+
* Over-fetch floor for SHORT (≤3-char) queries — region abbreviations like "NY"/"VT". An exact-abbrev holder's BM25 is
|
|
25
|
+
* poor (long multilingual alt-name document), so the normal `limit * 4` window can drop it before `exactMatchTiering`
|
|
26
|
+
* promotes it. 200 comfortably covers every same-abbrev region across the 12-country gazetteer (a 2-letter token
|
|
27
|
+
* matches a few dozen regions at most) while staying a cheap region-placetype fetch. See the `#fuzzyNameMatch`
|
|
28
|
+
* over-fetch comment.
|
|
29
|
+
*/
|
|
30
|
+
const SHORT_QUERY_OVERFETCH = 200;
|
|
31
|
+
/**
|
|
32
|
+
* How many rows the population-ordered companion fetch (#905) adds to the candidate pool. Small on purpose: its only
|
|
33
|
+
* job is to guarantee the FAMOUS holders of a name enter the pool at all — for "Paris"-class floods the bm25 window is
|
|
34
|
+
* saturated by thousands of tiny same-name rows and no boost inside the bm25-based ORDER BY can rescue a candidate
|
|
35
|
+
* whose bm25 is length-poisoned by ~15 points (see the fetch-site comment).
|
|
36
|
+
*/
|
|
37
|
+
const POPULATION_FETCH_LIMIT = 15;
|
|
38
|
+
/**
|
|
39
|
+
* Fetch the raw candidate rows for a name match on one shard: the BM25-ordered window over `place_search` (widened for
|
|
40
|
+
* short queries), plus the population-ordered companion fetch that keeps the prominent holders of a name pool-complete.
|
|
41
|
+
* `schemaName` is the routed shard's bare schema name — validated at construction, so it is interpolated directly.
|
|
42
|
+
*/
|
|
43
|
+
export function fetchSearchRows(options) {
|
|
44
|
+
const { db, schemaName: sch, query, placetypes, ftsQuery, limit, hasBboxIndex, hasPopulationIndex, encyclopedicClauses, weights, } = options;
|
|
45
|
+
// Over-fetch so post-scoring + exact-match tiering have room to re-rank. SHORT queries (a 2–3-char
|
|
46
|
+
// region abbreviation like "NY"/"VT") are the danger case the `exactMatchTiering` docstring flags:
|
|
47
|
+
// the exact-abbrev holder's BM25 is poor (its long multilingual alt-name document tanks the score),
|
|
48
|
+
// so under the normal `limit * 4` window it drops OUT of the candidate pool BEFORE tiering can
|
|
49
|
+
// promote it — "NY" then resolves to a token-matching foreign region (Highland, GB) instead of New
|
|
50
|
+
// York. Widen the window for short queries so the exact match is always present to be tiered.
|
|
51
|
+
// (Cross-country abbrev collisions — "VT" is BOTH Vermont and Viterbo — still need a country/
|
|
52
|
+
// postcode signal to disambiguate; this only rescues the window-drop class, not genuine ambiguity.
|
|
53
|
+
// With a `country` hint every abbrev resolves; bare + no-context lifts 7→10/15 US states.)
|
|
54
|
+
const ftsLimit = query.text.trim().length <= SHORT_QUERY_MAX_LENGTH ? Math.max(limit * 4, SHORT_QUERY_OVERFETCH) : limit * 4;
|
|
55
|
+
// Filter out historical / superseded / deprecated places by default — they live in the same
|
|
56
|
+
// spr table but should never win a contemporary lookup. `is_current = 0` is the only WOF
|
|
57
|
+
// value that means "not current"; both `-1` (modern) and `1` (legacy) mean current. See #91.
|
|
58
|
+
// Note: with schema-qualified FROM the bare `place_search` reference in MATCH resolves to
|
|
59
|
+
// the FROM table — required by FTS5 parser, see sharding.ts header comment.
|
|
60
|
+
const where = ["place_search MATCH ?", "spr.is_current != 0", "spr.is_deprecated = 0"];
|
|
61
|
+
const params = [ftsQuery];
|
|
62
|
+
if (placetypes && placetypes.length) {
|
|
63
|
+
where.push(`spr.placetype IN (${placetypes.map(() => "?").join(", ")})`);
|
|
64
|
+
params.push(...placetypes);
|
|
65
|
+
}
|
|
66
|
+
if (query.country) {
|
|
67
|
+
where.push("spr.country = ?");
|
|
68
|
+
params.push(query.country);
|
|
69
|
+
}
|
|
70
|
+
if (query.parentID !== undefined) {
|
|
71
|
+
where.push(`(spr.parent_id = ? OR spr.id IN (SELECT id FROM ${sch}.ancestors WHERE ancestor_id = ?))`);
|
|
72
|
+
params.push(query.parentID, query.parentID);
|
|
73
|
+
}
|
|
74
|
+
// Bbox + near-with-radius are SQL-level filters via the R*Tree. We only emit the JOIN when
|
|
75
|
+
// the active shard has the R*Tree; missing-but-requested is silently treated as no-bbox-
|
|
76
|
+
// filter so legacy DBs / shards-without-bbox don't crash.
|
|
77
|
+
const shardHasBbox = hasBboxIndex.get(sch) === true;
|
|
78
|
+
const useBboxJoin = (query.bbox || query.near?.maxDistanceKm !== undefined) && shardHasBbox;
|
|
79
|
+
let joinClause = `JOIN ${sch}.spr ON spr.id = place_search.wof_id`;
|
|
80
|
+
if (useBboxJoin) {
|
|
81
|
+
joinClause += ` JOIN ${sch}.${PLACE_BBOX_TABLE} bbox ON bbox.id = spr.id`;
|
|
82
|
+
// AABB intersection — both bbox sides must overlap. R*Tree handles this in O(log n).
|
|
83
|
+
const filterBox = query.bbox || bboxAround(query.near.lat, query.near.lon, query.near.maxDistanceKm);
|
|
84
|
+
where.push("bbox.min_lat <= ? AND bbox.max_lat >= ?", "bbox.min_lon <= ? AND bbox.max_lon >= ?");
|
|
85
|
+
params.push(filterBox.maxLat, filterBox.minLat, filterBox.maxLon, filterBox.minLon);
|
|
86
|
+
}
|
|
87
|
+
// LEFT JOIN the population aux table when present. Missing-on-this-shard means the SELECT
|
|
88
|
+
// just doesn't include the population column; the post-scoring loop treats it as 0.
|
|
89
|
+
const shardHasPopulation = hasPopulationIndex.get(sch) === true;
|
|
90
|
+
const populationSelect = shardHasPopulation
|
|
91
|
+
? `${PLACE_POPULATION_TABLE}.population AS population`
|
|
92
|
+
: `NULL AS population`;
|
|
93
|
+
const populationJoin = shardHasPopulation
|
|
94
|
+
? `LEFT JOIN ${sch}.${PLACE_POPULATION_TABLE} ON ${PLACE_POPULATION_TABLE}.id = spr.id`
|
|
95
|
+
: "";
|
|
96
|
+
// The encyclopedic score is CARRIED, never ranked on (ROAD_TO_V9 §2, ratified 2026-08-06) — it
|
|
97
|
+
// appears in the SELECT and in no ORDER BY, here or in the companion fetch below. Gated on the
|
|
98
|
+
// split column, so a pre-split shard emits a literal NULL and builds no join at all.
|
|
99
|
+
const { select: encyclopedicSelect, join: encyclopedicJoin } = encyclopedicClauses.get(sch);
|
|
100
|
+
// Push the population boost into the ORDER BY when the index is available, so famous places
|
|
101
|
+
// (whose long alt-name lists hurt BM25) actually make it into the over-fetch window. The TS
|
|
102
|
+
// post-scoring will still compute the same boost for the final score; this just ensures the
|
|
103
|
+
// candidate set is right.
|
|
104
|
+
//
|
|
105
|
+
// Formula: rank_adjusted = bm25 - populationBoost * min(1.0, log10(1 + pop) / scaleLog10)
|
|
106
|
+
// Lower rank_adjusted = better (matches SQLite's bm25 convention of "more negative = better").
|
|
107
|
+
//
|
|
108
|
+
// #905 — do NOT reach for bm25 column weights here. Measured falsification (2026-07-02): FTS5's
|
|
109
|
+
// bm25 length normalization is polluted by the row's TOTAL document size, so identical 1-token
|
|
110
|
+
// `name` docs read −16.0 (empty alt_names) vs −0.43 (2.7 KB alt_names) EVEN with the alt_names
|
|
111
|
+
// column weighted to zero — no weighting isolates name relevance in this schema. The famous-
|
|
112
|
+
// holder guarantee lives in the population-ordered companion fetch below instead, and the
|
|
113
|
+
// exact tier breaks ties by population in the post-scoring sort.
|
|
114
|
+
const orderByExpr = shardHasPopulation
|
|
115
|
+
? `(bm25(place_search) - ? * MIN(1.0, COALESCE(log10(1.0 + ${PLACE_POPULATION_TABLE}.population), 0) / ?))`
|
|
116
|
+
: "bm25(place_search)";
|
|
117
|
+
// Schema-qualified FROM with bare-name MATCH — required syntax for FTS5 on attached schemas.
|
|
118
|
+
// See sharding.ts header for the gotcha that drove this design.
|
|
119
|
+
const stmt = db.prepare(`
|
|
120
|
+
SELECT
|
|
121
|
+
spr.id AS id,
|
|
122
|
+
spr.name,
|
|
123
|
+
spr.placetype,
|
|
124
|
+
spr.country,
|
|
125
|
+
spr.parent_id,
|
|
126
|
+
bm25(place_search) AS rank,
|
|
127
|
+
spr.latitude AS lat,
|
|
128
|
+
spr.longitude AS lon,
|
|
129
|
+
spr.min_latitude, spr.max_latitude, spr.min_longitude, spr.max_longitude,
|
|
130
|
+
${populationSelect},
|
|
131
|
+
${encyclopedicSelect}
|
|
132
|
+
FROM ${sch}.place_search
|
|
133
|
+
${joinClause}
|
|
134
|
+
${populationJoin}
|
|
135
|
+
${encyclopedicJoin}
|
|
136
|
+
WHERE ${where.join(" AND ")}
|
|
137
|
+
ORDER BY ${orderByExpr} ASC
|
|
138
|
+
LIMIT ?
|
|
139
|
+
`);
|
|
140
|
+
if (shardHasPopulation) {
|
|
141
|
+
params.push(weights.populationBoost, weights.populationScaleLog10);
|
|
142
|
+
}
|
|
143
|
+
params.push(ftsLimit);
|
|
144
|
+
const rawRows = allRows(stmt, ...params);
|
|
145
|
+
// #905 companion fetch: the same MATCH, ordered by population alone. For name floods
|
|
146
|
+
// ("Paris" matches thousands of gap-fill villages) the bm25-based window above cannot admit
|
|
147
|
+
// the famous holder — its bm25 is length-poisoned by the row's alias bulk (measured ~15 pts,
|
|
148
|
+
// vs a +4.0 boost cap), so FR Paris never even reaches post-scoring. This fetch makes the
|
|
149
|
+
// prominent holders of a name pool-complete BY CONSTRUCTION; the exact-tier sort below
|
|
150
|
+
// decides whether they win. Skipped without a population index (nothing to order by).
|
|
151
|
+
if (shardHasPopulation) {
|
|
152
|
+
const popStmt = db.prepare(`
|
|
153
|
+
SELECT
|
|
154
|
+
spr.id AS id,
|
|
155
|
+
spr.name,
|
|
156
|
+
spr.placetype,
|
|
157
|
+
spr.country,
|
|
158
|
+
spr.parent_id,
|
|
159
|
+
bm25(place_search) AS rank,
|
|
160
|
+
spr.latitude AS lat,
|
|
161
|
+
spr.longitude AS lon,
|
|
162
|
+
spr.min_latitude, spr.max_latitude, spr.min_longitude, spr.max_longitude,
|
|
163
|
+
${populationSelect},
|
|
164
|
+
${encyclopedicSelect}
|
|
165
|
+
FROM ${sch}.place_search
|
|
166
|
+
${joinClause}
|
|
167
|
+
${populationJoin}
|
|
168
|
+
${encyclopedicJoin}
|
|
169
|
+
WHERE ${where.join(" AND ")}
|
|
170
|
+
ORDER BY COALESCE(${PLACE_POPULATION_TABLE}.population, 0) DESC
|
|
171
|
+
LIMIT ?
|
|
172
|
+
`);
|
|
173
|
+
const popParams = params.slice(0, -3); // drop the two boost params + ftsLimit
|
|
174
|
+
const seen = new Set(rawRows.map((r) => r.id));
|
|
175
|
+
for (const row of allRows(popStmt, ...popParams, POPULATION_FETCH_LIMIT)) {
|
|
176
|
+
if (!seen.has(row.id)) {
|
|
177
|
+
rawRows.push(row);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
return rawRows;
|
|
182
|
+
}
|
|
183
|
+
//# sourceMappingURL=search-fetch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search-fetch.js","sourceRoot":"","sources":["../search-fetch.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAIH,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAA;AAE/C,OAAO,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,MAAM,UAAU,CAAA;AAEnE,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAA;AAG3C;;;;GAIG;AACH,MAAM,sBAAsB,GAAG,CAAC,CAAA;AAEhC;;;;;;GAMG;AACH,MAAM,qBAAqB,GAAG,GAAG,CAAA;AAEjC;;;;;GAKG;AACH,MAAM,sBAAsB,GAAG,EAAE,CAAA;AAuBjC;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,OAW/B;IACA,MAAM,EACL,EAAE,EACF,UAAU,EAAE,GAAG,EACf,KAAK,EACL,UAAU,EACV,QAAQ,EACR,KAAK,EACL,YAAY,EACZ,kBAAkB,EAClB,mBAAmB,EACnB,OAAO,GACP,GAAG,OAAO,CAAA;IAEX,mGAAmG;IACnG,mGAAmG;IACnG,oGAAoG;IACpG,+FAA+F;IAC/F,mGAAmG;IACnG,8FAA8F;IAC9F,8FAA8F;IAC9F,mGAAmG;IACnG,2FAA2F;IAC3F,MAAM,QAAQ,GACb,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,IAAI,sBAAsB,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,GAAG,CAAC,EAAE,qBAAqB,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAA;IAE5G,4FAA4F;IAC5F,yFAAyF;IACzF,6FAA6F;IAC7F,0FAA0F;IAC1F,4EAA4E;IAC5E,MAAM,KAAK,GAAa,CAAC,sBAAsB,EAAE,qBAAqB,EAAE,uBAAuB,CAAC,CAAA;IAChG,MAAM,MAAM,GAAoB,CAAC,QAAQ,CAAC,CAAA;IAE1C,IAAI,UAAU,IAAI,UAAU,CAAC,MAAM,EAAE,CAAC;QACrC,KAAK,CAAC,IAAI,CAAC,qBAAqB,UAAU,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;QACxE,MAAM,CAAC,IAAI,CAAC,GAAG,UAAU,CAAC,CAAA;IAC3B,CAAC;IAED,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;QACnB,KAAK,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAA;QAC7B,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;IAC3B,CAAC;IAED,IAAI,KAAK,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;QAClC,KAAK,CAAC,IAAI,CAAC,mDAAmD,GAAG,oCAAoC,CAAC,CAAA;QACtG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAA;IAC5C,CAAC;IAED,2FAA2F;IAC3F,yFAAyF;IACzF,0DAA0D;IAC1D,MAAM,YAAY,GAAG,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,IAAI,CAAA;IACnD,MAAM,WAAW,GAAG,CAAC,KAAK,CAAC,IAAI,IAAI,KAAK,CAAC,IAAI,EAAE,aAAa,KAAK,SAAS,CAAC,IAAI,YAAY,CAAA;IAC3F,IAAI,UAAU,GAAG,QAAQ,GAAG,sCAAsC,CAAA;IAElE,IAAI,WAAW,EAAE,CAAC;QACjB,UAAU,IAAI,SAAS,GAAG,IAAI,gBAAgB,2BAA2B,CAAA;QACzE,qFAAqF;QACrF,MAAM,SAAS,GAAG,KAAK,CAAC,IAAI,IAAI,UAAU,CAAC,KAAK,CAAC,IAAK,CAAC,GAAG,EAAE,KAAK,CAAC,IAAK,CAAC,GAAG,EAAE,KAAK,CAAC,IAAK,CAAC,aAAc,CAAC,CAAA;QACxG,KAAK,CAAC,IAAI,CAAC,yCAAyC,EAAE,yCAAyC,CAAC,CAAA;QAChG,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,SAAS,CAAC,MAAM,EAAE,SAAS,CAAC,MAAM,EAAE,SAAS,CAAC,MAAM,CAAC,CAAA;IACpF,CAAC;IAED,0FAA0F;IAC1F,oFAAoF;IACpF,MAAM,kBAAkB,GAAG,kBAAkB,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,IAAI,CAAA;IAE/D,MAAM,gBAAgB,GAAG,kBAAkB;QAC1C,CAAC,CAAC,GAAG,sBAAsB,2BAA2B;QACtD,CAAC,CAAC,oBAAoB,CAAA;IAEvB,MAAM,cAAc,GAAG,kBAAkB;QACxC,CAAC,CAAC,aAAa,GAAG,IAAI,sBAAsB,OAAO,sBAAsB,cAAc;QACvF,CAAC,CAAC,EAAE,CAAA;IAEL,+FAA+F;IAC/F,+FAA+F;IAC/F,qFAAqF;IACrF,MAAM,EAAE,MAAM,EAAE,kBAAkB,EAAE,IAAI,EAAE,gBAAgB,EAAE,GAAG,mBAAmB,CAAC,GAAG,CAAC,GAAG,CAAE,CAAA;IAE5F,4FAA4F;IAC5F,4FAA4F;IAC5F,4FAA4F;IAC5F,0BAA0B;IAC1B,EAAE;IACF,0FAA0F;IAC1F,+FAA+F;IAC/F,EAAE;IACF,gGAAgG;IAChG,+FAA+F;IAC/F,+FAA+F;IAC/F,6FAA6F;IAC7F,0FAA0F;IAC1F,iEAAiE;IACjE,MAAM,WAAW,GAAG,kBAAkB;QACrC,CAAC,CAAC,2DAA2D,sBAAsB,wBAAwB;QAC3G,CAAC,CAAC,oBAAoB,CAAA;IAEvB,6FAA6F;IAC7F,gEAAgE;IAChE,MAAM,IAAI,GAAG,EAAE,CAAC,OAAO,CAAC;;;;;;;;;;;KAWpB,gBAAgB;KAChB,kBAAkB;SACd,GAAG;IACR,UAAU;IACV,cAAc;IACd,gBAAgB;UACV,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC;aAChB,WAAW;;EAEtB,CAAC,CAAA;IAEF,IAAI,kBAAkB,EAAE,CAAC;QACxB,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,eAAe,EAAE,OAAO,CAAC,oBAAoB,CAAC,CAAA;IACnE,CAAC;IAED,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;IAErB,MAAM,OAAO,GAAG,OAAO,CAAe,IAAI,EAAE,GAAG,MAAM,CAAC,CAAA;IAEtD,qFAAqF;IACrF,4FAA4F;IAC5F,6FAA6F;IAC7F,0FAA0F;IAC1F,uFAAuF;IACvF,sFAAsF;IACtF,IAAI,kBAAkB,EAAE,CAAC;QACxB,MAAM,OAAO,GAAG,EAAE,CAAC,OAAO,CAAC;;;;;;;;;;;MAWvB,gBAAgB;MAChB,kBAAkB;UACd,GAAG;KACR,UAAU;KACV,cAAc;KACd,gBAAgB;WACV,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC;uBACP,sBAAsB;;GAE1C,CAAC,CAAA;QAEF,MAAM,SAAS,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAA,CAAC,uCAAuC;QAC7E,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;QAE9C,KAAK,MAAM,GAAG,IAAI,OAAO,CAAe,OAAO,EAAE,GAAG,SAAS,EAAE,sBAAsB,CAAC,EAAE,CAAC;YACxF,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC;gBACvB,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;YAClB,CAAC;QACF,CAAC;IACF,CAAC;IAED,OAAO,OAAO,CAAA;AACf,CAAC"}
|
package/out/sharding.d.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* @license AGPL-3.0
|
|
4
4
|
* @author Teffen Ellis, et al.
|
|
5
5
|
*
|
|
6
|
-
* Multi-shard support for `
|
|
6
|
+
* Multi-shard support for `WOFSQLitePlaceLookup` — opens multiple WOF SQLite distributions on one
|
|
7
7
|
* connection via `ATTACH DATABASE`, and routes queries to the right shard based on placetype.
|
|
8
8
|
*
|
|
9
9
|
* ## The FTS5 syntax rule that drove this design
|
|
@@ -61,7 +61,7 @@ export interface ShardConfig {
|
|
|
61
61
|
}
|
|
62
62
|
/**
|
|
63
63
|
* Resolved post-derivation: paired path + chosen schema name + (possibly empty) placetypes hint. Used internally by
|
|
64
|
-
* `
|
|
64
|
+
* `WOFSQLitePlaceLookup` so the routing logic operates on uniform structures.
|
|
65
65
|
*/
|
|
66
66
|
export interface ResolvedShard {
|
|
67
67
|
path: string;
|
|
@@ -107,7 +107,7 @@ export declare function pickShardForPlacetype(shards: ResolvedShard[], placetype
|
|
|
107
107
|
*/
|
|
108
108
|
country?: string;
|
|
109
109
|
/**
|
|
110
|
-
* Per-schema probed country sets (see `
|
|
110
|
+
* Per-schema probed country sets (see `WOFSQLitePlaceLookup`'s construction probe).
|
|
111
111
|
*/
|
|
112
112
|
countriesBySchema?: ReadonlyMap<string, ReadonlySet<string>>;
|
|
113
113
|
}): ResolvedShard;
|
package/out/sharding.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* @license AGPL-3.0
|
|
4
4
|
* @author Teffen Ellis, et al.
|
|
5
5
|
*
|
|
6
|
-
* Multi-shard support for `
|
|
6
|
+
* Multi-shard support for `WOFSQLitePlaceLookup` — opens multiple WOF SQLite distributions on one
|
|
7
7
|
* connection via `ATTACH DATABASE`, and routes queries to the right shard based on placetype.
|
|
8
8
|
*
|
|
9
9
|
* ## The FTS5 syntax rule that drove this design
|
|
@@ -21,7 +21,7 @@ export declare class SqliteConventionSource implements ConventionSource {
|
|
|
21
21
|
/**
|
|
22
22
|
* @param db An open handle to a DB that has the convention asset attached (or is it).
|
|
23
23
|
* @param schema The schema name the `address_convention` table lives under (`main` or an ATTACHed shard name —
|
|
24
|
-
* `
|
|
24
|
+
* `WOFSQLitePlaceLookup` auto-detects which shard carries the table).
|
|
25
25
|
*/
|
|
26
26
|
constructor(db: DatabaseSync, schema: string);
|
|
27
27
|
get(wofID: number): Convention | undefined;
|
|
@@ -26,7 +26,7 @@ export class SqliteConventionSource {
|
|
|
26
26
|
/**
|
|
27
27
|
* @param db An open handle to a DB that has the convention asset attached (or is it).
|
|
28
28
|
* @param schema The schema name the `address_convention` table lives under (`main` or an ATTACHed shard name —
|
|
29
|
-
* `
|
|
29
|
+
* `WOFSQLitePlaceLookup` auto-detects which shard carries the table).
|
|
30
30
|
*/
|
|
31
31
|
constructor(db, schema) {
|
|
32
32
|
this.#db = db;
|
package/out/sqlite-utils.d.ts
CHANGED
|
@@ -5,7 +5,25 @@
|
|
|
5
5
|
*
|
|
6
6
|
* Small shared helpers for the SQLite-backed lookups.
|
|
7
7
|
*/
|
|
8
|
-
import type { DatabaseSync } from "node:sqlite";
|
|
8
|
+
import type { DatabaseSync, SQLInputValue } from "node:sqlite";
|
|
9
|
+
export { allRows, getRow } from "@mailwoman/core/utils";
|
|
10
|
+
/**
|
|
11
|
+
* A prepared single-row query whose parameter tuple remains visible to TypeScript. `StatementSync` accepts only the
|
|
12
|
+
* broad `SQLInputValue[]`, which otherwise erases tagged key types before they reach SQLite.
|
|
13
|
+
*/
|
|
14
|
+
export type PreparedGet<Parameters extends SQLInputValue[], Row> = (...parameters: Parameters) => Row | undefined;
|
|
15
|
+
/**
|
|
16
|
+
* Prepare a single-row query while preserving its exact parameter tuple at every call site.
|
|
17
|
+
*/
|
|
18
|
+
export declare function prepareGet<Parameters extends SQLInputValue[], Row>(db: DatabaseSync, sql: string): PreparedGet<Parameters, Row>;
|
|
19
|
+
/**
|
|
20
|
+
* Multi-row counterpart to {@link PreparedGet}.
|
|
21
|
+
*/
|
|
22
|
+
export type PreparedAll<Parameters extends SQLInputValue[], Row> = (...parameters: Parameters) => Row[];
|
|
23
|
+
/**
|
|
24
|
+
* Prepare a multi-row query while preserving its exact parameter tuple at every call site.
|
|
25
|
+
*/
|
|
26
|
+
export declare function prepareAll<Parameters extends SQLInputValue[], Row>(db: DatabaseSync, sql: string): PreparedAll<Parameters, Row>;
|
|
9
27
|
/**
|
|
10
28
|
* True when `name` is a table in the open database. The street-level lookups use this to degrade gracefully on an
|
|
11
29
|
* empty/tableless shard — an interrupted `build-*-shard.ts`, or a stray 0-byte file (e.g. `sqlite3 <missing>.db "…"`
|
|
@@ -13,4 +31,16 @@ import type { DatabaseSync } from "node:sqlite";
|
|
|
13
31
|
* missing table makes the lookup a no-op miss.
|
|
14
32
|
*/
|
|
15
33
|
export declare function hasTable(db: DatabaseSync, name: string): boolean;
|
|
34
|
+
/**
|
|
35
|
+
* True when `table` exists in the open database AND carries `column`.
|
|
36
|
+
*
|
|
37
|
+
* The column-level sibling of {@link hasTable}, and it exists for the same reason one layer down: an artifact built
|
|
38
|
+
* before a column was added is still a VALID artifact, and a reader that unconditionally names the new column in its
|
|
39
|
+
* `SELECT` turns "this gazetteer is a build behind" into `no such column` at the first keystroke. Probe once at
|
|
40
|
+
* construction and shape the query — `table_info` is a PRAGMA, so it must not sit on a per-query path.
|
|
41
|
+
*
|
|
42
|
+
* Note the interpolation: PRAGMA does not take bound parameters, so `table` is spliced. Every caller passes a
|
|
43
|
+
* module-level constant; never pass user input.
|
|
44
|
+
*/
|
|
45
|
+
export declare function hasColumn(db: DatabaseSync, table: string, column: string): boolean;
|
|
16
46
|
//# sourceMappingURL=sqlite-utils.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sqlite-utils.d.ts","sourceRoot":"","sources":["../sqlite-utils.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"sqlite-utils.d.ts","sourceRoot":"","sources":["../sqlite-utils.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAM9D,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAA;AAEvD;;;GAGG;AACH,MAAM,MAAM,WAAW,CAAC,UAAU,SAAS,aAAa,EAAE,EAAE,GAAG,IAAI,CAAC,GAAG,UAAU,EAAE,UAAU,KAAK,GAAG,GAAG,SAAS,CAAA;AAEjH;;GAEG;AACH,wBAAgB,UAAU,CAAC,UAAU,SAAS,aAAa,EAAE,EAAE,GAAG,EACjE,EAAE,EAAE,YAAY,EAChB,GAAG,EAAE,MAAM,GACT,WAAW,CAAC,UAAU,EAAE,GAAG,CAAC,CAI9B;AAED;;GAEG;AACH,MAAM,MAAM,WAAW,CAAC,UAAU,SAAS,aAAa,EAAE,EAAE,GAAG,IAAI,CAAC,GAAG,UAAU,EAAE,UAAU,KAAK,GAAG,EAAE,CAAA;AAEvG;;GAEG;AACH,wBAAgB,UAAU,CAAC,UAAU,SAAS,aAAa,EAAE,EAAE,GAAG,EACjE,EAAE,EAAE,YAAY,EAChB,GAAG,EAAE,MAAM,GACT,WAAW,CAAC,UAAU,EAAE,GAAG,CAAC,CAI9B;AAED;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAQhE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,SAAS,CAAC,EAAE,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAQlF"}
|