@cyanheads/eurostat-mcp-server 0.11.2 → 0.11.3
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/AGENTS.md +6 -4
- package/CLAUDE.md +6 -4
- package/README.md +17 -15
- package/changelog/0.11.x/0.11.3.md +45 -0
- package/changelog/template.md +10 -3
- package/dist/config/server-config.js +1 -1
- package/dist/config/server-config.js.map +1 -1
- package/dist/mcp-server/tools/definitions/eurostat-browse-themes.tool.d.ts +1 -1
- package/dist/mcp-server/tools/definitions/eurostat-browse-themes.tool.js +2 -2
- package/dist/mcp-server/tools/definitions/eurostat-browse-themes.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/eurostat-dataframe-describe.tool.js +2 -2
- package/dist/mcp-server/tools/definitions/eurostat-dataframe-describe.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/eurostat-dataframe-drop.tool.js +1 -1
- package/dist/mcp-server/tools/definitions/eurostat-dataframe-drop.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/eurostat-dataframe-query.tool.js +1 -1
- package/dist/mcp-server/tools/definitions/eurostat-dataframe-query.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/eurostat-download-dataset.tool.d.ts +18 -6
- package/dist/mcp-server/tools/definitions/eurostat-download-dataset.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/eurostat-download-dataset.tool.js +129 -31
- package/dist/mcp-server/tools/definitions/eurostat-download-dataset.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/eurostat-get-dataset-info.tool.js +1 -1
- package/dist/mcp-server/tools/definitions/eurostat-get-dataset-info.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/eurostat-get-dimension-values.tool.d.ts +6 -4
- package/dist/mcp-server/tools/definitions/eurostat-get-dimension-values.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/eurostat-get-dimension-values.tool.js +139 -53
- package/dist/mcp-server/tools/definitions/eurostat-get-dimension-values.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/eurostat-query-dataset.tool.d.ts +20 -8
- package/dist/mcp-server/tools/definitions/eurostat-query-dataset.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/eurostat-query-dataset.tool.js +208 -43
- package/dist/mcp-server/tools/definitions/eurostat-query-dataset.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/eurostat-search-datasets.tool.d.ts +16 -6
- package/dist/mcp-server/tools/definitions/eurostat-search-datasets.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/eurostat-search-datasets.tool.js +62 -23
- package/dist/mcp-server/tools/definitions/eurostat-search-datasets.tool.js.map +1 -1
- package/dist/mcp-server/tools/narrowing-advice.d.ts +64 -3
- package/dist/mcp-server/tools/narrowing-advice.d.ts.map +1 -1
- package/dist/mcp-server/tools/narrowing-advice.js +160 -4
- package/dist/mcp-server/tools/narrowing-advice.js.map +1 -1
- package/dist/mcp-server/tools/notice-text.d.ts +18 -0
- package/dist/mcp-server/tools/notice-text.d.ts.map +1 -0
- package/dist/mcp-server/tools/notice-text.js +40 -0
- package/dist/mcp-server/tools/notice-text.js.map +1 -0
- package/dist/mcp-server/tools/value-list-paging.d.ts +11 -1
- package/dist/mcp-server/tools/value-list-paging.d.ts.map +1 -1
- package/dist/mcp-server/tools/value-list-paging.js +13 -1
- package/dist/mcp-server/tools/value-list-paging.js.map +1 -1
- package/dist/services/eurostat-bulk/eurostat-bulk-service.d.ts +4 -3
- package/dist/services/eurostat-bulk/eurostat-bulk-service.d.ts.map +1 -1
- package/dist/services/eurostat-bulk/eurostat-bulk-service.js +107 -54
- package/dist/services/eurostat-bulk/eurostat-bulk-service.js.map +1 -1
- package/dist/services/eurostat-bulk/types.d.ts +27 -4
- package/dist/services/eurostat-bulk/types.d.ts.map +1 -1
- package/dist/services/eurostat-bulk/types.js.map +1 -1
- package/dist/services/eurostat-catalogue/dimension-value-index.d.ts +81 -0
- package/dist/services/eurostat-catalogue/dimension-value-index.d.ts.map +1 -0
- package/dist/services/eurostat-catalogue/dimension-value-index.js +410 -0
- package/dist/services/eurostat-catalogue/dimension-value-index.js.map +1 -0
- package/dist/services/eurostat-catalogue/eurostat-catalogue-service.d.ts +87 -7
- package/dist/services/eurostat-catalogue/eurostat-catalogue-service.d.ts.map +1 -1
- package/dist/services/eurostat-catalogue/eurostat-catalogue-service.js +333 -81
- package/dist/services/eurostat-catalogue/eurostat-catalogue-service.js.map +1 -1
- package/dist/services/eurostat-catalogue/search-text.d.ts +12 -0
- package/dist/services/eurostat-catalogue/search-text.d.ts.map +1 -0
- package/dist/services/eurostat-catalogue/search-text.js +31 -0
- package/dist/services/eurostat-catalogue/search-text.js.map +1 -0
- package/dist/services/eurostat-catalogue/types.d.ts +11 -0
- package/dist/services/eurostat-catalogue/types.d.ts.map +1 -1
- package/dist/services/eurostat-data/eurostat-data-service.d.ts +26 -5
- package/dist/services/eurostat-data/eurostat-data-service.d.ts.map +1 -1
- package/dist/services/eurostat-data/eurostat-data-service.js +132 -16
- package/dist/services/eurostat-data/eurostat-data-service.js.map +1 -1
- package/dist/services/eurostat-data/sdmx-metadata.d.ts.map +1 -1
- package/dist/services/eurostat-data/sdmx-metadata.js +4 -20
- package/dist/services/eurostat-data/sdmx-metadata.js.map +1 -1
- package/dist/services/eurostat-data/types.d.ts +92 -2
- package/dist/services/eurostat-data/types.d.ts.map +1 -1
- package/dist/services/eurostat-data/types.js.map +1 -1
- package/dist/services/eurostat-filters.d.ts +18 -6
- package/dist/services/eurostat-filters.d.ts.map +1 -1
- package/dist/services/eurostat-filters.js +24 -7
- package/dist/services/eurostat-filters.js.map +1 -1
- package/dist/services/eurostat-periods.d.ts +54 -1
- package/dist/services/eurostat-periods.d.ts.map +1 -1
- package/dist/services/eurostat-periods.js +101 -1
- package/dist/services/eurostat-periods.js.map +1 -1
- package/dist/services/xml-entities.d.ts +13 -0
- package/dist/services/xml-entities.d.ts.map +1 -0
- package/dist/services/xml-entities.js +31 -0
- package/dist/services/xml-entities.js.map +1 -0
- package/package.json +5 -5
- package/server.json +5 -5
- package/skills/eurostat-guide/SKILL.md +6 -6
- package/skills/eurostat-guide/references/sql-analysis.md +5 -5
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/services/eurostat-bulk/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/services/eurostat-bulk/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAcH,4EAA4E;AAC5E,MAAM,CAAC,MAAM,WAAW,GAAG,MAAM,CAAC"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview The dimension value index search reads when a dataset's own text lacks a
|
|
3
|
+
* query word: the codes Eurostat's metabase lists for each dataset (`time` excluded), each
|
|
4
|
+
* labelled in English from the global codelists. Both downloads are gzip files read as
|
|
5
|
+
* streams, never held whole; the index keeps joined strings and typed arrays.
|
|
6
|
+
* @module services/eurostat-catalogue/dimension-value-index
|
|
7
|
+
*/
|
|
8
|
+
import { type RequestContext } from '@cyanheads/mcp-ts-core/utils';
|
|
9
|
+
import type { MatchedValue } from './types.js';
|
|
10
|
+
/** The index's arrays. A value is one (dimension, code) pair, shared by every dataset listing it. */
|
|
11
|
+
interface IndexParts {
|
|
12
|
+
codeStart: Int32Array;
|
|
13
|
+
/** Every value's code, joined; value `v` spans `codeStart[v]` to `codeStart[v + 1] - 1`. */
|
|
14
|
+
codeText: string;
|
|
15
|
+
datasetIds: Map<string, number>;
|
|
16
|
+
datasetStart: Int32Array;
|
|
17
|
+
/** Each dataset's values, in metabase order; dataset `d` spans `datasetStart[d]` to `datasetStart[d + 1]`. */
|
|
18
|
+
datasetValues: Int32Array;
|
|
19
|
+
dimensions: string[];
|
|
20
|
+
labelStart: Int32Array;
|
|
21
|
+
/** Every distinct label as published, joined. */
|
|
22
|
+
labelText: string;
|
|
23
|
+
searchStart: Int32Array;
|
|
24
|
+
/** Every label folded as search reads it, joined in the same order as `labelText`. */
|
|
25
|
+
searchText: string;
|
|
26
|
+
valueDimension: Int32Array;
|
|
27
|
+
/** Each value's label id, or -1 when its codelist names it in no English label. */
|
|
28
|
+
valueLabel: Int32Array;
|
|
29
|
+
}
|
|
30
|
+
/** The labels of the codes Eurostat's metabase lists per dataset, searchable at word starts. */
|
|
31
|
+
export declare class DimensionValueIndex {
|
|
32
|
+
/** Epoch ms when this index finished loading: its age, and the snapshot a cursor binds. */
|
|
33
|
+
readonly loadedAt: number;
|
|
34
|
+
private readonly parts;
|
|
35
|
+
constructor(parts: IndexParts, loadedAt: number);
|
|
36
|
+
/** Dataset, value, and label counts, for logging. */
|
|
37
|
+
get size(): {
|
|
38
|
+
datasets: number;
|
|
39
|
+
labels: number;
|
|
40
|
+
values: number;
|
|
41
|
+
};
|
|
42
|
+
/** The id of a dataset the metabase lists, or undefined for one it does not. */
|
|
43
|
+
datasetOf(code: string): number | undefined;
|
|
44
|
+
/**
|
|
45
|
+
* Flags, per label id, whether `word` occurs in the folded label with no letter or digit
|
|
46
|
+
* right before it; undefined when no label carries it. One scan of the joined labels;
|
|
47
|
+
* past a label's first match the scan resumes at the next label.
|
|
48
|
+
*/
|
|
49
|
+
labelsAtWordStart(word: string): Uint8Array | undefined;
|
|
50
|
+
/** Whether some value of `dataset` has a label `flags` marks. */
|
|
51
|
+
hasValue(dataset: number, flags: Uint8Array): boolean;
|
|
52
|
+
/** Whether one value of `dataset` has a label every one of `flagSets` marks. */
|
|
53
|
+
hasValueMarkedByAll(dataset: number, flagSets: readonly Uint8Array[]): boolean;
|
|
54
|
+
/**
|
|
55
|
+
* The values of `dataset` whose label any of `flagSets` marks: those marked by the most
|
|
56
|
+
* flag sets first, so a value carrying every word leads the ones carrying one of them,
|
|
57
|
+
* then metabase order.
|
|
58
|
+
*/
|
|
59
|
+
valuesMarked(dataset: number, flagSets: readonly Uint8Array[]): MatchedValue[];
|
|
60
|
+
private someValue;
|
|
61
|
+
/** The label whose span of the joined search text holds offset `at`. */
|
|
62
|
+
private labelAt;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Build the index from the metabase (`dataset<TAB>dimension<TAB>code` lines) and the codelist
|
|
66
|
+
* dump (SDMX 2.1 structure XML), each read as a stream of decoded text. The metabase is read
|
|
67
|
+
* first, so only the labels of codes it lists are kept from the codelist named, in upper case,
|
|
68
|
+
* after each dimension. A code's first English name is its label.
|
|
69
|
+
*
|
|
70
|
+
* Throws on a metabase line that is not three tab-separated fields, a metabase that lists no
|
|
71
|
+
* dataset, a codelist dump that stops before its root element closes, or codelists that label
|
|
72
|
+
* none of the metabase's codes: each is a broken download, not an empty catalogue.
|
|
73
|
+
*/
|
|
74
|
+
export declare function buildDimensionValueIndex(metabase: AsyncIterable<string>, codelists: AsyncIterable<string>): Promise<DimensionValueIndex>;
|
|
75
|
+
/**
|
|
76
|
+
* Download and build the index: the metabase first, then the codelist dump, each within
|
|
77
|
+
* `EUROSTAT_BULK_TIMEOUT_MS`. One attempt; the caller schedules retries.
|
|
78
|
+
*/
|
|
79
|
+
export declare function fetchDimensionValueIndex(context: RequestContext): Promise<DimensionValueIndex>;
|
|
80
|
+
export {};
|
|
81
|
+
//# sourceMappingURL=dimension-value-index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dimension-value-index.d.ts","sourceRoot":"","sources":["../../../src/services/eurostat-catalogue/dimension-value-index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAoB,KAAK,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAIrF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AA6B/C,qGAAqG;AACrG,UAAU,UAAU;IAClB,SAAS,EAAE,UAAU,CAAC;IACtB,4FAA4F;IAC5F,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,YAAY,EAAE,UAAU,CAAC;IACzB,8GAA8G;IAC9G,aAAa,EAAE,UAAU,CAAC;IAC1B,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,UAAU,EAAE,UAAU,CAAC;IACvB,iDAAiD;IACjD,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,UAAU,CAAC;IACxB,sFAAsF;IACtF,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,UAAU,CAAC;IAC3B,mFAAmF;IACnF,UAAU,EAAE,UAAU,CAAC;CACxB;AAWD,gGAAgG;AAChG,qBAAa,mBAAmB;IAC9B,2FAA2F;IAC3F,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAa;IAEnC,YAAY,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAG9C;IAED,qDAAqD;IACrD,IAAI,IAAI,IAAI;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAM/D;IAED,gFAAgF;IAChF,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAE1C;IAED;;;;OAIG;IACH,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS,CAetD;IAED,iEAAiE;IACjE,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,GAAG,OAAO,CAEpD;IAED,gFAAgF;IAChF,mBAAmB,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,UAAU,EAAE,GAAG,OAAO,CAE7E;IAED;;;;OAIG;IACH,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,UAAU,EAAE,GAAG,YAAY,EAAE,CAuB7E;IAED,OAAO,CAAC,SAAS;IAajB,wEAAwE;IACxE,OAAO,CAAC,OAAO;CAWhB;AA6OD;;;;;;;;;GASG;AACH,wBAAsB,wBAAwB,CAC5C,QAAQ,EAAE,aAAa,CAAC,MAAM,CAAC,EAC/B,SAAS,EAAE,aAAa,CAAC,MAAM,CAAC,GAC/B,OAAO,CAAC,mBAAmB,CAAC,CAM9B;AA0CD;;;GAGG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAU9F"}
|
|
@@ -0,0 +1,410 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview The dimension value index search reads when a dataset's own text lacks a
|
|
3
|
+
* query word: the codes Eurostat's metabase lists for each dataset (`time` excluded), each
|
|
4
|
+
* labelled in English from the global codelists. Both downloads are gzip files read as
|
|
5
|
+
* streams, never held whole; the index keeps joined strings and typed arrays.
|
|
6
|
+
* @module services/eurostat-catalogue/dimension-value-index
|
|
7
|
+
*/
|
|
8
|
+
import { fetchWithTimeout } from '@cyanheads/mcp-ts-core/utils';
|
|
9
|
+
import { getServerConfig } from '../../config/server-config.js';
|
|
10
|
+
import { decodeXmlEntities } from '../xml-entities.js';
|
|
11
|
+
import { continuesWord, foldSearchText } from './search-text.js';
|
|
12
|
+
/** The period dimension, listed for every dataset; its codes are dates, not vocabulary. */
|
|
13
|
+
const TIME_DIMENSION = 'time';
|
|
14
|
+
/** Joins labels and codes. A search word holds no whitespace, so no match spans two labels. */
|
|
15
|
+
const SEPARATOR = '\n';
|
|
16
|
+
/**
|
|
17
|
+
* A codelist's opening tag, a code's opening tag, an English name, and a code's closing tag.
|
|
18
|
+
* `<s:Code\b` does not match `<s:Codelist`.
|
|
19
|
+
*/
|
|
20
|
+
const CODELIST_TOKEN = /<s:Codelist\b[^>]*?\sid="([^"]*)"|<s:Code\b[^>]*?\sid="([^"]*)"|<c:Name xml:lang="en">([^<]*)<\/c:Name>|<\/s:Code>/g;
|
|
21
|
+
const CODE_CLOSE = '</s:Code>';
|
|
22
|
+
/** The codelist dump's root element closes the document; a stream without it was cut short. */
|
|
23
|
+
const DOCUMENT_CLOSE = '</m:Structure>';
|
|
24
|
+
/**
|
|
25
|
+
* A copy of `text` in a new string. A string cut from a decoded chunk keeps the whole chunk
|
|
26
|
+
* alive, and on JavaScriptCore so does a concatenate-then-slice copy, which slices the
|
|
27
|
+
* original again; a join writes new characters on every runtime.
|
|
28
|
+
*/
|
|
29
|
+
function copyOf(text) {
|
|
30
|
+
return [text, ''].join(SEPARATOR).slice(0, -SEPARATOR.length);
|
|
31
|
+
}
|
|
32
|
+
/** Offsets of each part joined by {@link SEPARATOR}, plus one past the end. */
|
|
33
|
+
function offsetsOf(parts) {
|
|
34
|
+
const starts = new Int32Array(parts.length + 1);
|
|
35
|
+
for (let i = 0; i < parts.length; i++) {
|
|
36
|
+
starts[i + 1] = starts[i] + parts[i].length + SEPARATOR.length;
|
|
37
|
+
}
|
|
38
|
+
return starts;
|
|
39
|
+
}
|
|
40
|
+
/** The labels of the codes Eurostat's metabase lists per dataset, searchable at word starts. */
|
|
41
|
+
export class DimensionValueIndex {
|
|
42
|
+
/** Epoch ms when this index finished loading: its age, and the snapshot a cursor binds. */
|
|
43
|
+
loadedAt;
|
|
44
|
+
parts;
|
|
45
|
+
constructor(parts, loadedAt) {
|
|
46
|
+
this.parts = parts;
|
|
47
|
+
this.loadedAt = loadedAt;
|
|
48
|
+
}
|
|
49
|
+
/** Dataset, value, and label counts, for logging. */
|
|
50
|
+
get size() {
|
|
51
|
+
return {
|
|
52
|
+
datasets: this.parts.datasetIds.size,
|
|
53
|
+
labels: this.parts.labelStart.length - 1,
|
|
54
|
+
values: this.parts.valueLabel.length,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
/** The id of a dataset the metabase lists, or undefined for one it does not. */
|
|
58
|
+
datasetOf(code) {
|
|
59
|
+
return this.parts.datasetIds.get(code);
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Flags, per label id, whether `word` occurs in the folded label with no letter or digit
|
|
63
|
+
* right before it; undefined when no label carries it. One scan of the joined labels;
|
|
64
|
+
* past a label's first match the scan resumes at the next label.
|
|
65
|
+
*/
|
|
66
|
+
labelsAtWordStart(word) {
|
|
67
|
+
const { searchStart, searchText } = this.parts;
|
|
68
|
+
let flags;
|
|
69
|
+
let at = searchText.indexOf(word);
|
|
70
|
+
while (at !== -1) {
|
|
71
|
+
if (continuesWord(searchText, at)) {
|
|
72
|
+
at = searchText.indexOf(word, at + 1);
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
const label = this.labelAt(at);
|
|
76
|
+
flags ??= new Uint8Array(searchStart.length - 1);
|
|
77
|
+
flags[label] = 1;
|
|
78
|
+
at = searchText.indexOf(word, searchStart[label + 1]);
|
|
79
|
+
}
|
|
80
|
+
return flags;
|
|
81
|
+
}
|
|
82
|
+
/** Whether some value of `dataset` has a label `flags` marks. */
|
|
83
|
+
hasValue(dataset, flags) {
|
|
84
|
+
return this.someValue(dataset, (label) => flags[label] === 1);
|
|
85
|
+
}
|
|
86
|
+
/** Whether one value of `dataset` has a label every one of `flagSets` marks. */
|
|
87
|
+
hasValueMarkedByAll(dataset, flagSets) {
|
|
88
|
+
return this.someValue(dataset, (label) => flagSets.every((flags) => flags[label] === 1));
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* The values of `dataset` whose label any of `flagSets` marks: those marked by the most
|
|
92
|
+
* flag sets first, so a value carrying every word leads the ones carrying one of them,
|
|
93
|
+
* then metabase order.
|
|
94
|
+
*/
|
|
95
|
+
valuesMarked(dataset, flagSets) {
|
|
96
|
+
const { codeStart, codeText, datasetStart, datasetValues, dimensions, labelStart, labelText } = this.parts;
|
|
97
|
+
const values = [];
|
|
98
|
+
for (let at = datasetStart[dataset]; at < datasetStart[dataset + 1]; at++) {
|
|
99
|
+
const value = datasetValues[at];
|
|
100
|
+
const label = this.parts.valueLabel[value];
|
|
101
|
+
const marked = label < 0 ? 0 : flagSets.filter((flags) => flags[label] === 1).length;
|
|
102
|
+
if (marked === 0)
|
|
103
|
+
continue;
|
|
104
|
+
values.push({
|
|
105
|
+
marked,
|
|
106
|
+
value: {
|
|
107
|
+
dimension: dimensions[this.parts.valueDimension[value]],
|
|
108
|
+
code: codeText.slice(codeStart[value], codeStart[value + 1] - 1),
|
|
109
|
+
label: labelText.slice(labelStart[label], labelStart[label + 1] - 1),
|
|
110
|
+
},
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
return values.sort((a, b) => b.marked - a.marked).map(({ value }) => value);
|
|
114
|
+
}
|
|
115
|
+
someValue(dataset, test) {
|
|
116
|
+
const { datasetStart, datasetValues, valueLabel } = this.parts;
|
|
117
|
+
for (let at = datasetStart[dataset]; at < datasetStart[dataset + 1]; at++) {
|
|
118
|
+
const label = valueLabel[datasetValues[at]];
|
|
119
|
+
if (label >= 0 && test(label))
|
|
120
|
+
return true;
|
|
121
|
+
}
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
/** The label whose span of the joined search text holds offset `at`. */
|
|
125
|
+
labelAt(at) {
|
|
126
|
+
const { searchStart } = this.parts;
|
|
127
|
+
let low = 0;
|
|
128
|
+
let high = searchStart.length - 2;
|
|
129
|
+
while (low < high) {
|
|
130
|
+
const mid = (low + high + 1) >> 1;
|
|
131
|
+
if (searchStart[mid] <= at)
|
|
132
|
+
low = mid;
|
|
133
|
+
else
|
|
134
|
+
high = mid - 1;
|
|
135
|
+
}
|
|
136
|
+
return low;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Text of a stream in pieces that each end at a boundary, so no token spans two pieces. Only
|
|
141
|
+
* the newest chunk is searched for a boundary, so a long run without one costs its length.
|
|
142
|
+
*/
|
|
143
|
+
class BoundaryPieces {
|
|
144
|
+
pending = [];
|
|
145
|
+
boundary;
|
|
146
|
+
constructor(boundary) {
|
|
147
|
+
this.boundary = boundary;
|
|
148
|
+
}
|
|
149
|
+
/** The text up to the last boundary in `chunk`, or undefined while `chunk` holds none. */
|
|
150
|
+
cut(chunk) {
|
|
151
|
+
const at = chunk.lastIndexOf(this.boundary);
|
|
152
|
+
if (at === -1) {
|
|
153
|
+
this.pending.push(chunk);
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
const end = at + this.boundary.length;
|
|
157
|
+
this.pending.push(chunk.slice(0, end));
|
|
158
|
+
const piece = this.pending.join('');
|
|
159
|
+
this.pending = [chunk.slice(end)];
|
|
160
|
+
return piece;
|
|
161
|
+
}
|
|
162
|
+
/** The text after the last boundary. */
|
|
163
|
+
rest() {
|
|
164
|
+
const rest = this.pending.join('');
|
|
165
|
+
this.pending = [];
|
|
166
|
+
return rest;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Reads `dataset<TAB>dimension<TAB>code` lines as they stream in, `time` rows skipped. Every
|
|
171
|
+
* string it keeps is a copy, so no decoded chunk outlives its turn.
|
|
172
|
+
*/
|
|
173
|
+
class MetabaseReader {
|
|
174
|
+
line = 0;
|
|
175
|
+
lines = new BoundaryPieces('\n');
|
|
176
|
+
table = {
|
|
177
|
+
datasetIds: new Map(),
|
|
178
|
+
dimensionIds: new Map(),
|
|
179
|
+
dimensions: [],
|
|
180
|
+
rowDatasets: [],
|
|
181
|
+
rowValues: [],
|
|
182
|
+
valueCodes: [],
|
|
183
|
+
valueDimensions: [],
|
|
184
|
+
valueIds: new Map(),
|
|
185
|
+
};
|
|
186
|
+
read(chunk) {
|
|
187
|
+
const text = this.lines.cut(chunk);
|
|
188
|
+
if (text !== undefined)
|
|
189
|
+
for (const line of text.slice(0, -1).split('\n'))
|
|
190
|
+
this.add(line);
|
|
191
|
+
}
|
|
192
|
+
/** The table read. Throws when the metabase listed no dataset: a broken download, not an empty catalogue. */
|
|
193
|
+
finish() {
|
|
194
|
+
const rest = this.lines.rest();
|
|
195
|
+
if (rest)
|
|
196
|
+
this.add(rest);
|
|
197
|
+
if (this.table.datasetIds.size === 0)
|
|
198
|
+
throw new Error('The metabase listed no dataset dimension values');
|
|
199
|
+
return this.table;
|
|
200
|
+
}
|
|
201
|
+
add(text) {
|
|
202
|
+
this.line += 1;
|
|
203
|
+
const line = text.endsWith('\r') ? text.slice(0, -1) : text;
|
|
204
|
+
if (!line)
|
|
205
|
+
return;
|
|
206
|
+
const fields = line.split('\t');
|
|
207
|
+
const [dataset, dimension, code] = fields;
|
|
208
|
+
if (fields.length !== 3 || !dataset || !dimension || code === undefined) {
|
|
209
|
+
throw new Error(`Metabase line ${this.line} is not a dataset, dimension and code separated by tabs`);
|
|
210
|
+
}
|
|
211
|
+
if (dimension === TIME_DIMENSION)
|
|
212
|
+
return;
|
|
213
|
+
const table = this.table;
|
|
214
|
+
let datasetId = table.datasetIds.get(dataset);
|
|
215
|
+
if (datasetId === undefined) {
|
|
216
|
+
datasetId = table.datasetIds.size;
|
|
217
|
+
table.datasetIds.set(copyOf(dataset), datasetId);
|
|
218
|
+
}
|
|
219
|
+
let dimensionId = table.dimensionIds.get(dimension);
|
|
220
|
+
if (dimensionId === undefined) {
|
|
221
|
+
dimensionId = table.dimensions.length;
|
|
222
|
+
table.dimensions.push(copyOf(dimension));
|
|
223
|
+
table.dimensionIds.set(table.dimensions[dimensionId], dimensionId);
|
|
224
|
+
}
|
|
225
|
+
const key = `${dimensionId}\t${code}`;
|
|
226
|
+
let valueId = table.valueIds.get(key);
|
|
227
|
+
if (valueId === undefined) {
|
|
228
|
+
valueId = table.valueCodes.length;
|
|
229
|
+
table.valueIds.set(copyOf(key), valueId);
|
|
230
|
+
table.valueCodes.push(copyOf(code));
|
|
231
|
+
table.valueDimensions.push(dimensionId);
|
|
232
|
+
}
|
|
233
|
+
table.rowDatasets.push(datasetId);
|
|
234
|
+
table.rowValues.push(valueId);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Reads the codelist dump as it streams in, keeping the first English name of each code the
|
|
239
|
+
* metabase lists, from the codelist named after its dimension in upper case.
|
|
240
|
+
*/
|
|
241
|
+
class CodelistReader {
|
|
242
|
+
code;
|
|
243
|
+
codelistDimensions;
|
|
244
|
+
dimensionId;
|
|
245
|
+
labelled = 0;
|
|
246
|
+
labels;
|
|
247
|
+
/** Whether the open code, or the codelist before its first code, has had its name. */
|
|
248
|
+
named = true;
|
|
249
|
+
pieces = new BoundaryPieces(CODE_CLOSE);
|
|
250
|
+
table;
|
|
251
|
+
constructor(table) {
|
|
252
|
+
this.table = table;
|
|
253
|
+
this.labels = new Array(table.valueCodes.length);
|
|
254
|
+
this.codelistDimensions = new Map(table.dimensions.map((dimension, id) => [dimension.toUpperCase(), id]));
|
|
255
|
+
}
|
|
256
|
+
read(chunk) {
|
|
257
|
+
const piece = this.pieces.cut(chunk);
|
|
258
|
+
if (piece !== undefined)
|
|
259
|
+
this.scan(piece);
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* The index, from the metabase table and the labels read. Throws when the dump stops before
|
|
263
|
+
* its root element closes, or labels none of the metabase's codes.
|
|
264
|
+
*/
|
|
265
|
+
finish() {
|
|
266
|
+
const rest = this.pieces.rest();
|
|
267
|
+
this.scan(rest);
|
|
268
|
+
if (!rest.includes(DOCUMENT_CLOSE)) {
|
|
269
|
+
throw new Error('The codelist dump ended before its root element closed');
|
|
270
|
+
}
|
|
271
|
+
if (this.labelled === 0)
|
|
272
|
+
throw new Error('The codelists labelled none of the metabase codes');
|
|
273
|
+
return assembleIndex(this.table, this.labels);
|
|
274
|
+
}
|
|
275
|
+
scan(piece) {
|
|
276
|
+
CODELIST_TOKEN.lastIndex = 0;
|
|
277
|
+
for (let token = CODELIST_TOKEN.exec(piece); token; token = CODELIST_TOKEN.exec(piece)) {
|
|
278
|
+
const [, codelistId, codeId, name] = token;
|
|
279
|
+
if (codelistId !== undefined) {
|
|
280
|
+
this.dimensionId = this.codelistDimensions.get(decodeXmlEntities(codelistId));
|
|
281
|
+
this.named = true;
|
|
282
|
+
}
|
|
283
|
+
else if (codeId !== undefined) {
|
|
284
|
+
this.code = decodeXmlEntities(codeId);
|
|
285
|
+
this.named = false;
|
|
286
|
+
}
|
|
287
|
+
else if (name !== undefined) {
|
|
288
|
+
// The codelist's own name precedes its first code; only a code's first name labels it.
|
|
289
|
+
if (this.named || this.dimensionId === undefined)
|
|
290
|
+
continue;
|
|
291
|
+
this.named = true;
|
|
292
|
+
const valueId = this.table.valueIds.get(`${this.dimensionId}\t${this.code}`);
|
|
293
|
+
if (valueId === undefined || this.labels[valueId] !== undefined)
|
|
294
|
+
continue;
|
|
295
|
+
this.labels[valueId] = copyOf(decodeXmlEntities(name).trim());
|
|
296
|
+
this.labelled += 1;
|
|
297
|
+
}
|
|
298
|
+
else {
|
|
299
|
+
this.named = true;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
/** The index from the metabase table and each value's label. */
|
|
305
|
+
function assembleIndex(table, valueLabels) {
|
|
306
|
+
const { datasetIds, dimensions, rowDatasets, rowValues, valueCodes, valueDimensions } = table;
|
|
307
|
+
const labelIds = new Map();
|
|
308
|
+
const labels = [];
|
|
309
|
+
const valueLabel = new Int32Array(valueCodes.length).fill(-1);
|
|
310
|
+
valueLabels.forEach((label, valueId) => {
|
|
311
|
+
if (label === undefined)
|
|
312
|
+
return;
|
|
313
|
+
let labelId = labelIds.get(label);
|
|
314
|
+
if (labelId === undefined) {
|
|
315
|
+
labelId = labels.length;
|
|
316
|
+
labels.push(label);
|
|
317
|
+
labelIds.set(label, labelId);
|
|
318
|
+
}
|
|
319
|
+
valueLabel[valueId] = labelId;
|
|
320
|
+
});
|
|
321
|
+
const folded = labels.map(foldSearchText);
|
|
322
|
+
const datasetStart = new Int32Array(datasetIds.size + 1);
|
|
323
|
+
for (const datasetId of rowDatasets)
|
|
324
|
+
datasetStart[datasetId + 1] = datasetStart[datasetId + 1] + 1;
|
|
325
|
+
for (let d = 0; d < datasetIds.size; d++) {
|
|
326
|
+
datasetStart[d + 1] = datasetStart[d + 1] + datasetStart[d];
|
|
327
|
+
}
|
|
328
|
+
const fill = datasetStart.slice(0, -1);
|
|
329
|
+
const datasetValues = new Int32Array(rowValues.length);
|
|
330
|
+
rowDatasets.forEach((datasetId, row) => {
|
|
331
|
+
datasetValues[fill[datasetId]++] = rowValues[row];
|
|
332
|
+
});
|
|
333
|
+
return new DimensionValueIndex({
|
|
334
|
+
codeStart: offsetsOf(valueCodes),
|
|
335
|
+
codeText: valueCodes.join(SEPARATOR),
|
|
336
|
+
datasetIds,
|
|
337
|
+
datasetStart,
|
|
338
|
+
datasetValues,
|
|
339
|
+
dimensions,
|
|
340
|
+
labelStart: offsetsOf(labels),
|
|
341
|
+
labelText: labels.join(SEPARATOR),
|
|
342
|
+
searchStart: offsetsOf(folded),
|
|
343
|
+
searchText: folded.join(SEPARATOR),
|
|
344
|
+
valueDimension: Int32Array.from(valueDimensions),
|
|
345
|
+
valueLabel,
|
|
346
|
+
}, Date.now());
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
349
|
+
* Build the index from the metabase (`dataset<TAB>dimension<TAB>code` lines) and the codelist
|
|
350
|
+
* dump (SDMX 2.1 structure XML), each read as a stream of decoded text. The metabase is read
|
|
351
|
+
* first, so only the labels of codes it lists are kept from the codelist named, in upper case,
|
|
352
|
+
* after each dimension. A code's first English name is its label.
|
|
353
|
+
*
|
|
354
|
+
* Throws on a metabase line that is not three tab-separated fields, a metabase that lists no
|
|
355
|
+
* dataset, a codelist dump that stops before its root element closes, or codelists that label
|
|
356
|
+
* none of the metabase's codes: each is a broken download, not an empty catalogue.
|
|
357
|
+
*/
|
|
358
|
+
export async function buildDimensionValueIndex(metabase, codelists) {
|
|
359
|
+
const metabaseReader = new MetabaseReader();
|
|
360
|
+
for await (const chunk of metabase)
|
|
361
|
+
metabaseReader.read(chunk);
|
|
362
|
+
const codelistReader = new CodelistReader(metabaseReader.finish());
|
|
363
|
+
for await (const chunk of codelists)
|
|
364
|
+
codelistReader.read(chunk);
|
|
365
|
+
return codelistReader.finish();
|
|
366
|
+
}
|
|
367
|
+
/**
|
|
368
|
+
* The decoded text of a gzip file at `url`, chunk by chunk. The request starts when the
|
|
369
|
+
* first chunk is asked for; `timeoutMs` bounds the whole transfer.
|
|
370
|
+
*/
|
|
371
|
+
async function* gzipText(url, timeoutMs, context) {
|
|
372
|
+
const response = await fetchWithTimeout(url, timeoutMs, context);
|
|
373
|
+
if (!response.body)
|
|
374
|
+
throw new Error(`${new URL(url).pathname} answered with no body`);
|
|
375
|
+
/**
|
|
376
|
+
* `DecompressionStream`'s writable side is typed `BufferSource`, wider than the
|
|
377
|
+
* `Uint8Array` chunks the body carries, so the transform is narrowed to the types in play.
|
|
378
|
+
*/
|
|
379
|
+
const reader = response.body
|
|
380
|
+
.pipeThrough(new DecompressionStream('gzip'))
|
|
381
|
+
.getReader();
|
|
382
|
+
const decoder = new TextDecoder('utf-8');
|
|
383
|
+
try {
|
|
384
|
+
while (true) {
|
|
385
|
+
const { done, value } = await reader.read();
|
|
386
|
+
if (done)
|
|
387
|
+
break;
|
|
388
|
+
const text = decoder.decode(value, { stream: true });
|
|
389
|
+
if (text)
|
|
390
|
+
yield text;
|
|
391
|
+
}
|
|
392
|
+
const tail = decoder.decode();
|
|
393
|
+
if (tail)
|
|
394
|
+
yield tail;
|
|
395
|
+
}
|
|
396
|
+
finally {
|
|
397
|
+
// Ends the transfer when parsing stops early; a no-op once the stream is done.
|
|
398
|
+
await reader.cancel().catch(() => undefined);
|
|
399
|
+
reader.releaseLock();
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
/**
|
|
403
|
+
* Download and build the index: the metabase first, then the codelist dump, each within
|
|
404
|
+
* `EUROSTAT_BULK_TIMEOUT_MS`. One attempt; the caller schedules retries.
|
|
405
|
+
*/
|
|
406
|
+
export function fetchDimensionValueIndex(context) {
|
|
407
|
+
const { baseUrl, bulkTimeoutMs } = getServerConfig();
|
|
408
|
+
return buildDimensionValueIndex(gzipText(`${baseUrl}/catalogue/metabase.txt.gz`, bulkTimeoutMs, context), gzipText(`${baseUrl}/sdmx/2.1/codelist/ESTAT/all/latest?compressed=true`, bulkTimeoutMs, context));
|
|
409
|
+
}
|
|
410
|
+
//# sourceMappingURL=dimension-value-index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dimension-value-index.js","sourceRoot":"","sources":["../../../src/services/eurostat-catalogue/dimension-value-index.ts"],"names":[],"mappings":"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"}
|