@netwerk-digitaal-erfgoed/network-of-terms-query 6.2.9 → 6.2.11
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/CHANGELOG.md +1 -1
- package/dist/query.d.ts.map +1 -1
- package/dist/query.js +55 -4
- package/dist/test-utils.js +1 -1
- package/dist/tsconfig.lib.tsbuildinfo +1 -1
- package/package.json +8 -8
- package/src/query.ts +62 -7
- package/src/test-utils.ts +1 -1
- package/test/query.test.ts +3 -2
- package/vite.config.ts +5 -5
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@netwerk-digitaal-erfgoed/network-of-terms-query",
|
|
3
|
-
"version": "6.2.
|
|
3
|
+
"version": "6.2.11",
|
|
4
4
|
"description": "Engine for querying sources in the Network of Terms",
|
|
5
5
|
"keywords": [],
|
|
6
6
|
"homepage": "https://github.com/netwerk-digitaal-erfgoed/network-of-terms#readme",
|
|
@@ -30,19 +30,19 @@
|
|
|
30
30
|
"main": "./dist/index.js",
|
|
31
31
|
"types": "./dist/index.d.ts",
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@comunica/query-sparql": "^
|
|
34
|
-
"@comunica/types": "^
|
|
35
|
-
"@comunica/utils-bindings-factory": "^
|
|
33
|
+
"@comunica/query-sparql": "^5.1.2",
|
|
34
|
+
"@comunica/types": "^5.1.0",
|
|
35
|
+
"@comunica/utils-bindings-factory": "^5.1.0",
|
|
36
36
|
"@hapi/hoek": "^11.0.7",
|
|
37
37
|
"@opentelemetry/api": "^1.9.0",
|
|
38
|
-
"@opentelemetry/exporter-metrics-otlp-proto": "0.
|
|
38
|
+
"@opentelemetry/exporter-metrics-otlp-proto": "0.208.0",
|
|
39
39
|
"@opentelemetry/resources": "2.2.0",
|
|
40
40
|
"@opentelemetry/sdk-metrics": "2.2.0",
|
|
41
|
-
"@opentelemetry/semantic-conventions": "1.
|
|
41
|
+
"@opentelemetry/semantic-conventions": "1.38.0",
|
|
42
42
|
"@rdfjs/types": "^2.0.1",
|
|
43
43
|
"env-schema": "^6.0.1",
|
|
44
44
|
"jest-dev-server": "11.0.0",
|
|
45
|
-
"joi": "^
|
|
45
|
+
"joi": "^18.0.2",
|
|
46
46
|
"nock": "^14.0.10",
|
|
47
47
|
"pino": "^9.7.0",
|
|
48
48
|
"pretty-ms": "^9.2.0",
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
"tslib": "^2.3.0"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
|
-
"@comunica/query-sparql-file": "^
|
|
54
|
+
"@comunica/query-sparql-file": "^5.1.2",
|
|
55
55
|
"asynciterator": "^3.9.0"
|
|
56
56
|
}
|
|
57
57
|
}
|
package/src/query.ts
CHANGED
|
@@ -4,7 +4,6 @@ import { LoggerPino } from './helpers/logger-pino.js';
|
|
|
4
4
|
import Pino from 'pino';
|
|
5
5
|
import PrettyMilliseconds from 'pretty-ms';
|
|
6
6
|
import * as RDF from '@rdfjs/types';
|
|
7
|
-
import { Bindings } from '@rdfjs/types';
|
|
8
7
|
import { Term, TermsTransformer } from './terms.js';
|
|
9
8
|
import { QueryMode, queryVariants } from './search/query-mode.js';
|
|
10
9
|
import { Dataset, Distribution, IRI } from './catalog.js';
|
|
@@ -14,6 +13,50 @@ import { DataFactory } from 'rdf-data-factory';
|
|
|
14
13
|
import { sourceQueriesHistogram } from './instrumentation.js';
|
|
15
14
|
import { config } from './config.js';
|
|
16
15
|
|
|
16
|
+
/**
|
|
17
|
+
* Check if a query requires string substitution instead of initialBindings.
|
|
18
|
+
* Workaround for Comunica v5 traqula bug that crashes with:
|
|
19
|
+
* - SERVICE clauses
|
|
20
|
+
* - VALUES combination
|
|
21
|
+
*/
|
|
22
|
+
function requiresStringSubstitution(query: string): boolean {
|
|
23
|
+
const hasService = /\bSERVICE\b/i.test(query);
|
|
24
|
+
const hasValues = /\bVALUES\b/i.test(query);
|
|
25
|
+
return hasService || hasValues;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Substitute bindings directly into a SPARQL query string.
|
|
30
|
+
* This is a workaround for Comunica v5's initialBindings bug with SERVICE clauses.
|
|
31
|
+
*/
|
|
32
|
+
function substituteBindings(
|
|
33
|
+
query: string,
|
|
34
|
+
bindings: Record<string, RDF.Term>,
|
|
35
|
+
): string {
|
|
36
|
+
let result = query;
|
|
37
|
+
for (const [name, term] of Object.entries(bindings)) {
|
|
38
|
+
const pattern = new RegExp(`\\?${name}\\b`, 'g');
|
|
39
|
+
if (term.termType === 'NamedNode') {
|
|
40
|
+
result = result.replace(pattern, `<${term.value}>`);
|
|
41
|
+
} else if (term.termType === 'Literal') {
|
|
42
|
+
const literal = term as RDF.Literal;
|
|
43
|
+
const datatype = literal.datatype?.value;
|
|
44
|
+
if (
|
|
45
|
+
datatype &&
|
|
46
|
+
datatype !== 'http://www.w3.org/2001/XMLSchema#string' &&
|
|
47
|
+
datatype !== 'http://www.w3.org/1999/02/22-rdf-syntax-ns#langString'
|
|
48
|
+
) {
|
|
49
|
+
result = result.replace(pattern, `"${term.value}"^^<${datatype}>`);
|
|
50
|
+
} else if (literal.language) {
|
|
51
|
+
result = result.replace(pattern, `"${term.value}"@${literal.language}`);
|
|
52
|
+
} else {
|
|
53
|
+
result = result.replace(pattern, `"${term.value}"`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return result;
|
|
58
|
+
}
|
|
59
|
+
|
|
17
60
|
export type TermsResult = Terms | TimeoutError | ServerError;
|
|
18
61
|
|
|
19
62
|
export class TermsResponse {
|
|
@@ -141,10 +184,21 @@ export class QueryTermsService {
|
|
|
141
184
|
const logger = new LoggerPino({ logger: this.logger });
|
|
142
185
|
// Extract HTTP credentials if the distribution URL contains any.
|
|
143
186
|
const url = new URL(distribution.endpoint.toString());
|
|
144
|
-
|
|
145
|
-
|
|
187
|
+
|
|
188
|
+
// Workaround for https://github.com/comunica/comunica/issues/1655, so use
|
|
189
|
+
// string substitution instead of initialBindings for:
|
|
190
|
+
// - SERVICE clauses crash with initialBindings
|
|
191
|
+
// - VALUES crashes in some combinations
|
|
192
|
+
const useStringSubstitution = requiresStringSubstitution(query);
|
|
193
|
+
const finalQuery = useStringSubstitution
|
|
194
|
+
? substituteBindings(query, bindings)
|
|
195
|
+
: query;
|
|
196
|
+
|
|
197
|
+
this.logger.info(`Querying "${url}" with "${finalQuery}"...`);
|
|
198
|
+
const quadStream = await this.engine.queryQuads(finalQuery, {
|
|
146
199
|
log: logger,
|
|
147
|
-
httpAuth:
|
|
200
|
+
httpAuth:
|
|
201
|
+
url.username === '' ? undefined : url.username + ':' + url.password,
|
|
148
202
|
httpTimeout: timeoutMs,
|
|
149
203
|
noCache: true,
|
|
150
204
|
sources: [
|
|
@@ -153,9 +207,10 @@ export class QueryTermsService {
|
|
|
153
207
|
value: url.origin + url.pathname,
|
|
154
208
|
},
|
|
155
209
|
],
|
|
156
|
-
initialBindings
|
|
157
|
-
|
|
158
|
-
|
|
210
|
+
// Only pass initialBindings when NOT using string substitution
|
|
211
|
+
...(useStringSubstitution
|
|
212
|
+
? {}
|
|
213
|
+
: { initialBindings: bindingsFactory.fromRecord(bindings) }),
|
|
159
214
|
});
|
|
160
215
|
|
|
161
216
|
return new Promise((resolve) => {
|
package/src/test-utils.ts
CHANGED
|
@@ -187,7 +187,7 @@ export const testCatalog = (port: number) =>
|
|
|
187
187
|
new SparqlDistribution(
|
|
188
188
|
'https://data.beeldengeluid.nl/id/datadownload/0026',
|
|
189
189
|
'https://username:password@gtaa.apis.beeldengeluid.nl/sparql',
|
|
190
|
-
'CONSTRUCT { ?s ?p ?o } WHERE { ?s ?p ?o }',
|
|
190
|
+
'CONSTRUCT { ?s ?p ?o } WHERE { ?s skos:inScheme ?datasetUri ; ?p ?o }',
|
|
191
191
|
'CONSTRUCT { ?s ?p ?o } WHERE { ?s ?p ?o }',
|
|
192
192
|
),
|
|
193
193
|
],
|
package/test/query.test.ts
CHANGED
|
@@ -21,11 +21,12 @@ describe('Query', () => {
|
|
|
21
21
|
vi.clearAllMocks();
|
|
22
22
|
});
|
|
23
23
|
it('passes dataset IRI query parameter to Comunica', async () => {
|
|
24
|
+
// Use GTAA which doesn't have VALUES, so uses initialBindings (not string substitution)
|
|
24
25
|
const config = await query(
|
|
25
|
-
'https://data.
|
|
26
|
+
'https://data.beeldengeluid.nl/id/datadownload/0026',
|
|
26
27
|
);
|
|
27
28
|
expect(config.initialBindings.get('datasetUri')?.value).toEqual(
|
|
28
|
-
'
|
|
29
|
+
'http://data.beeldengeluid.nl/gtaa/Persoonsnamen',
|
|
29
30
|
);
|
|
30
31
|
});
|
|
31
32
|
|
package/vite.config.ts
CHANGED
|
@@ -16,11 +16,11 @@ export default defineConfig(() => ({
|
|
|
16
16
|
provider: 'v8' as const,
|
|
17
17
|
thresholds: {
|
|
18
18
|
autoUpdate: true,
|
|
19
|
-
lines:
|
|
20
|
-
functions: 40,
|
|
21
|
-
branches:
|
|
22
|
-
statements:
|
|
19
|
+
lines: 56.81,
|
|
20
|
+
functions: 40.25,
|
|
21
|
+
branches: 91.22,
|
|
22
|
+
statements: 56.81,
|
|
23
23
|
},
|
|
24
24
|
},
|
|
25
25
|
},
|
|
26
|
-
}));
|
|
26
|
+
}));
|