@mailwoman/resolver-wof-sqlite 7.0.0 → 7.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.
@@ -0,0 +1,74 @@
1
+ /**
2
+ * @copyright Sister Software
3
+ * @license AGPL-3.0
4
+ * @author Teffen Ellis, et al.
5
+ *
6
+ * Node reader for `poi.db` (spec §3.4) — the res-9 k-ring reader over the clustered `poi`
7
+ * `WITHOUT ROWID` B-tree Task 1's schema (`poi-schema.ts`) builds. Three search modes share one
8
+ * artifact:
9
+ *
10
+ * - **Category**: `latLngToCell(center, 9)` → `gridDisk` ring-by-ring expansion, probing each
11
+ * cell's clustered `(h3_cell, category_id, neg_rank, …)` range. Rings accumulate until `limit`
12
+ * rows are on hand after a completed ring, or `maxRings` is exhausted; the pool is sorted by
13
+ * haversine distance from `center` after every ring.
14
+ * - **Brand**: the same k-ring walk, but probing on `brand_wikidata` instead — category
15
+ * unconstrained, so a chain's rows surface regardless of how they were categorized.
16
+ * - **Name**: FTS5 `MATCH` against the `poi_search` virtual table, hydrated back to full rows by
17
+ * `name_key`. No center required; if one is given, hits are still distance-sorted.
18
+ *
19
+ * `latLngToCell`/`gridDisk` come from `h3-js`; the 48-bit short-cell packing that turns a raw H3
20
+ * cell into the integer `poi.h3_cell` stores is `@mailwoman/spatial`'s `shortenH3Cell` — that math
21
+ * is NEVER reimplemented here (see AGENTS.md on `@mailwoman/spatial` being the one true home for
22
+ * it).
23
+ */
24
+ import { DatabaseSync } from "node:sqlite";
25
+ /** Resolution the `poi` table's `h3_cell` column is keyed at — matches the builder (spec §3.4). */
26
+ export declare const POI_H3_RESOLUTION = 9;
27
+ export interface POISearchQuery {
28
+ /** Poi-taxonomy category id (string side of the dictionary). Ignored when `brandWikidata` is also set — brand wins. */
29
+ categoryID?: string;
30
+ /** Wikidata QID for brand-exact search. */
31
+ brandWikidata?: string;
32
+ /** Free-text name (FTS5). */
33
+ name?: string;
34
+ /** Search center. Required for category/brand queries (k-ring expansion). */
35
+ center?: {
36
+ latitude: number;
37
+ longitude: number;
38
+ };
39
+ /**
40
+ * Ring budget: how many res-9 k-rings to expand before giving up (default 12 ≈ ~4 km). Counts ring 0, so k reaches
41
+ * `maxRings - 1`.
42
+ */
43
+ maxRings?: number;
44
+ limit?: number;
45
+ }
46
+ export interface POISearchHit {
47
+ name: string | null;
48
+ categoryID: string | null;
49
+ brandWikidata: string | null;
50
+ latitude: number;
51
+ longitude: number;
52
+ country: string;
53
+ confidence: number;
54
+ distanceM?: number;
55
+ }
56
+ export interface POILookupOpts {
57
+ /** Path to a `poi.db` built by the (future) POI builder. Opened read-only. */
58
+ databasePath?: string;
59
+ /** Pre-opened handle (tests / shared connections). Mutually exclusive with `databasePath`. */
60
+ database?: DatabaseSync;
61
+ }
62
+ /**
63
+ * Node reader over `poi.db`. `implements Disposable` so callers can `using lookup = new POILookup(...)` (or call
64
+ * `[Symbol.dispose]()` explicitly), the same precedent as {@link WOFCandidateTableLookup} /
65
+ * {@link WOFSqlitePlaceLookup}.
66
+ */
67
+ export declare class POILookup implements Disposable {
68
+ #private;
69
+ constructor(opts: POILookupOpts);
70
+ search(query: POISearchQuery): POISearchHit[];
71
+ close(): void;
72
+ [Symbol.dispose](): void;
73
+ }
74
+ //# sourceMappingURL=poi-lookup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"poi-lookup.d.ts","sourceRoot":"","sources":["../poi-lookup.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAO1C,mGAAmG;AACnG,eAAO,MAAM,iBAAiB,IAAI,CAAA;AAQlC,MAAM,WAAW,cAAc;IAC9B,uHAAuH;IACvH,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,2CAA2C;IAC3C,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,6BAA6B;IAC7B,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,6EAA6E;IAC7E,MAAM,CAAC,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAA;IAChD;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,KAAK,CAAC,EAAE,MAAM,CAAA;CACd;AAED,MAAM,WAAW,YAAY;IAC5B,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;IACnB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAA;IAC5B,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,EAAE,MAAM,CAAA;IACf,UAAU,EAAE,MAAM,CAAA;IAClB,SAAS,CAAC,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,aAAa;IAC7B,8EAA8E;IAC9E,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,8FAA8F;IAC9F,QAAQ,CAAC,EAAE,YAAY,CAAA;CACvB;AAQD;;;;GAIG;AACH,qBAAa,SAAU,YAAW,UAAU;;gBAa/B,IAAI,EAAE,aAAa;IAiC/B,MAAM,CAAC,KAAK,EAAE,cAAc,GAAG,YAAY,EAAE;IA8H7C,KAAK,IAAI,IAAI;IAMb,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,IAAI;CAGxB"}
@@ -0,0 +1,226 @@
1
+ /**
2
+ * @copyright Sister Software
3
+ * @license AGPL-3.0
4
+ * @author Teffen Ellis, et al.
5
+ *
6
+ * Node reader for `poi.db` (spec §3.4) — the res-9 k-ring reader over the clustered `poi`
7
+ * `WITHOUT ROWID` B-tree Task 1's schema (`poi-schema.ts`) builds. Three search modes share one
8
+ * artifact:
9
+ *
10
+ * - **Category**: `latLngToCell(center, 9)` → `gridDisk` ring-by-ring expansion, probing each
11
+ * cell's clustered `(h3_cell, category_id, neg_rank, …)` range. Rings accumulate until `limit`
12
+ * rows are on hand after a completed ring, or `maxRings` is exhausted; the pool is sorted by
13
+ * haversine distance from `center` after every ring.
14
+ * - **Brand**: the same k-ring walk, but probing on `brand_wikidata` instead — category
15
+ * unconstrained, so a chain's rows surface regardless of how they were categorized.
16
+ * - **Name**: FTS5 `MATCH` against the `poi_search` virtual table, hydrated back to full rows by
17
+ * `name_key`. No center required; if one is given, hits are still distance-sorted.
18
+ *
19
+ * `latLngToCell`/`gridDisk` come from `h3-js`; the 48-bit short-cell packing that turns a raw H3
20
+ * cell into the integer `poi.h3_cell` stores is `@mailwoman/spatial`'s `shortenH3Cell` — that math
21
+ * is NEVER reimplemented here (see AGENTS.md on `@mailwoman/spatial` being the one true home for
22
+ * it).
23
+ */
24
+ import { DatabaseSync } from "node:sqlite";
25
+ import { haversineKm, shortenH3Cell } from "@mailwoman/spatial";
26
+ import { gridDisk, latLngToCell } from "h3-js";
27
+ /** Resolution the `poi` table's `h3_cell` column is keyed at — matches the builder (spec §3.4). */
28
+ export const POI_H3_RESOLUTION = 9;
29
+ /** Ring budget default: 12 res-9 k-rings ≈ ~4 km. */
30
+ const DEFAULT_MAX_RINGS = 12;
31
+ /** Row-count default when a query doesn't specify `limit`. */
32
+ const DEFAULT_LIMIT = 20;
33
+ /**
34
+ * Node reader over `poi.db`. `implements Disposable` so callers can `using lookup = new POILookup(...)` (or call
35
+ * `[Symbol.dispose]()` explicitly), the same precedent as {@link WOFCandidateTableLookup} /
36
+ * {@link WOFSqlitePlaceLookup}.
37
+ */
38
+ export class POILookup {
39
+ #db;
40
+ #ownsDB;
41
+ #categoryToID = new Map();
42
+ #idToCategory = new Map();
43
+ /** `(h3_cell, category_id)` → the cell's category-clustered range, most-confident-first. */
44
+ #categoryCellProbe;
45
+ /** `(h3_cell, brand_wikidata)` → the cell's brand-matched rows (category unconstrained). */
46
+ #brandCellProbe;
47
+ /** FTS5 `MATCH` over `poi_search`, returning candidate `name_key`s to hydrate. */
48
+ #nameFTSProbe;
49
+ constructor(opts) {
50
+ if (opts.database) {
51
+ this.#db = opts.database;
52
+ this.#ownsDB = false;
53
+ }
54
+ else if (opts.databasePath) {
55
+ this.#db = new DatabaseSync(opts.databasePath, { readOnly: true });
56
+ this.#ownsDB = true;
57
+ }
58
+ else {
59
+ throw new Error("POILookup needs `databasePath` or `database`");
60
+ }
61
+ // The category dictionary is tiny (poi-taxonomy's category count) — load it once at
62
+ // construction so `search` never round-trips to it.
63
+ for (const r of this.#db
64
+ .prepare("SELECT id, category FROM poi_category_codes")
65
+ .all()) {
66
+ this.#categoryToID.set(String(r.category), Number(r.id));
67
+ this.#idToCategory.set(Number(r.id), String(r.category));
68
+ }
69
+ const columns = "name, category_id, brand_wikidata, latitude, longitude, country, confidence, name_key";
70
+ this.#categoryCellProbe = this.#db.prepare(`SELECT ${columns} FROM poi WHERE h3_cell = ? AND category_id = ? ORDER BY neg_rank ASC LIMIT ?`);
71
+ this.#brandCellProbe = this.#db.prepare(`SELECT ${columns} FROM poi WHERE h3_cell = ? AND brand_wikidata = ? ORDER BY neg_rank ASC LIMIT ?`);
72
+ this.#nameFTSProbe = this.#db.prepare("SELECT name_key FROM poi_search WHERE poi_search MATCH ? ORDER BY bm25(poi_search) LIMIT ?");
73
+ }
74
+ search(query) {
75
+ const limit = Math.max(1, query.limit ?? DEFAULT_LIMIT);
76
+ if (query.name) {
77
+ return this.#searchByName(query.name, limit, query.center);
78
+ }
79
+ if (query.categoryID || query.brandWikidata) {
80
+ if (!query.center) {
81
+ throw new Error("POILookup.search: category/brand search requires a `center`");
82
+ }
83
+ return this.#searchKRing(query, limit);
84
+ }
85
+ return [];
86
+ }
87
+ /** Category or brand path: k-ring expansion from `query.center`'s res-9 cell, probing each new ring's cells. */
88
+ #searchKRing(query, limit) {
89
+ const center = query.center;
90
+ const maxRings = query.maxRings ?? DEFAULT_MAX_RINGS;
91
+ let categoryId;
92
+ // brandWikidata wins over categoryID when both are set — see POISearchQuery.categoryID.
93
+ if (!query.brandWikidata) {
94
+ categoryId = this.#categoryToID.get(query.categoryID);
95
+ // An unknown category (not in the dictionary) can't have rows — a clean miss, not a throw.
96
+ if (categoryId === undefined)
97
+ return [];
98
+ }
99
+ const origin = latLngToCell(center.latitude, center.longitude, POI_H3_RESOLUTION);
100
+ const seenCells = new Set();
101
+ let rows = [];
102
+ // `ring` starts at 0 (the origin cell itself), so this loop's k reaches `maxRings - 1`.
103
+ for (let ring = 0; ring < maxRings; ring++) {
104
+ // gridDisk(origin, ring) returns the WHOLE disk out to `ring`; diffing against what's
105
+ // already been probed derives just this ring's new cells.
106
+ const diskCells = gridDisk(origin, ring);
107
+ const newCells = diskCells.filter((cell) => !seenCells.has(cell));
108
+ for (const cell of newCells) {
109
+ seenCells.add(cell);
110
+ const shortCell = h3CellToInt(cell);
111
+ const hits = query.brandWikidata
112
+ ? this.#brandCellProbe.all(shortCell, query.brandWikidata, limit)
113
+ : this.#categoryCellProbe.all(shortCell, categoryId, limit);
114
+ rows.push(...hits);
115
+ }
116
+ rows = sortByDistance(rows, center);
117
+ if (rows.length >= limit)
118
+ break;
119
+ }
120
+ return rows.slice(0, limit).map((row) => toHit(row, this.#idToCategory, center));
121
+ }
122
+ /**
123
+ * Name path: FTS5 MATCH → hydrate by name_key. No center required; distance-sorts if one is given anyway.
124
+ *
125
+ * Hydration is ONE batched `WHERE name_key IN (...)` query over the FTS hits' unique `name_key`s, not a per-hit probe
126
+ * — with up to `limit` FTS hits, a per-hit probe was up to `limit` full table scans before `createPOINameKeyIndex`
127
+ * (poi-schema.ts) + this batching.
128
+ */
129
+ #searchByName(name, limit, center) {
130
+ const matchQuery = sanitizePOINameQuery(name);
131
+ if (!matchQuery)
132
+ return [];
133
+ const ftsHits = this.#nameFTSProbe.all(matchQuery, limit);
134
+ const uniqueKeys = [];
135
+ const seenKeys = new Set();
136
+ for (const hit of ftsHits) {
137
+ if (!hit.name_key || seenKeys.has(hit.name_key))
138
+ continue;
139
+ seenKeys.add(hit.name_key);
140
+ uniqueKeys.push(hit.name_key);
141
+ }
142
+ if (uniqueKeys.length === 0)
143
+ return [];
144
+ const hydrated = this.#hydrateByNameKeys(uniqueKeys);
145
+ const rowsByKey = new Map();
146
+ for (const row of hydrated) {
147
+ if (row.name_key === null)
148
+ continue;
149
+ const bucket = rowsByKey.get(row.name_key);
150
+ if (bucket) {
151
+ bucket.push(row);
152
+ }
153
+ else {
154
+ rowsByKey.set(row.name_key, [row]);
155
+ }
156
+ }
157
+ let rows = [];
158
+ for (const key of uniqueKeys) {
159
+ rows.push(...(rowsByKey.get(key) ?? []));
160
+ }
161
+ if (center) {
162
+ rows = sortByDistance(rows, center);
163
+ }
164
+ return rows.slice(0, limit).map((row) => toHit(row, this.#idToCategory, center));
165
+ }
166
+ /**
167
+ * Batched hydration for the FTS name path: `WHERE name_key IN (?, ?, …)`, one query for the whole batch instead of
168
+ * one probe per FTS hit. This is a cold path (name search only) with variable arity per call, so the statement is
169
+ * prepared fresh each time rather than cached.
170
+ */
171
+ #hydrateByNameKeys(nameKeys) {
172
+ const columns = "name, category_id, brand_wikidata, latitude, longitude, country, confidence, name_key";
173
+ const placeholders = nameKeys.map(() => "?").join(", ");
174
+ const stmt = this.#db.prepare(`SELECT ${columns} FROM poi WHERE name_key IN (${placeholders})`);
175
+ return stmt.all(...nameKeys);
176
+ }
177
+ close() {
178
+ if (this.#ownsDB) {
179
+ this.#db.close();
180
+ }
181
+ }
182
+ [Symbol.dispose]() {
183
+ this.close();
184
+ }
185
+ }
186
+ /** `poi.h3_cell` is the SHORTENED (48-bit) cell — never the full h3-js cell string. */
187
+ function h3CellToInt(cell) {
188
+ return Number(BigInt(`0x${shortenH3Cell(cell)}`));
189
+ }
190
+ function sortByDistance(rows, center) {
191
+ return [...rows].sort((a, b) => haversineKm(center.latitude, center.longitude, a.latitude, a.longitude) -
192
+ haversineKm(center.latitude, center.longitude, b.latitude, b.longitude));
193
+ }
194
+ function toHit(row, idToCategory, center) {
195
+ return {
196
+ name: row.name,
197
+ categoryID: row.category_id !== 0 ? (idToCategory.get(row.category_id) ?? null) : null,
198
+ brandWikidata: row.brand_wikidata,
199
+ latitude: row.latitude,
200
+ longitude: row.longitude,
201
+ country: row.country,
202
+ confidence: row.confidence,
203
+ ...(center
204
+ ? { distanceM: haversineKm(center.latitude, center.longitude, row.latitude, row.longitude) * 1000 }
205
+ : {}),
206
+ };
207
+ }
208
+ /**
209
+ * Sanitize free text into an FTS5-safe MATCH query: strip the characters FTS5 would otherwise read as syntax (`"`
210
+ * phrase delimiters, `*` prefix wildcards, `:` column-filter separators), then phrase-quote each whitespace-separated
211
+ * token (AND-joined).
212
+ *
213
+ * `resolver-wof-sqlite` already has this discipline — `lookup.ts`'s `sanitizeFTSQuery` — but that function is
214
+ * module-private there (not re-exported from `fts.ts` or the package's `index.ts`), so this replicates the same
215
+ * discipline locally rather than reaching across the module boundary for a private helper.
216
+ */
217
+ function sanitizePOINameQuery(text) {
218
+ return text
219
+ .replace(/["*:]/g, "")
220
+ .trim()
221
+ .split(/\s+/u)
222
+ .filter(Boolean)
223
+ .map((token) => `"${token.replace(/"/g, '""')}"`)
224
+ .join(" ");
225
+ }
226
+ //# sourceMappingURL=poi-lookup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"poi-lookup.js","sourceRoot":"","sources":["../poi-lookup.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,77 @@
1
+ /**
2
+ * @copyright Sister Software
3
+ * @license AGPL-3.0
4
+ * @author Teffen Ellis, et al.
5
+ *
6
+ * Typed schema for poi.db — spatial layer #1 (spec §3.4). One clustered `WITHOUT ROWID` B-tree
7
+ * keyed `(h3_cell, category_id, neg_rank, rowid_key)` so "everything near this res-9 cell" is a
8
+ * contiguous key range (the byte-range/httpvfs access pattern, same discipline as the candidate
9
+ * gazetteer). Rows carry denormalized name/brand/coords; category ids are small ints via the
10
+ * `poi_category_codes` dictionary (poi-taxonomy category ids are the string side). The DB also
11
+ * embeds the layer-contract tables from `@mailwoman/core/layers` — the builder writes the
12
+ * manifest (tier `shipped`, spine `h3` res 9) and per-res-6-cell coverage.
13
+ */
14
+ import type { DatabaseSync } from "node:sqlite";
15
+ import type { LayerContractDatabase } from "@mailwoman/core/layers";
16
+ import { type Kysely } from "kysely";
17
+ /** One POI row. Clustered PK: h3_cell → category_id → neg_rank → rowid_key. */
18
+ export interface POITable {
19
+ /** 48-bit short H3 cell at res 9 (`latLngToCell` → `shortenH3Cell`). */
20
+ h3_cell: number;
21
+ /** Small int from {@link POICategoryCodeTable}; 0 = uncategorized. */
22
+ category_id: number;
23
+ /** `-log10(confidence + epsilon)` so ASC = most-confident-first within a cell+category. */
24
+ neg_rank: number;
25
+ /** Uniquifier within the clustered key (builder-assigned monotonic int). */
26
+ rowid_key: number;
27
+ name: string | null;
28
+ /** Lowercased, diacritic-flattened probe key for exact name lookups. */
29
+ name_key: string | null;
30
+ brand_wikidata: string | null;
31
+ latitude: number;
32
+ longitude: number;
33
+ /** ISO 3166-1 alpha-2 (from the Overture partition). */
34
+ country: string;
35
+ /** Overture existence confidence (already filtered ≥ 0.85 at build). */
36
+ confidence: number;
37
+ /** GERS id — nullable METADATA ONLY, never a key (the #470 rule). */
38
+ gers_id: string | null;
39
+ }
40
+ /**
41
+ * Staging mirror — every column nullable except the coords (the loader fills positionally; the materialize SELECT
42
+ * enforces completeness).
43
+ */
44
+ export interface POIStageTable {
45
+ h3_cell: number | null;
46
+ category_id: number | null;
47
+ neg_rank: number | null;
48
+ rowid_key: number | null;
49
+ name: string | null;
50
+ name_key: string | null;
51
+ brand_wikidata: string | null;
52
+ latitude: number;
53
+ longitude: number;
54
+ country: string | null;
55
+ confidence: number | null;
56
+ gers_id: string | null;
57
+ }
58
+ /** `(id → poi-taxonomy category id)` dictionary, e.g. `3 → "cafe"`. */
59
+ export interface POICategoryCodeTable {
60
+ id: number;
61
+ category: string;
62
+ }
63
+ export interface POIDatabase extends LayerContractDatabase {
64
+ poi: POITable;
65
+ poi_stage: POIStageTable;
66
+ poi_category_codes: POICategoryCodeTable;
67
+ }
68
+ /** Clustered-key-order column list shared by builder + `INSERT INTO poi SELECT … FROM poi_stage`. */
69
+ export declare const POI_COLUMNS: readonly ["h3_cell", "category_id", "neg_rank", "rowid_key", "name", "name_key", "brand_wikidata", "latitude", "longitude", "country", "confidence", "gers_id"];
70
+ export declare function createPOIStagingTables(db: Kysely<POIDatabase>): Promise<void>;
71
+ export declare function createPOITable(db: Kysely<POIDatabase>): Promise<void>;
72
+ /** Secondary index for the FTS-hydration path. Builders call this AFTER the bulk materialize (index-after-load). */
73
+ export declare function createPOINameKeyIndex(db: Kysely<POIDatabase>): Promise<void>;
74
+ export declare const POI_FTS_TABLE = "poi_search";
75
+ /** FTS5 stays raw SQL by project rule (Kysely can't express virtual tables). Content-keyed by name_key. */
76
+ export declare function createPOISearchFTS(db: DatabaseSync): void;
77
+ //# sourceMappingURL=poi-schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"poi-schema.d.ts","sourceRoot":"","sources":["../poi-schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAE/C,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAA;AACnE,OAAO,EAAO,KAAK,MAAM,EAAE,MAAM,QAAQ,CAAA;AAEzC,+EAA+E;AAC/E,MAAM,WAAW,QAAQ;IACxB,wEAAwE;IACxE,OAAO,EAAE,MAAM,CAAA;IACf,sEAAsE;IACtE,WAAW,EAAE,MAAM,CAAA;IACnB,2FAA2F;IAC3F,QAAQ,EAAE,MAAM,CAAA;IAChB,4EAA4E;IAC5E,SAAS,EAAE,MAAM,CAAA;IACjB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;IACnB,wEAAwE;IACxE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;IACjB,wDAAwD;IACxD,OAAO,EAAE,MAAM,CAAA;IACf,wEAAwE;IACxE,UAAU,EAAE,MAAM,CAAA;IAClB,qEAAqE;IACrE,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;CACtB;AAED;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC7B,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;IACnB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;CACtB;AAED,uEAAuE;AACvE,MAAM,WAAW,oBAAoB;IACpC,EAAE,EAAE,MAAM,CAAA;IACV,QAAQ,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,WAAW,WAAY,SAAQ,qBAAqB;IACzD,GAAG,EAAE,QAAQ,CAAA;IACb,SAAS,EAAE,aAAa,CAAA;IACxB,kBAAkB,EAAE,oBAAoB,CAAA;CACxC;AAED,qGAAqG;AACrG,eAAO,MAAM,WAAW,iKAad,CAAA;AAEV,wBAAsB,sBAAsB,CAAC,EAAE,EAAE,MAAM,CAAC,WAAW,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAqBnF;AAED,wBAAsB,cAAc,CAAC,EAAE,EAAE,MAAM,CAAC,WAAW,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAmB3E;AAED,oHAAoH;AACpH,wBAAsB,qBAAqB,CAAC,EAAE,EAAE,MAAM,CAAC,WAAW,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAElF;AAED,eAAO,MAAM,aAAa,eAAe,CAAA;AAEzC,2GAA2G;AAC3G,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,YAAY,GAAG,IAAI,CAIzD"}
@@ -0,0 +1,81 @@
1
+ /**
2
+ * @copyright Sister Software
3
+ * @license AGPL-3.0
4
+ * @author Teffen Ellis, et al.
5
+ *
6
+ * Typed schema for poi.db — spatial layer #1 (spec §3.4). One clustered `WITHOUT ROWID` B-tree
7
+ * keyed `(h3_cell, category_id, neg_rank, rowid_key)` so "everything near this res-9 cell" is a
8
+ * contiguous key range (the byte-range/httpvfs access pattern, same discipline as the candidate
9
+ * gazetteer). Rows carry denormalized name/brand/coords; category ids are small ints via the
10
+ * `poi_category_codes` dictionary (poi-taxonomy category ids are the string side). The DB also
11
+ * embeds the layer-contract tables from `@mailwoman/core/layers` — the builder writes the
12
+ * manifest (tier `shipped`, spine `h3` res 9) and per-res-6-cell coverage.
13
+ */
14
+ import { sql } from "kysely";
15
+ /** Clustered-key-order column list shared by builder + `INSERT INTO poi SELECT … FROM poi_stage`. */
16
+ export const POI_COLUMNS = [
17
+ "h3_cell",
18
+ "category_id",
19
+ "neg_rank",
20
+ "rowid_key",
21
+ "name",
22
+ "name_key",
23
+ "brand_wikidata",
24
+ "latitude",
25
+ "longitude",
26
+ "country",
27
+ "confidence",
28
+ "gers_id",
29
+ ];
30
+ export async function createPOIStagingTables(db) {
31
+ await db.schema
32
+ .createTable("poi_category_codes")
33
+ .addColumn("id", "integer", (c) => c.primaryKey())
34
+ .addColumn("category", "text", (c) => c.unique())
35
+ .execute();
36
+ await db.schema
37
+ .createTable("poi_stage")
38
+ .addColumn("h3_cell", "integer")
39
+ .addColumn("category_id", "integer")
40
+ .addColumn("neg_rank", "real")
41
+ .addColumn("rowid_key", "integer")
42
+ .addColumn("name", "text")
43
+ .addColumn("name_key", "text")
44
+ .addColumn("brand_wikidata", "text")
45
+ .addColumn("latitude", "real")
46
+ .addColumn("longitude", "real")
47
+ .addColumn("country", "text")
48
+ .addColumn("confidence", "real")
49
+ .addColumn("gers_id", "text")
50
+ .execute();
51
+ }
52
+ export async function createPOITable(db) {
53
+ await db.schema
54
+ .createTable("poi")
55
+ .addColumn("h3_cell", "integer", (c) => c.notNull())
56
+ .addColumn("category_id", "integer", (c) => c.notNull())
57
+ .addColumn("neg_rank", "real", (c) => c.notNull())
58
+ .addColumn("rowid_key", "integer", (c) => c.notNull())
59
+ .addColumn("name", "text")
60
+ .addColumn("name_key", "text")
61
+ .addColumn("brand_wikidata", "text")
62
+ .addColumn("latitude", "real", (c) => c.notNull())
63
+ .addColumn("longitude", "real", (c) => c.notNull())
64
+ .addColumn("country", "text", (c) => c.notNull())
65
+ .addColumn("confidence", "real", (c) => c.notNull())
66
+ .addColumn("gers_id", "text")
67
+ .addPrimaryKeyConstraint("poi_pk", ["h3_cell", "category_id", "neg_rank", "rowid_key"])
68
+ // `WITHOUT ROWID` has no first-class builder; the raw modifier is the idiomatic fallback.
69
+ .modifyEnd(sql `without rowid`)
70
+ .execute();
71
+ }
72
+ /** Secondary index for the FTS-hydration path. Builders call this AFTER the bulk materialize (index-after-load). */
73
+ export async function createPOINameKeyIndex(db) {
74
+ await db.schema.createIndex("poi_name_key").on("poi").column("name_key").execute();
75
+ }
76
+ export const POI_FTS_TABLE = "poi_search";
77
+ /** FTS5 stays raw SQL by project rule (Kysely can't express virtual tables). Content-keyed by name_key. */
78
+ export function createPOISearchFTS(db) {
79
+ db.exec(`CREATE VIRTUAL TABLE ${POI_FTS_TABLE} USING fts5(name, name_key UNINDEXED, h3_cell UNINDEXED, tokenize = 'unicode61')`);
80
+ }
81
+ //# sourceMappingURL=poi-schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"poi-schema.js","sourceRoot":"","sources":["../poi-schema.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mailwoman/resolver-wof-sqlite",
3
- "version": "7.0.0",
3
+ "version": "7.2.0",
4
4
  "description": "Who's On First SQLite resolver — FTS5-backed PlaceLookup that resolves text queries to WOF place IDs + coordinates. Optional dependency for callers who want geocoding.",
5
5
  "license": "AGPL-3.0-only OR LicenseRef-Commercial",
6
6
  "repository": {
@@ -19,201 +19,140 @@
19
19
  "exports": {
20
20
  "./package.json": "./package.json",
21
21
  ".": {
22
- "types": "./out/index.d.ts",
23
- "default": "./out/index.js"
22
+ "node": "./index.ts",
23
+ "default": "./out/index.js",
24
+ "types": "./out/index.d.ts"
24
25
  },
25
26
  "./fts": {
26
- "types": "./out/fts.d.ts",
27
- "default": "./out/fts.js"
27
+ "node": "./fts.ts",
28
+ "default": "./out/fts.js",
29
+ "types": "./out/fts.d.ts"
28
30
  },
29
31
  "./build-slim": {
30
- "types": "./out/build-slim.d.ts",
31
- "default": "./out/build-slim.js"
32
+ "node": "./build-slim.ts",
33
+ "default": "./out/build-slim.js",
34
+ "types": "./out/build-slim.d.ts"
32
35
  },
33
36
  "./fst-builder": {
34
- "types": "./out/fst-builder.d.ts",
35
- "default": "./out/fst-builder.js"
37
+ "node": "./fst-builder.ts",
38
+ "default": "./out/fst-builder.js",
39
+ "types": "./out/fst-builder.d.ts"
36
40
  },
37
41
  "./street-morphology-fst-builder": {
38
- "types": "./out/street-morphology-fst-builder.d.ts",
39
- "default": "./out/street-morphology-fst-builder.js"
42
+ "node": "./street-morphology-fst-builder.ts",
43
+ "default": "./out/street-morphology-fst-builder.js",
44
+ "types": "./out/street-morphology-fst-builder.d.ts"
40
45
  },
41
46
  "./fst-matcher": {
42
- "types": "./out/fst-matcher.d.ts",
43
- "default": "./out/fst-matcher.js"
47
+ "node": "./fst-matcher.ts",
48
+ "default": "./out/fst-matcher.js",
49
+ "types": "./out/fst-matcher.d.ts"
44
50
  },
45
51
  "./fst-serialize": {
46
- "types": "./out/fst-serialize.d.ts",
47
- "default": "./out/fst-serialize.js"
52
+ "node": "./fst-serialize.ts",
53
+ "default": "./out/fst-serialize.js",
54
+ "types": "./out/fst-serialize.d.ts"
48
55
  },
49
56
  "./fst-autocomplete": {
50
- "types": "./out/fst-autocomplete.d.ts",
51
- "default": "./out/fst-autocomplete.js"
57
+ "node": "./fst-autocomplete.ts",
58
+ "default": "./out/fst-autocomplete.js",
59
+ "types": "./out/fst-autocomplete.d.ts"
52
60
  },
53
61
  "./fst-types": {
54
- "types": "./out/fst-types.d.ts",
55
- "default": "./out/fst-types.js"
62
+ "node": "./fst-types.ts",
63
+ "default": "./out/fst-types.js",
64
+ "types": "./out/fst-types.d.ts"
56
65
  },
57
66
  "./fst-deserialize-web": {
58
- "types": "./out/fst-deserialize-web.d.ts",
59
- "default": "./out/fst-deserialize-web.js"
67
+ "node": "./fst-deserialize-web.ts",
68
+ "default": "./out/fst-deserialize-web.js",
69
+ "types": "./out/fst-deserialize-web.d.ts"
60
70
  },
61
71
  "./unified-schema": {
62
- "types": "./out/unified-schema.d.ts",
63
- "default": "./out/unified-schema.js"
72
+ "node": "./unified-schema.ts",
73
+ "default": "./out/unified-schema.js",
74
+ "types": "./out/unified-schema.d.ts"
64
75
  },
65
76
  "./ancestry-backfill": {
66
- "types": "./out/ancestry-backfill.d.ts",
67
- "default": "./out/ancestry-backfill.js"
77
+ "node": "./ancestry-backfill.ts",
78
+ "default": "./out/ancestry-backfill.js",
79
+ "types": "./out/ancestry-backfill.d.ts"
68
80
  },
69
81
  "./coincident-roles": {
70
- "types": "./out/coincident-roles.d.ts",
71
- "default": "./out/coincident-roles.js"
82
+ "node": "./coincident-roles.ts",
83
+ "default": "./out/coincident-roles.js",
84
+ "types": "./out/coincident-roles.d.ts"
72
85
  },
73
86
  "./geonames-aliases": {
74
- "types": "./out/geonames-aliases.d.ts",
75
- "default": "./out/geonames-aliases.js"
87
+ "node": "./geonames-aliases.ts",
88
+ "default": "./out/geonames-aliases.js",
89
+ "types": "./out/geonames-aliases.d.ts"
76
90
  },
77
91
  "./geonames-postal": {
78
- "types": "./out/geonames-postal.d.ts",
79
- "default": "./out/geonames-postal.js"
92
+ "node": "./geonames-postal.ts",
93
+ "default": "./out/geonames-postal.js",
94
+ "types": "./out/geonames-postal.d.ts"
80
95
  },
81
96
  "./geo": {
82
- "types": "./out/geo.d.ts",
83
- "default": "./out/geo.js"
97
+ "node": "./geo.ts",
98
+ "default": "./out/geo.js",
99
+ "types": "./out/geo.d.ts"
84
100
  },
85
101
  "./street-normalize": {
86
- "types": "./out/street-normalize.d.ts",
87
- "default": "./out/street-normalize.js"
102
+ "node": "./street-normalize.ts",
103
+ "default": "./out/street-normalize.js",
104
+ "types": "./out/street-normalize.d.ts"
88
105
  },
89
106
  "./build-candidate": {
90
- "types": "./out/build-candidate.d.ts",
91
- "default": "./out/build-candidate.js"
107
+ "node": "./build-candidate.ts",
108
+ "default": "./out/build-candidate.js",
109
+ "types": "./out/build-candidate.d.ts"
92
110
  },
93
111
  "./candidate-schema": {
94
- "types": "./out/candidate-schema.d.ts",
95
- "default": "./out/candidate-schema.js"
112
+ "node": "./candidate-schema.ts",
113
+ "default": "./out/candidate-schema.js",
114
+ "types": "./out/candidate-schema.d.ts"
96
115
  },
97
116
  "./postal-city-alias-schema": {
98
- "types": "./out/postal-city-alias-schema.d.ts",
99
- "default": "./out/postal-city-alias-schema.js"
117
+ "node": "./postal-city-alias-schema.ts",
118
+ "default": "./out/postal-city-alias-schema.js",
119
+ "types": "./out/postal-city-alias-schema.d.ts"
100
120
  },
101
121
  "./address-point-schema": {
102
- "types": "./out/address-point-schema.d.ts",
103
- "default": "./out/address-point-schema.js"
122
+ "node": "./address-point-schema.ts",
123
+ "default": "./out/address-point-schema.js",
124
+ "types": "./out/address-point-schema.d.ts"
104
125
  },
105
126
  "./street-centroid-schema": {
106
- "types": "./out/street-centroid-schema.d.ts",
107
- "default": "./out/street-centroid-schema.js"
127
+ "node": "./street-centroid-schema.ts",
128
+ "default": "./out/street-centroid-schema.js",
129
+ "types": "./out/street-centroid-schema.d.ts"
108
130
  },
109
131
  "./street-segment-schema": {
110
- "types": "./out/street-segment-schema.d.ts",
111
- "default": "./out/street-segment-schema.js"
132
+ "node": "./street-segment-schema.ts",
133
+ "default": "./out/street-segment-schema.js",
134
+ "types": "./out/street-segment-schema.d.ts"
135
+ },
136
+ "./poi-schema": {
137
+ "node": "./poi-schema.ts",
138
+ "default": "./out/poi-schema.js",
139
+ "types": "./out/poi-schema.d.ts"
140
+ },
141
+ "./poi-lookup": {
142
+ "node": "./poi-lookup.ts",
143
+ "default": "./out/poi-lookup.js",
144
+ "types": "./out/poi-lookup.d.ts"
112
145
  }
113
146
  },
114
147
  "publishConfig": {
115
- "exports": {
116
- "./package.json": "./package.json",
117
- ".": {
118
- "types": "./out/index.d.ts",
119
- "default": "./out/index.js"
120
- },
121
- "./fts": {
122
- "types": "./out/fts.d.ts",
123
- "default": "./out/fts.js"
124
- },
125
- "./build-slim": {
126
- "types": "./out/build-slim.d.ts",
127
- "default": "./out/build-slim.js"
128
- },
129
- "./fst-builder": {
130
- "types": "./out/fst-builder.d.ts",
131
- "default": "./out/fst-builder.js"
132
- },
133
- "./street-morphology-fst-builder": {
134
- "types": "./out/street-morphology-fst-builder.d.ts",
135
- "default": "./out/street-morphology-fst-builder.js"
136
- },
137
- "./fst-matcher": {
138
- "types": "./out/fst-matcher.d.ts",
139
- "default": "./out/fst-matcher.js"
140
- },
141
- "./fst-serialize": {
142
- "types": "./out/fst-serialize.d.ts",
143
- "default": "./out/fst-serialize.js"
144
- },
145
- "./fst-autocomplete": {
146
- "types": "./out/fst-autocomplete.d.ts",
147
- "default": "./out/fst-autocomplete.js"
148
- },
149
- "./fst-types": {
150
- "types": "./out/fst-types.d.ts",
151
- "default": "./out/fst-types.js"
152
- },
153
- "./fst-deserialize-web": {
154
- "types": "./out/fst-deserialize-web.d.ts",
155
- "default": "./out/fst-deserialize-web.js"
156
- },
157
- "./unified-schema": {
158
- "types": "./out/unified-schema.d.ts",
159
- "default": "./out/unified-schema.js"
160
- },
161
- "./ancestry-backfill": {
162
- "types": "./out/ancestry-backfill.d.ts",
163
- "default": "./out/ancestry-backfill.js"
164
- },
165
- "./coincident-roles": {
166
- "types": "./out/coincident-roles.d.ts",
167
- "default": "./out/coincident-roles.js"
168
- },
169
- "./geonames-aliases": {
170
- "types": "./out/geonames-aliases.d.ts",
171
- "default": "./out/geonames-aliases.js"
172
- },
173
- "./geonames-postal": {
174
- "types": "./out/geonames-postal.d.ts",
175
- "default": "./out/geonames-postal.js"
176
- },
177
- "./geo": {
178
- "types": "./out/geo.d.ts",
179
- "default": "./out/geo.js"
180
- },
181
- "./street-normalize": {
182
- "types": "./out/street-normalize.d.ts",
183
- "default": "./out/street-normalize.js"
184
- },
185
- "./build-candidate": {
186
- "types": "./out/build-candidate.d.ts",
187
- "default": "./out/build-candidate.js"
188
- },
189
- "./candidate-schema": {
190
- "types": "./out/candidate-schema.d.ts",
191
- "default": "./out/candidate-schema.js"
192
- },
193
- "./postal-city-alias-schema": {
194
- "types": "./out/postal-city-alias-schema.d.ts",
195
- "default": "./out/postal-city-alias-schema.js"
196
- },
197
- "./address-point-schema": {
198
- "types": "./out/address-point-schema.d.ts",
199
- "default": "./out/address-point-schema.js"
200
- },
201
- "./street-centroid-schema": {
202
- "types": "./out/street-centroid-schema.d.ts",
203
- "default": "./out/street-centroid-schema.js"
204
- },
205
- "./street-segment-schema": {
206
- "types": "./out/street-segment-schema.d.ts",
207
- "default": "./out/street-segment-schema.js"
208
- }
209
- },
210
148
  "access": "public"
211
149
  },
212
150
  "dependencies": {
213
- "@mailwoman/codex": "7.0.0",
214
- "@mailwoman/core": "7.0.0",
215
- "@mailwoman/resolver": "7.0.0",
216
- "@mailwoman/spatial": "7.0.0",
151
+ "@mailwoman/codex": "7.2.0",
152
+ "@mailwoman/core": "7.2.0",
153
+ "@mailwoman/resolver": "7.2.0",
154
+ "@mailwoman/spatial": "7.2.0",
155
+ "h3-js": "^4.5.0",
217
156
  "kysely": "^0.29.3"
218
157
  }
219
158
  }