ochre-sdk 0.22.2 → 0.22.4
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/dist/index.mjs +422 -1032
- package/package.json +4 -2
package/dist/index.mjs
CHANGED
|
@@ -2158,7 +2158,7 @@ async function fetchItem(uuid, category, itemCategories, options) {
|
|
|
2158
2158
|
}
|
|
2159
2159
|
//#endregion
|
|
2160
2160
|
//#region src/utils/query.ts
|
|
2161
|
-
const CTS_INCLUDES_STOP_WORDS = [
|
|
2161
|
+
const CTS_INCLUDES_STOP_WORDS = new Set([
|
|
2162
2162
|
"and",
|
|
2163
2163
|
"at",
|
|
2164
2164
|
"in",
|
|
@@ -2166,601 +2166,415 @@ const CTS_INCLUDES_STOP_WORDS = [
|
|
|
2166
2166
|
"of",
|
|
2167
2167
|
"the",
|
|
2168
2168
|
"to"
|
|
2169
|
-
];
|
|
2170
|
-
const CTS_INCLUDES_STOP_WORDS_VAR = "$ctsIncludesStopWords";
|
|
2169
|
+
]);
|
|
2171
2170
|
const CTS_INCLUDES_TOKEN_SPLIT_REGEX = /\W+/u;
|
|
2171
|
+
const CONTENT_TARGET_CONTAINER_ELEMENTS = {
|
|
2172
|
+
title: "identification",
|
|
2173
|
+
description: "description",
|
|
2174
|
+
image: "image",
|
|
2175
|
+
periods: "period",
|
|
2176
|
+
bibliography: "bibliography"
|
|
2177
|
+
};
|
|
2172
2178
|
function tokenizeIncludesSearchValue(params) {
|
|
2173
2179
|
const { value, isCaseSensitive } = params;
|
|
2174
2180
|
const rawTerms = (isCaseSensitive ? value : value.toLowerCase()).split(CTS_INCLUDES_TOKEN_SPLIT_REGEX);
|
|
2175
2181
|
const terms = [];
|
|
2176
2182
|
for (const term of rawTerms) {
|
|
2177
2183
|
const normalizedTerm = term.toLowerCase();
|
|
2178
|
-
if (normalizedTerm !== "" && !CTS_INCLUDES_STOP_WORDS.
|
|
2184
|
+
if (normalizedTerm !== "" && !CTS_INCLUDES_STOP_WORDS.has(normalizedTerm)) terms.push(term);
|
|
2179
2185
|
}
|
|
2180
2186
|
return terms;
|
|
2181
2187
|
}
|
|
2182
|
-
function
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
}
|
|
2186
|
-
function buildSearchableContentNodesExpression(contentNodesExpression) {
|
|
2187
|
-
return `for $content in ${contentNodesExpression}
|
|
2188
|
-
return (
|
|
2189
|
-
$content//string[not(ancestor::string)],
|
|
2190
|
-
$content[not(.//string)]
|
|
2191
|
-
)`;
|
|
2192
|
-
}
|
|
2193
|
-
function buildCombinedSearchableContentNodesExpression(contentNodesExpressions) {
|
|
2194
|
-
const searchableExpressions = [];
|
|
2195
|
-
for (const contentNodesExpression of contentNodesExpressions) searchableExpressions.push(buildSearchableContentNodesExpression(contentNodesExpression));
|
|
2196
|
-
if (searchableExpressions.length === 0) return "()";
|
|
2197
|
-
if (searchableExpressions.length === 1) return searchableExpressions[0] ?? "()";
|
|
2198
|
-
return `(${searchableExpressions.join(", ")})`;
|
|
2199
|
-
}
|
|
2200
|
-
function buildAttributeValuesExpression(params) {
|
|
2201
|
-
const { nodeExpression, attributeName } = params;
|
|
2202
|
-
return `for $node in ${nodeExpression}[@${attributeName}]
|
|
2203
|
-
return string($node/@${attributeName})`;
|
|
2204
|
-
}
|
|
2205
|
-
function buildNotesContentNodesExpression(language) {
|
|
2206
|
-
return `notes/note/content[@xml:lang="${language}"]`;
|
|
2207
|
-
}
|
|
2208
|
-
function buildNotesTitleValuesExpression(language) {
|
|
2209
|
-
return buildAttributeValuesExpression({
|
|
2210
|
-
nodeExpression: buildNotesContentNodesExpression(language),
|
|
2211
|
-
attributeName: "title"
|
|
2212
|
-
});
|
|
2213
|
-
}
|
|
2214
|
-
function buildNotesValueExpressions(language) {
|
|
2215
|
-
return [buildFlattenedContentValuesExpression(buildNotesContentNodesExpression(language)), buildNotesTitleValuesExpression(language)];
|
|
2216
|
-
}
|
|
2217
|
-
function buildPropertyValueNodesExpression(params) {
|
|
2218
|
-
const predicates = [];
|
|
2219
|
-
if (params?.excludeInherited) predicates.push(`not(@inherited="true")`);
|
|
2220
|
-
for (const excludedDataType of params?.excludedDataTypes ?? []) predicates.push(`not(@dataType=${stringLiteral(excludedDataType)})`);
|
|
2221
|
-
if (predicates.length === 0) return "value";
|
|
2222
|
-
return `value[${predicates.join(" and ")}]`;
|
|
2223
|
-
}
|
|
2224
|
-
function buildPropertyValueContentNodesExpression(params) {
|
|
2225
|
-
const { language, excludeInherited, excludedDataTypes } = params;
|
|
2226
|
-
return `${buildPropertyValueNodesExpression({
|
|
2227
|
-
excludeInherited,
|
|
2228
|
-
excludedDataTypes
|
|
2229
|
-
})}/content[@xml:lang="${language}"]`;
|
|
2230
|
-
}
|
|
2231
|
-
function buildPropertyValueContentValuesExpression(params) {
|
|
2232
|
-
return buildFlattenedContentValuesExpression(buildPropertyValueContentNodesExpression(params));
|
|
2233
|
-
}
|
|
2234
|
-
function buildPropertyValueRawValuesExpression(params) {
|
|
2235
|
-
return `for $value in ${buildPropertyValueNodesExpression(params)}[@rawValue]
|
|
2236
|
-
return string($value/@rawValue)`;
|
|
2237
|
-
}
|
|
2238
|
-
function buildPropertyValueDirectTextValuesExpression(params) {
|
|
2239
|
-
return `for $value in ${buildPropertyValueNodesExpression(params)}
|
|
2240
|
-
let $candidate := string-join($value/text(), "")
|
|
2241
|
-
where normalize-space($candidate) != ""
|
|
2242
|
-
return $candidate`;
|
|
2243
|
-
}
|
|
2244
|
-
function buildPropertyScalarValueExpressions() {
|
|
2245
|
-
return [buildPropertyValueRawValuesExpression(), buildPropertyValueDirectTextValuesExpression()];
|
|
2246
|
-
}
|
|
2247
|
-
function buildAllPropertyValueExpressions(language) {
|
|
2248
|
-
const params = { excludedDataTypes: ["IDREF"] };
|
|
2249
|
-
return [
|
|
2250
|
-
buildPropertyValueRawValuesExpression(params),
|
|
2251
|
-
buildPropertyValueContentValuesExpression({
|
|
2252
|
-
language,
|
|
2253
|
-
...params
|
|
2254
|
-
}),
|
|
2255
|
-
buildPropertyValueDirectTextValuesExpression(params)
|
|
2256
|
-
];
|
|
2257
|
-
}
|
|
2258
|
-
/**
|
|
2259
|
-
* Build a string match predicate for an XQuery string.
|
|
2260
|
-
*/
|
|
2261
|
-
function buildRawStringMatchPredicate(params) {
|
|
2262
|
-
const { valueExpression, value, matchMode, isCaseSensitive } = params;
|
|
2263
|
-
const comparedValueLiteral = stringLiteral(isCaseSensitive ? value : value.toLowerCase());
|
|
2264
|
-
const candidateVar = "$candidate";
|
|
2265
|
-
const comparedCandidate = isCaseSensitive ? candidateVar : `lower-case(${candidateVar})`;
|
|
2266
|
-
if (matchMode === "includes") return `exists(
|
|
2267
|
-
for ${candidateVar} in (${valueExpression})
|
|
2268
|
-
where contains(${comparedCandidate}, ${comparedValueLiteral})
|
|
2269
|
-
return ${candidateVar}
|
|
2270
|
-
)`;
|
|
2271
|
-
return `exists(
|
|
2272
|
-
for ${candidateVar} in (${valueExpression})
|
|
2273
|
-
where ${comparedCandidate} = ${comparedValueLiteral}
|
|
2274
|
-
return ${candidateVar}
|
|
2275
|
-
)`;
|
|
2276
|
-
}
|
|
2277
|
-
/**
|
|
2278
|
-
* Build a combined raw string match predicate for multiple paths.
|
|
2279
|
-
*/
|
|
2280
|
-
function buildCombinedRawStringMatchPredicate(params) {
|
|
2281
|
-
const { valueExpressions, value, matchMode, isCaseSensitive } = params;
|
|
2282
|
-
const predicates = [];
|
|
2283
|
-
for (const valueExpression of valueExpressions) predicates.push(`(${buildRawStringMatchPredicate({
|
|
2284
|
-
valueExpression,
|
|
2285
|
-
value,
|
|
2286
|
-
matchMode,
|
|
2287
|
-
isCaseSensitive
|
|
2288
|
-
})})`);
|
|
2289
|
-
return buildOrPredicate(predicates);
|
|
2290
|
-
}
|
|
2291
|
-
/**
|
|
2292
|
-
* Build CTS word-query options for API v2 includes search.
|
|
2293
|
-
*/
|
|
2294
|
-
function buildCtsQueryOptionsExpression(params) {
|
|
2295
|
-
const { isCaseSensitive, isWildcarded } = params;
|
|
2296
|
-
const options = [
|
|
2188
|
+
function buildCtsMatchOptionsExpression(params) {
|
|
2189
|
+
const { isCaseSensitive } = params;
|
|
2190
|
+
return `(${[
|
|
2297
2191
|
isCaseSensitive ? "case-sensitive" : "case-insensitive",
|
|
2298
2192
|
"diacritic-insensitive",
|
|
2299
|
-
"punctuation-insensitive"
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
}
|
|
2304
|
-
function buildWildcardedTermExpression(termExpression) {
|
|
2305
|
-
return `fn:concat("*", ${termExpression}, "*")`;
|
|
2193
|
+
"punctuation-insensitive",
|
|
2194
|
+
"whitespace-insensitive",
|
|
2195
|
+
"unstemmed",
|
|
2196
|
+
"unwildcarded"
|
|
2197
|
+
].map((option) => stringLiteral(option)).join(", ")})`;
|
|
2306
2198
|
}
|
|
2307
|
-
/**
|
|
2308
|
-
* Build a CTS word-query expression for an XQuery term.
|
|
2309
|
-
*/
|
|
2310
2199
|
function buildCtsWordQueryExpression(params) {
|
|
2311
|
-
const {
|
|
2312
|
-
return `cts:word-query(${
|
|
2313
|
-
|
|
2314
|
-
|
|
2200
|
+
const { value, matchMode, isCaseSensitive } = params;
|
|
2201
|
+
return `cts:word-query(${stringLiteral(value)}, ${buildCtsMatchOptionsExpression({
|
|
2202
|
+
matchMode,
|
|
2203
|
+
isCaseSensitive
|
|
2315
2204
|
})})`;
|
|
2316
2205
|
}
|
|
2317
2206
|
function buildCtsElementWordQueryExpression(params) {
|
|
2318
|
-
const { elementName,
|
|
2319
|
-
return `cts:element-word-query(xs:QName("${elementName}"), ${
|
|
2320
|
-
|
|
2321
|
-
|
|
2207
|
+
const { elementName, value, matchMode, isCaseSensitive } = params;
|
|
2208
|
+
return `cts:element-word-query(xs:QName("${elementName}"), ${stringLiteral(value)}, ${buildCtsMatchOptionsExpression({
|
|
2209
|
+
matchMode,
|
|
2210
|
+
isCaseSensitive
|
|
2322
2211
|
})})`;
|
|
2323
2212
|
}
|
|
2324
2213
|
function buildCtsElementAttributeWordQueryExpression(params) {
|
|
2325
|
-
const { elementName, attributeName,
|
|
2326
|
-
return `cts:element-attribute-word-query(xs:QName("${elementName}"), xs:QName("${attributeName}"), ${
|
|
2327
|
-
|
|
2328
|
-
|
|
2214
|
+
const { elementName, attributeName, value, matchMode, isCaseSensitive } = params;
|
|
2215
|
+
return `cts:element-attribute-word-query(xs:QName("${elementName}"), xs:QName("${attributeName}"), ${stringLiteral(value)}, ${buildCtsMatchOptionsExpression({
|
|
2216
|
+
matchMode,
|
|
2217
|
+
isCaseSensitive
|
|
2329
2218
|
})})`;
|
|
2330
2219
|
}
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
const rawTermsVar = `$query${queryKey}RawTerms`;
|
|
2338
|
-
const termsVar = `$query${queryKey}Terms`;
|
|
2339
|
-
const tokenSourceExpression = isCaseSensitive ? searchStringVar : `fn:lower-case(${searchStringVar})`;
|
|
2340
|
-
return {
|
|
2341
|
-
declarations: [
|
|
2342
|
-
`let ${searchStringVar} := ${stringLiteral(value)}`,
|
|
2343
|
-
String.raw`let ${rawTermsVar} := fn:tokenize(${tokenSourceExpression}, "\W+")`,
|
|
2344
|
-
`let ${termsVar} :=
|
|
2345
|
-
for $term in ${rawTermsVar}
|
|
2346
|
-
let $normalizedTerm := fn:lower-case($term)
|
|
2347
|
-
where $normalizedTerm ne "" and not($normalizedTerm = ${CTS_INCLUDES_STOP_WORDS_VAR})
|
|
2348
|
-
return $term`
|
|
2349
|
-
],
|
|
2350
|
-
termsVar
|
|
2351
|
-
};
|
|
2220
|
+
function buildCtsElementValueQueryExpression(params) {
|
|
2221
|
+
const { elementName, value, isCaseSensitive } = params;
|
|
2222
|
+
return `cts:element-value-query(xs:QName("${elementName}"), ${stringLiteral(value)}, ${buildCtsMatchOptionsExpression({
|
|
2223
|
+
matchMode: "exact",
|
|
2224
|
+
isCaseSensitive
|
|
2225
|
+
})})`;
|
|
2352
2226
|
}
|
|
2353
|
-
function
|
|
2354
|
-
const {
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
isCaseSensitive,
|
|
2358
|
-
queryKey
|
|
2359
|
-
});
|
|
2360
|
-
const termQueriesVar = `$query${queryKey}TermQueries`;
|
|
2361
|
-
const candidateQueryVar = `$query${queryKey}CandidateQuery`;
|
|
2362
|
-
if (tokenizeIncludesSearchValue({
|
|
2363
|
-
value,
|
|
2227
|
+
function buildCtsElementAttributeValueQueryExpression(params) {
|
|
2228
|
+
const { elementName, attributeName, value, isCaseSensitive } = params;
|
|
2229
|
+
return `cts:element-attribute-value-query(xs:QName("${elementName}"), xs:QName("${attributeName}"), ${stringLiteral(value)}, ${buildCtsMatchOptionsExpression({
|
|
2230
|
+
matchMode: "exact",
|
|
2364
2231
|
isCaseSensitive
|
|
2365
|
-
})
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
}
|
|
2370
|
-
return {
|
|
2371
|
-
declarations: [
|
|
2372
|
-
...tokenizedSearchDeclarations.declarations,
|
|
2373
|
-
`let ${termQueriesVar} :=
|
|
2374
|
-
for $term in ${tokenizedSearchDeclarations.termsVar}
|
|
2375
|
-
return ${buildCandidateTermQuery("$term")}`,
|
|
2376
|
-
`let ${candidateQueryVar} :=
|
|
2377
|
-
if (count(${tokenizedSearchDeclarations.termsVar}) = 1)
|
|
2378
|
-
then ${termQueriesVar}[1]
|
|
2379
|
-
else if (count(${tokenizedSearchDeclarations.termsVar}) gt 1)
|
|
2380
|
-
then cts:and-query(${termQueriesVar})
|
|
2381
|
-
else ()`
|
|
2382
|
-
],
|
|
2383
|
-
termsVar: tokenizedSearchDeclarations.termsVar,
|
|
2384
|
-
candidateQueryVar
|
|
2385
|
-
};
|
|
2232
|
+
})})`;
|
|
2233
|
+
}
|
|
2234
|
+
function buildPlainElementAttributeValueQueryExpression(params) {
|
|
2235
|
+
const { elementName, attributeName, value } = params;
|
|
2236
|
+
return `cts:element-attribute-value-query(xs:QName("${elementName}"), xs:QName("${attributeName}"), ${stringLiteral(value)})`;
|
|
2386
2237
|
}
|
|
2387
|
-
function
|
|
2388
|
-
const {
|
|
2389
|
-
|
|
2238
|
+
function buildSearchableContentTextQueryExpression(params) {
|
|
2239
|
+
const { value, matchMode, isCaseSensitive } = params;
|
|
2240
|
+
if (matchMode === "exact") return buildCtsWordQueryExpression({
|
|
2390
2241
|
value,
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
buildCandidateTermQuery
|
|
2242
|
+
matchMode,
|
|
2243
|
+
isCaseSensitive
|
|
2394
2244
|
});
|
|
2395
|
-
|
|
2396
|
-
|
|
2245
|
+
return buildOrCtsQueryExpressionInternal([buildCtsElementWordQueryExpression({
|
|
2246
|
+
elementName: "string",
|
|
2397
2247
|
value,
|
|
2398
|
-
matchMode
|
|
2248
|
+
matchMode,
|
|
2399
2249
|
isCaseSensitive
|
|
2400
|
-
})
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
predicate: fallbackPredicate,
|
|
2404
|
-
candidateQueryVar: null
|
|
2405
|
-
};
|
|
2406
|
-
return {
|
|
2407
|
-
declarations: candidateQuery.declarations,
|
|
2408
|
-
predicate: `(every $term in ${candidateQuery.termsVar}
|
|
2409
|
-
satisfies some $searchNode in (${searchableNodesExpression})
|
|
2410
|
-
satisfies cts:contains(
|
|
2411
|
-
$searchNode,
|
|
2412
|
-
${buildCtsWordQueryExpression({
|
|
2413
|
-
termExpression: "$term",
|
|
2414
|
-
isCaseSensitive
|
|
2415
|
-
})}
|
|
2416
|
-
))`,
|
|
2417
|
-
candidateQueryVar: candidateQuery.candidateQueryVar
|
|
2418
|
-
};
|
|
2419
|
-
}
|
|
2420
|
-
/**
|
|
2421
|
-
* Build the raw search paths for item-level string search.
|
|
2422
|
-
*/
|
|
2423
|
-
function buildItemStringSearchPaths(language) {
|
|
2424
|
-
return [buildFlattenedContentValuesExpression(`identification/label/content[@xml:lang="${language}"]`), buildFlattenedContentValuesExpression(`properties//property/value[not(@inherited="true")]/content[@xml:lang="${language}"]`)];
|
|
2425
|
-
}
|
|
2426
|
-
function buildItemStringSearchableNodesExpression(language) {
|
|
2427
|
-
return buildCombinedSearchableContentNodesExpression([`identification/label/content[@xml:lang="${language}"]`, `properties//property/value[not(@inherited="true")]/content[@xml:lang="${language}"]`]);
|
|
2428
|
-
}
|
|
2429
|
-
/**
|
|
2430
|
-
* Build the identification branch for an item-level CTS string search.
|
|
2431
|
-
*/
|
|
2432
|
-
function buildItemStringIdentificationBranch(params) {
|
|
2433
|
-
const { termExpression, isCaseSensitive, language } = params;
|
|
2434
|
-
return `cts:element-query(xs:QName("identification"),
|
|
2435
|
-
cts:and-query((
|
|
2436
|
-
cts:element-attribute-value-query(xs:QName("content"), xs:QName("xml:lang"), ${stringLiteral(language)}),
|
|
2437
|
-
${buildCtsWordQueryExpression({
|
|
2438
|
-
termExpression,
|
|
2250
|
+
}), buildCtsWordQueryExpression({
|
|
2251
|
+
value,
|
|
2252
|
+
matchMode,
|
|
2439
2253
|
isCaseSensitive
|
|
2440
|
-
})
|
|
2441
|
-
))
|
|
2442
|
-
)`;
|
|
2254
|
+
})]);
|
|
2443
2255
|
}
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
const { termExpression, isCaseSensitive, language } = params;
|
|
2449
|
-
return `cts:element-query(xs:QName("properties"),
|
|
2450
|
-
cts:element-query(xs:QName("property"),
|
|
2451
|
-
cts:element-query(xs:QName("value"),
|
|
2452
|
-
cts:and-query((
|
|
2453
|
-
cts:not-query(cts:element-attribute-value-query(xs:QName("value"), xs:QName("inherited"), "true")),
|
|
2454
|
-
cts:element-query(xs:QName("content"),
|
|
2455
|
-
cts:and-query((
|
|
2456
|
-
cts:element-attribute-value-query(xs:QName("content"), xs:QName("xml:lang"), ${stringLiteral(language)}),
|
|
2457
|
-
${buildCtsWordQueryExpression({
|
|
2458
|
-
termExpression,
|
|
2459
|
-
isCaseSensitive
|
|
2460
|
-
})}
|
|
2461
|
-
))
|
|
2462
|
-
)
|
|
2463
|
-
))
|
|
2464
|
-
)
|
|
2465
|
-
)
|
|
2466
|
-
)`;
|
|
2256
|
+
function buildNestedElementQuery(elementNames, queryExpression) {
|
|
2257
|
+
let wrappedQueryExpression = queryExpression;
|
|
2258
|
+
for (const elementName of elementNames.toReversed()) wrappedQueryExpression = `cts:element-query(xs:QName("${elementName}"), ${wrappedQueryExpression})`;
|
|
2259
|
+
return wrappedQueryExpression;
|
|
2467
2260
|
}
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
*/
|
|
2471
|
-
function buildItemStringSearchClause(params) {
|
|
2472
|
-
const { query, version, queryKey, allowCtsPredicates } = params;
|
|
2473
|
-
const fallbackPredicate = buildCombinedRawStringMatchPredicate({
|
|
2474
|
-
valueExpressions: buildItemStringSearchPaths(query.language),
|
|
2475
|
-
value: query.value,
|
|
2476
|
-
matchMode: query.matchMode,
|
|
2477
|
-
isCaseSensitive: query.isCaseSensitive
|
|
2478
|
-
});
|
|
2479
|
-
if (!allowCtsPredicates || query.matchMode !== "includes" || version !== 2) return {
|
|
2480
|
-
declarations: [],
|
|
2481
|
-
predicate: fallbackPredicate,
|
|
2482
|
-
candidateQueryVar: null
|
|
2483
|
-
};
|
|
2484
|
-
return buildCtsIncludesPredicate({
|
|
2485
|
-
searchableNodesExpression: buildItemStringSearchableNodesExpression(query.language),
|
|
2486
|
-
fallbackValueExpression: `(${buildItemStringSearchPaths(query.language).join(", ")})`,
|
|
2487
|
-
value: query.value,
|
|
2488
|
-
isCaseSensitive: query.isCaseSensitive,
|
|
2489
|
-
queryKey,
|
|
2490
|
-
buildCandidateTermQuery: (termExpression) => `cts:or-query((
|
|
2491
|
-
${buildItemStringIdentificationBranch({
|
|
2492
|
-
termExpression,
|
|
2493
|
-
isCaseSensitive: query.isCaseSensitive,
|
|
2494
|
-
language: query.language
|
|
2495
|
-
})},
|
|
2496
|
-
${buildItemStringPropertyValueBranch({
|
|
2497
|
-
termExpression,
|
|
2498
|
-
isCaseSensitive: query.isCaseSensitive,
|
|
2499
|
-
language: query.language
|
|
2500
|
-
})}
|
|
2501
|
-
))`
|
|
2502
|
-
});
|
|
2261
|
+
function buildNotCtsQueryExpression(queryExpression) {
|
|
2262
|
+
return `cts:not-query(${queryExpression})`;
|
|
2503
2263
|
}
|
|
2504
|
-
function
|
|
2505
|
-
if (
|
|
2506
|
-
if (
|
|
2507
|
-
return `(${
|
|
2264
|
+
function buildAndCtsQueryExpressionInternal(queryExpressions) {
|
|
2265
|
+
if (queryExpressions.length === 0) return "cts:true-query()";
|
|
2266
|
+
if (queryExpressions.length === 1) return queryExpressions[0] ?? "cts:true-query()";
|
|
2267
|
+
return `cts:and-query((${queryExpressions.join(", ")}))`;
|
|
2508
2268
|
}
|
|
2509
|
-
function
|
|
2510
|
-
if (
|
|
2511
|
-
if (
|
|
2512
|
-
return `(${
|
|
2269
|
+
function buildOrCtsQueryExpressionInternal(queryExpressions) {
|
|
2270
|
+
if (queryExpressions.length === 0) return "cts:false-query()";
|
|
2271
|
+
if (queryExpressions.length === 1) return queryExpressions[0] ?? "cts:false-query()";
|
|
2272
|
+
return `cts:or-query((${queryExpressions.join(", ")}))`;
|
|
2513
2273
|
}
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
function buildDateRangePredicate(params) {
|
|
2518
|
-
const { from, to } = params;
|
|
2519
|
-
const conditions = [];
|
|
2520
|
-
if (from != null) conditions.push(`(value/@rawValue ge ${stringLiteral(from)})`);
|
|
2521
|
-
if (to != null) conditions.push(`(value/@rawValue le ${stringLiteral(to)})`);
|
|
2522
|
-
return buildAndPredicate(conditions);
|
|
2274
|
+
function buildAndCtsQueryExpression(queryExpressions) {
|
|
2275
|
+
if (queryExpressions.length === 0) return null;
|
|
2276
|
+
return buildAndCtsQueryExpressionInternal(queryExpressions);
|
|
2523
2277
|
}
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2278
|
+
function buildContentLanguageQuery(language) {
|
|
2279
|
+
return buildPlainElementAttributeValueQueryExpression({
|
|
2280
|
+
elementName: "content",
|
|
2281
|
+
attributeName: "xml:lang",
|
|
2282
|
+
value: language
|
|
2283
|
+
});
|
|
2529
2284
|
}
|
|
2530
|
-
function
|
|
2531
|
-
|
|
2532
|
-
|
|
2285
|
+
function buildPropertyLabelQuery(propertyVariable) {
|
|
2286
|
+
return buildPlainElementAttributeValueQueryExpression({
|
|
2287
|
+
elementName: "label",
|
|
2288
|
+
attributeName: "uuid",
|
|
2289
|
+
value: propertyVariable
|
|
2290
|
+
});
|
|
2533
2291
|
}
|
|
2534
|
-
function
|
|
2535
|
-
return
|
|
2292
|
+
function buildValueNotInheritedQuery() {
|
|
2293
|
+
return buildNotCtsQueryExpression(buildPlainElementAttributeValueQueryExpression({
|
|
2294
|
+
elementName: "value",
|
|
2295
|
+
attributeName: "inherited",
|
|
2296
|
+
value: "true"
|
|
2297
|
+
}));
|
|
2536
2298
|
}
|
|
2537
|
-
function
|
|
2538
|
-
return
|
|
2299
|
+
function buildValueNotIdRefQuery() {
|
|
2300
|
+
return buildNotCtsQueryExpression(buildPlainElementAttributeValueQueryExpression({
|
|
2301
|
+
elementName: "value",
|
|
2302
|
+
attributeName: "dataType",
|
|
2303
|
+
value: "IDREF"
|
|
2304
|
+
}));
|
|
2539
2305
|
}
|
|
2540
|
-
function
|
|
2541
|
-
|
|
2306
|
+
function buildValueContentInnerQuery(params) {
|
|
2307
|
+
const { language, value, matchMode, isCaseSensitive } = params;
|
|
2308
|
+
return buildNestedElementQuery(["content"], buildAndCtsQueryExpressionInternal([buildContentLanguageQuery(language), buildSearchableContentTextQueryExpression({
|
|
2309
|
+
value,
|
|
2310
|
+
matchMode,
|
|
2311
|
+
isCaseSensitive
|
|
2312
|
+
})]));
|
|
2542
2313
|
}
|
|
2543
|
-
function
|
|
2544
|
-
const {
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
${buildCtsWordQueryExpression({
|
|
2549
|
-
termExpression,
|
|
2314
|
+
function buildValueDirectTextInnerQuery(params) {
|
|
2315
|
+
const { value, matchMode, isCaseSensitive } = params;
|
|
2316
|
+
const directTextQuery = matchMode === "exact" ? buildCtsElementValueQueryExpression({
|
|
2317
|
+
elementName: "value",
|
|
2318
|
+
value,
|
|
2550
2319
|
isCaseSensitive
|
|
2551
|
-
})
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
function buildPropertyStringCandidateBranch(params) {
|
|
2556
|
-
const { termExpression, isCaseSensitive, language } = params;
|
|
2557
|
-
return `cts:element-query(xs:QName("properties"),
|
|
2558
|
-
cts:element-query(xs:QName("property"),
|
|
2559
|
-
cts:element-query(xs:QName("value"),
|
|
2560
|
-
cts:and-query((
|
|
2561
|
-
cts:not-query(cts:element-attribute-value-query(xs:QName("value"), xs:QName("inherited"), "true")),
|
|
2562
|
-
cts:element-query(xs:QName("content"),
|
|
2563
|
-
cts:and-query((
|
|
2564
|
-
cts:element-attribute-value-query(xs:QName("content"), xs:QName("xml:lang"), ${stringLiteral(language)}),
|
|
2565
|
-
${buildCtsWordQueryExpression({
|
|
2566
|
-
termExpression,
|
|
2320
|
+
}) : buildCtsElementWordQueryExpression({
|
|
2321
|
+
elementName: "value",
|
|
2322
|
+
value,
|
|
2323
|
+
matchMode,
|
|
2567
2324
|
isCaseSensitive
|
|
2568
|
-
})
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
)`;
|
|
2575
|
-
}
|
|
2576
|
-
function buildPropertyScalarCandidateBranch(params) {
|
|
2577
|
-
const { termExpression, isCaseSensitive } = params;
|
|
2578
|
-
return `cts:element-query(xs:QName("properties"),
|
|
2579
|
-
cts:element-query(xs:QName("property"),
|
|
2580
|
-
cts:or-query((
|
|
2581
|
-
${buildCtsElementAttributeWordQueryExpression({
|
|
2325
|
+
});
|
|
2326
|
+
return buildAndCtsQueryExpressionInternal([buildNotCtsQueryExpression(buildNestedElementQuery(["content"], "cts:true-query()")), directTextQuery]);
|
|
2327
|
+
}
|
|
2328
|
+
function buildValueRawValueInnerQuery(params) {
|
|
2329
|
+
const { value, matchMode, isCaseSensitive } = params;
|
|
2330
|
+
if (matchMode === "exact") return buildCtsElementAttributeValueQueryExpression({
|
|
2582
2331
|
elementName: "value",
|
|
2583
2332
|
attributeName: "rawValue",
|
|
2584
|
-
|
|
2585
|
-
isCaseSensitive
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
${buildCtsElementWordQueryExpression({
|
|
2333
|
+
value,
|
|
2334
|
+
isCaseSensitive
|
|
2335
|
+
});
|
|
2336
|
+
return buildCtsElementAttributeWordQueryExpression({
|
|
2589
2337
|
elementName: "value",
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
}
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
cts:and-query((
|
|
2604
|
-
cts:element-attribute-value-query(xs:QName("content"), xs:QName("xml:lang"), ${stringLiteral(language)}),
|
|
2605
|
-
cts:or-query((
|
|
2606
|
-
${buildCtsElementAttributeWordQueryExpression({
|
|
2338
|
+
attributeName: "rawValue",
|
|
2339
|
+
value,
|
|
2340
|
+
matchMode,
|
|
2341
|
+
isCaseSensitive
|
|
2342
|
+
});
|
|
2343
|
+
}
|
|
2344
|
+
function buildNotesQueryExpression(params) {
|
|
2345
|
+
const { value, matchMode, isCaseSensitive, language } = params;
|
|
2346
|
+
return buildNestedElementQuery([
|
|
2347
|
+
"notes",
|
|
2348
|
+
"note",
|
|
2349
|
+
"content"
|
|
2350
|
+
], buildAndCtsQueryExpressionInternal([buildContentLanguageQuery(language), buildOrCtsQueryExpressionInternal([matchMode === "exact" ? buildCtsElementAttributeValueQueryExpression({
|
|
2607
2351
|
elementName: "content",
|
|
2608
2352
|
attributeName: "title",
|
|
2609
|
-
|
|
2353
|
+
value,
|
|
2610
2354
|
isCaseSensitive
|
|
2611
|
-
})
|
|
2612
|
-
|
|
2613
|
-
|
|
2355
|
+
}) : buildCtsElementAttributeWordQueryExpression({
|
|
2356
|
+
elementName: "content",
|
|
2357
|
+
attributeName: "title",
|
|
2358
|
+
value,
|
|
2359
|
+
matchMode,
|
|
2614
2360
|
isCaseSensitive
|
|
2615
|
-
})
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
)`;
|
|
2361
|
+
}), buildSearchableContentTextQueryExpression({
|
|
2362
|
+
value,
|
|
2363
|
+
matchMode,
|
|
2364
|
+
isCaseSensitive
|
|
2365
|
+
})])]));
|
|
2621
2366
|
}
|
|
2622
|
-
function
|
|
2367
|
+
function buildContentTargetQueryExpression(params) {
|
|
2368
|
+
const { target, value, matchMode, isCaseSensitive, language } = params;
|
|
2369
|
+
const containerElement = CONTENT_TARGET_CONTAINER_ELEMENTS[target];
|
|
2370
|
+
return buildNestedElementQuery([containerElement], buildAndCtsQueryExpressionInternal([buildContentLanguageQuery(language), buildSearchableContentTextQueryExpression({
|
|
2371
|
+
value,
|
|
2372
|
+
matchMode,
|
|
2373
|
+
isCaseSensitive
|
|
2374
|
+
})]));
|
|
2375
|
+
}
|
|
2376
|
+
function buildPropertyQueryExpression(params) {
|
|
2377
|
+
const { propertyVariable, queryExpression } = params;
|
|
2378
|
+
const propertyQueryExpressions = [queryExpression];
|
|
2379
|
+
if (propertyVariable != null) propertyQueryExpressions.unshift(buildPropertyLabelQuery(propertyVariable));
|
|
2380
|
+
return buildNestedElementQuery(["properties", "property"], buildAndCtsQueryExpressionInternal(propertyQueryExpressions));
|
|
2381
|
+
}
|
|
2382
|
+
function buildPropertyPresenceQueryExpression(params) {
|
|
2383
|
+
return buildPropertyQueryExpression({
|
|
2384
|
+
propertyVariable: params.propertyVariable,
|
|
2385
|
+
queryExpression: "cts:true-query()"
|
|
2386
|
+
});
|
|
2387
|
+
}
|
|
2388
|
+
function buildPropertyStringQueryExpression(params) {
|
|
2389
|
+
const { propertyVariable, value, matchMode, isCaseSensitive, language } = params;
|
|
2390
|
+
return buildPropertyQueryExpression({
|
|
2391
|
+
propertyVariable,
|
|
2392
|
+
queryExpression: buildNestedElementQuery(["value"], buildAndCtsQueryExpressionInternal([buildValueNotInheritedQuery(), buildValueContentInnerQuery({
|
|
2393
|
+
language,
|
|
2394
|
+
value,
|
|
2395
|
+
matchMode,
|
|
2396
|
+
isCaseSensitive
|
|
2397
|
+
})]))
|
|
2398
|
+
});
|
|
2399
|
+
}
|
|
2400
|
+
function buildPropertyScalarQueryExpression(params) {
|
|
2401
|
+
const { propertyVariable, value, matchMode, isCaseSensitive } = params;
|
|
2402
|
+
return buildPropertyQueryExpression({
|
|
2403
|
+
propertyVariable,
|
|
2404
|
+
queryExpression: buildNestedElementQuery(["value"], buildOrCtsQueryExpressionInternal([buildValueRawValueInnerQuery({
|
|
2405
|
+
value,
|
|
2406
|
+
matchMode,
|
|
2407
|
+
isCaseSensitive
|
|
2408
|
+
}), buildValueDirectTextInnerQuery({
|
|
2409
|
+
value,
|
|
2410
|
+
matchMode,
|
|
2411
|
+
isCaseSensitive
|
|
2412
|
+
})]))
|
|
2413
|
+
});
|
|
2414
|
+
}
|
|
2415
|
+
function buildPropertyAllQueryExpression(params) {
|
|
2416
|
+
const { query, value, matchMode } = params;
|
|
2417
|
+
return buildPropertyQueryExpression({
|
|
2418
|
+
propertyVariable: query.propertyVariable,
|
|
2419
|
+
queryExpression: buildNestedElementQuery(["value"], buildAndCtsQueryExpressionInternal([buildValueNotIdRefQuery(), buildCtsWordQueryExpression({
|
|
2420
|
+
value,
|
|
2421
|
+
matchMode,
|
|
2422
|
+
isCaseSensitive: query.isCaseSensitive
|
|
2423
|
+
})]))
|
|
2424
|
+
});
|
|
2425
|
+
}
|
|
2426
|
+
function buildPropertyIdRefQueryExpression(params) {
|
|
2427
|
+
const { propertyVariable, value } = params;
|
|
2428
|
+
return buildPropertyQueryExpression({
|
|
2429
|
+
propertyVariable,
|
|
2430
|
+
queryExpression: buildNestedElementQuery(["value"], buildPlainElementAttributeValueQueryExpression({
|
|
2431
|
+
elementName: "value",
|
|
2432
|
+
attributeName: "uuid",
|
|
2433
|
+
value
|
|
2434
|
+
}))
|
|
2435
|
+
});
|
|
2436
|
+
}
|
|
2437
|
+
function buildPropertyDateRangeQueryExpression(query) {
|
|
2438
|
+
const rangeQueryExpressions = [];
|
|
2439
|
+
if (query.from != null) rangeQueryExpressions.push(`cts:element-attribute-range-query(xs:QName("value"), xs:QName("rawValue"), ">=", ${stringLiteral(query.from)})`);
|
|
2440
|
+
if (query.to != null) rangeQueryExpressions.push(`cts:element-attribute-range-query(xs:QName("value"), xs:QName("rawValue"), "<=", ${stringLiteral(query.to)})`);
|
|
2441
|
+
return buildPropertyQueryExpression({
|
|
2442
|
+
propertyVariable: query.propertyVariable,
|
|
2443
|
+
queryExpression: buildNestedElementQuery(["value"], buildAndCtsQueryExpressionInternal(rangeQueryExpressions))
|
|
2444
|
+
});
|
|
2445
|
+
}
|
|
2446
|
+
function buildItemStringQueryExpression(params) {
|
|
2447
|
+
const { value, matchMode, isCaseSensitive, language } = params;
|
|
2448
|
+
return buildOrCtsQueryExpressionInternal([buildContentTargetQueryExpression({
|
|
2449
|
+
target: "title",
|
|
2450
|
+
value,
|
|
2451
|
+
matchMode,
|
|
2452
|
+
isCaseSensitive,
|
|
2453
|
+
language
|
|
2454
|
+
}), buildPropertyStringQueryExpression({
|
|
2455
|
+
value,
|
|
2456
|
+
matchMode,
|
|
2457
|
+
isCaseSensitive,
|
|
2458
|
+
language
|
|
2459
|
+
})]);
|
|
2460
|
+
}
|
|
2461
|
+
function getLeafSearchValue(query) {
|
|
2623
2462
|
switch (query.target) {
|
|
2624
2463
|
case "string":
|
|
2625
2464
|
case "title":
|
|
2626
2465
|
case "description":
|
|
2627
2466
|
case "image":
|
|
2628
|
-
case "notes":
|
|
2629
2467
|
case "periods":
|
|
2630
|
-
case "bibliography":
|
|
2631
|
-
case "
|
|
2632
|
-
|
|
2633
|
-
return query.value;
|
|
2468
|
+
case "bibliography":
|
|
2469
|
+
case "notes": return query.value;
|
|
2470
|
+
case "property": return "value" in query && query.value != null ? query.value : null;
|
|
2634
2471
|
}
|
|
2635
2472
|
}
|
|
2636
|
-
function
|
|
2637
|
-
const { query, value,
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
return queryValue != null && queryValue === value && query.isCaseSensitive === isCaseSensitive && query.language === language;
|
|
2641
|
-
}
|
|
2642
|
-
function buildContentTargetIncludesGroupMember(params) {
|
|
2643
|
-
const { contentNodesExpression, value, isCaseSensitive, language, containerElementName } = params;
|
|
2644
|
-
return {
|
|
2645
|
-
rawPredicate: buildRawStringMatchPredicate({
|
|
2646
|
-
valueExpression: buildFlattenedContentValuesExpression(contentNodesExpression),
|
|
2473
|
+
function buildLeafValueQueryExpression(params) {
|
|
2474
|
+
const { query, value, matchMode } = params;
|
|
2475
|
+
switch (query.target) {
|
|
2476
|
+
case "string": return buildItemStringQueryExpression({
|
|
2647
2477
|
value,
|
|
2648
|
-
matchMode
|
|
2649
|
-
isCaseSensitive
|
|
2650
|
-
}),
|
|
2651
|
-
buildCandidateTermQuery: (termExpression) => buildContentTargetCandidateBranch({
|
|
2652
|
-
containerElementName,
|
|
2653
|
-
termExpression,
|
|
2654
|
-
isCaseSensitive,
|
|
2655
|
-
language
|
|
2656
|
-
})
|
|
2657
|
-
};
|
|
2658
|
-
}
|
|
2659
|
-
function buildItemStringIncludesGroupMember(query) {
|
|
2660
|
-
return {
|
|
2661
|
-
rawPredicate: buildCombinedRawStringMatchPredicate({
|
|
2662
|
-
valueExpressions: buildItemStringSearchPaths(query.language),
|
|
2663
|
-
value: query.value,
|
|
2664
|
-
matchMode: "includes",
|
|
2665
|
-
isCaseSensitive: query.isCaseSensitive
|
|
2666
|
-
}),
|
|
2667
|
-
buildCandidateTermQuery: (termExpression) => `cts:or-query((
|
|
2668
|
-
${buildItemStringIdentificationBranch({
|
|
2669
|
-
termExpression,
|
|
2478
|
+
matchMode,
|
|
2670
2479
|
isCaseSensitive: query.isCaseSensitive,
|
|
2671
2480
|
language: query.language
|
|
2672
|
-
})
|
|
2673
|
-
|
|
2674
|
-
|
|
2481
|
+
});
|
|
2482
|
+
case "notes": return buildNotesQueryExpression({
|
|
2483
|
+
value,
|
|
2484
|
+
matchMode,
|
|
2675
2485
|
isCaseSensitive: query.isCaseSensitive,
|
|
2676
2486
|
language: query.language
|
|
2677
|
-
})
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
excludeInherited: true
|
|
2687
|
-
});
|
|
2688
|
-
if (propertyVariable != null) predicateParts.push(buildPropertyLabelPredicate(propertyVariable));
|
|
2689
|
-
return {
|
|
2690
|
-
rawPredicate: buildPropertyPredicateExpression([...predicateParts, buildRawStringMatchPredicate({
|
|
2691
|
-
valueExpression,
|
|
2692
|
-
value: query.value,
|
|
2693
|
-
matchMode: "includes",
|
|
2694
|
-
isCaseSensitive: query.isCaseSensitive
|
|
2695
|
-
})]),
|
|
2696
|
-
buildCandidateTermQuery: (termExpression) => buildPropertyStringCandidateBranch({
|
|
2697
|
-
termExpression,
|
|
2487
|
+
});
|
|
2488
|
+
case "title":
|
|
2489
|
+
case "description":
|
|
2490
|
+
case "image":
|
|
2491
|
+
case "periods":
|
|
2492
|
+
case "bibliography": return buildContentTargetQueryExpression({
|
|
2493
|
+
target: query.target,
|
|
2494
|
+
value,
|
|
2495
|
+
matchMode,
|
|
2698
2496
|
isCaseSensitive: query.isCaseSensitive,
|
|
2699
2497
|
language: query.language
|
|
2700
|
-
})
|
|
2701
|
-
|
|
2498
|
+
});
|
|
2499
|
+
case "property": switch (query.dataType) {
|
|
2500
|
+
case "all": return buildPropertyAllQueryExpression({
|
|
2501
|
+
query,
|
|
2502
|
+
value,
|
|
2503
|
+
matchMode
|
|
2504
|
+
});
|
|
2505
|
+
case "IDREF": return buildPropertyIdRefQueryExpression({
|
|
2506
|
+
propertyVariable: query.propertyVariable,
|
|
2507
|
+
value
|
|
2508
|
+
});
|
|
2509
|
+
case "string": return buildPropertyStringQueryExpression({
|
|
2510
|
+
propertyVariable: query.propertyVariable,
|
|
2511
|
+
value,
|
|
2512
|
+
matchMode,
|
|
2513
|
+
isCaseSensitive: query.isCaseSensitive,
|
|
2514
|
+
language: query.language
|
|
2515
|
+
});
|
|
2516
|
+
case "integer":
|
|
2517
|
+
case "decimal":
|
|
2518
|
+
case "time":
|
|
2519
|
+
case "boolean":
|
|
2520
|
+
case "date":
|
|
2521
|
+
case "dateTime": return buildPropertyScalarQueryExpression({
|
|
2522
|
+
propertyVariable: query.propertyVariable,
|
|
2523
|
+
value,
|
|
2524
|
+
matchMode,
|
|
2525
|
+
isCaseSensitive: query.isCaseSensitive
|
|
2526
|
+
});
|
|
2527
|
+
}
|
|
2528
|
+
}
|
|
2702
2529
|
}
|
|
2703
|
-
function
|
|
2704
|
-
return {
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
};
|
|
2530
|
+
function buildLeafQueryExpression(query) {
|
|
2531
|
+
if (query.target === "property" && query.dataType !== "date" && query.dataType !== "dateTime" && !("value" in query) && query.propertyVariable != null) return buildPropertyPresenceQueryExpression({ propertyVariable: query.propertyVariable });
|
|
2532
|
+
if (query.target === "property" && (query.dataType === "date" || query.dataType === "dateTime") && query.value == null) return buildPropertyDateRangeQueryExpression(query);
|
|
2533
|
+
const searchValue = getLeafSearchValue(query);
|
|
2534
|
+
if (searchValue == null) throw new Error("Missing searchable value for query leaf");
|
|
2535
|
+
if (query.matchMode === "exact") return buildLeafValueQueryExpression({
|
|
2536
|
+
query,
|
|
2537
|
+
value: searchValue,
|
|
2538
|
+
matchMode: "exact"
|
|
2539
|
+
});
|
|
2540
|
+
const terms = tokenizeIncludesSearchValue({
|
|
2541
|
+
value: searchValue,
|
|
2542
|
+
isCaseSensitive: query.isCaseSensitive
|
|
2543
|
+
});
|
|
2544
|
+
if (terms.length === 0) return "cts:false-query()";
|
|
2545
|
+
const termQueryExpressions = [];
|
|
2546
|
+
for (const term of terms) termQueryExpressions.push(buildLeafValueQueryExpression({
|
|
2547
|
+
query,
|
|
2548
|
+
value: term,
|
|
2549
|
+
matchMode: "includes"
|
|
2550
|
+
}));
|
|
2551
|
+
return buildAndCtsQueryExpressionInternal(termQueryExpressions);
|
|
2717
2552
|
}
|
|
2718
|
-
function
|
|
2553
|
+
function getGroupableIncludesValue(query) {
|
|
2554
|
+
if (query.matchMode !== "includes" || query.isNegated === true) return null;
|
|
2719
2555
|
switch (query.target) {
|
|
2720
|
-
case "string":
|
|
2721
|
-
case "title":
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
});
|
|
2728
|
-
case "description": return buildContentTargetIncludesGroupMember({
|
|
2729
|
-
contentNodesExpression: `description/content[@xml:lang="${query.language}"]`,
|
|
2730
|
-
value: query.value,
|
|
2731
|
-
isCaseSensitive: query.isCaseSensitive,
|
|
2732
|
-
language: query.language,
|
|
2733
|
-
containerElementName: "description"
|
|
2734
|
-
});
|
|
2735
|
-
case "periods": return buildContentTargetIncludesGroupMember({
|
|
2736
|
-
contentNodesExpression: `periods/period/identification/label/content[@xml:lang="${query.language}"]`,
|
|
2737
|
-
value: query.value,
|
|
2738
|
-
isCaseSensitive: query.isCaseSensitive,
|
|
2739
|
-
language: query.language,
|
|
2740
|
-
containerElementName: "period"
|
|
2741
|
-
});
|
|
2742
|
-
case "bibliography": return buildContentTargetIncludesGroupMember({
|
|
2743
|
-
contentNodesExpression: `bibliographies/bibliography/identification/label/content[@xml:lang="${query.language}"]`,
|
|
2744
|
-
value: query.value,
|
|
2745
|
-
isCaseSensitive: query.isCaseSensitive,
|
|
2746
|
-
language: query.language,
|
|
2747
|
-
containerElementName: "bibliography"
|
|
2748
|
-
});
|
|
2749
|
-
case "image": return buildContentTargetIncludesGroupMember({
|
|
2750
|
-
contentNodesExpression: `image/identification/label/content[@xml:lang="${query.language}"]`,
|
|
2751
|
-
value: query.value,
|
|
2752
|
-
isCaseSensitive: query.isCaseSensitive,
|
|
2753
|
-
language: query.language,
|
|
2754
|
-
containerElementName: "image"
|
|
2755
|
-
});
|
|
2756
|
-
case "notes": return buildNotesIncludesGroupMember(query);
|
|
2556
|
+
case "string":
|
|
2557
|
+
case "title":
|
|
2558
|
+
case "description":
|
|
2559
|
+
case "image":
|
|
2560
|
+
case "periods":
|
|
2561
|
+
case "bibliography":
|
|
2562
|
+
case "notes": return query.value;
|
|
2757
2563
|
case "property":
|
|
2758
|
-
if (
|
|
2759
|
-
return
|
|
2564
|
+
if (!("value" in query) || query.value == null || query.dataType === "IDREF") return null;
|
|
2565
|
+
return query.value;
|
|
2760
2566
|
}
|
|
2761
2567
|
}
|
|
2762
|
-
function
|
|
2763
|
-
|
|
2568
|
+
function isQueryLeaf(query) {
|
|
2569
|
+
return "target" in query;
|
|
2570
|
+
}
|
|
2571
|
+
function getQueryGroupChildren(query) {
|
|
2572
|
+
return "and" in query ? query.and : query.or;
|
|
2573
|
+
}
|
|
2574
|
+
function getQueryGroupOperator(query) {
|
|
2575
|
+
return "and" in query ? "and" : "or";
|
|
2576
|
+
}
|
|
2577
|
+
function getCompatibleIncludesGroupLeaves(query) {
|
|
2764
2578
|
if (!("or" in query) || query.or.length <= 1) return null;
|
|
2765
2579
|
const leafQueries = [];
|
|
2766
2580
|
for (const childQuery of query.or) {
|
|
@@ -2770,482 +2584,64 @@ function getCompatibleIncludesGroupLeaves(params) {
|
|
|
2770
2584
|
const firstQuery = leafQueries[0];
|
|
2771
2585
|
if (firstQuery == null) return null;
|
|
2772
2586
|
const groupValue = getGroupableIncludesValue(firstQuery);
|
|
2773
|
-
if (groupValue == null
|
|
2774
|
-
|
|
2775
|
-
value: groupValue,
|
|
2776
|
-
isCaseSensitive: firstQuery.isCaseSensitive,
|
|
2777
|
-
language: firstQuery.language,
|
|
2778
|
-
version
|
|
2779
|
-
})) return null;
|
|
2780
|
-
for (const leafQuery of leafQueries.slice(1)) if (!isCompatibleIncludesGroupQuery({
|
|
2781
|
-
query: leafQuery,
|
|
2782
|
-
value: groupValue,
|
|
2783
|
-
isCaseSensitive: firstQuery.isCaseSensitive,
|
|
2784
|
-
language: firstQuery.language,
|
|
2785
|
-
version
|
|
2786
|
-
})) return null;
|
|
2587
|
+
if (groupValue == null) return null;
|
|
2588
|
+
for (const leafQuery of leafQueries) if (getGroupableIncludesValue(leafQuery) !== groupValue || leafQuery.isCaseSensitive !== firstQuery.isCaseSensitive || leafQuery.language !== firstQuery.language) return null;
|
|
2787
2589
|
return leafQueries;
|
|
2788
2590
|
}
|
|
2789
|
-
function
|
|
2790
|
-
|
|
2591
|
+
function buildIncludesGroupMember(query) {
|
|
2592
|
+
return { buildTermQuery: (term) => buildLeafValueQueryExpression({
|
|
2593
|
+
query,
|
|
2594
|
+
value: term,
|
|
2595
|
+
matchMode: "includes"
|
|
2596
|
+
}) };
|
|
2597
|
+
}
|
|
2598
|
+
function buildIncludesGroupQueryExpression(queries) {
|
|
2791
2599
|
const firstQuery = queries[0];
|
|
2792
2600
|
if (firstQuery == null) throw new Error("Cannot build an includes group without queries");
|
|
2793
2601
|
const groupValue = getGroupableIncludesValue(firstQuery);
|
|
2794
2602
|
if (groupValue == null) throw new Error("Cannot build an includes group without a search value");
|
|
2795
|
-
const
|
|
2796
|
-
const tokenizedSearchDeclarations = buildTokenizedSearchDeclarations({
|
|
2797
|
-
value: groupValue,
|
|
2798
|
-
isCaseSensitive: firstQuery.isCaseSensitive,
|
|
2799
|
-
queryKey
|
|
2800
|
-
});
|
|
2801
|
-
const tokenizedTerms = tokenizeIncludesSearchValue({
|
|
2603
|
+
const terms = tokenizeIncludesSearchValue({
|
|
2802
2604
|
value: groupValue,
|
|
2803
2605
|
isCaseSensitive: firstQuery.isCaseSensitive
|
|
2804
2606
|
});
|
|
2805
|
-
|
|
2806
|
-
const
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
return {
|
|
2813
|
-
declarations: [
|
|
2814
|
-
...tokenizedSearchDeclarations.declarations,
|
|
2815
|
-
`let ${termQueriesVar} :=
|
|
2816
|
-
for $term in ${tokenizedSearchDeclarations.termsVar}
|
|
2817
|
-
return ${buildOrCtsQueryExpression(members.map((member) => member.buildCandidateTermQuery("$term")))}`,
|
|
2818
|
-
`let ${candidateQueryVar} :=
|
|
2819
|
-
if (count(${tokenizedSearchDeclarations.termsVar}) = 1)
|
|
2820
|
-
then ${termQueriesVar}[1]
|
|
2821
|
-
else if (count(${tokenizedSearchDeclarations.termsVar}) gt 1)
|
|
2822
|
-
then cts:and-query(${termQueriesVar})
|
|
2823
|
-
else ()`
|
|
2824
|
-
],
|
|
2825
|
-
predicate: `cts:contains(., ${candidateQueryVar})`,
|
|
2826
|
-
candidateQueryVar
|
|
2827
|
-
};
|
|
2828
|
-
}
|
|
2829
|
-
/**
|
|
2830
|
-
* Build a string match predicate for an XQuery string.
|
|
2831
|
-
*/
|
|
2832
|
-
function buildStringMatchClause(params) {
|
|
2833
|
-
const { contentNodesExpression, value, matchMode, isCaseSensitive, version, queryKey, allowCtsPredicates, buildCandidateTermQuery } = params;
|
|
2834
|
-
const valueExpression = buildFlattenedContentValuesExpression(contentNodesExpression);
|
|
2835
|
-
if (allowCtsPredicates && matchMode === "includes" && version === 2 && buildCandidateTermQuery != null) return buildCtsIncludesPredicate({
|
|
2836
|
-
searchableNodesExpression: buildSearchableContentNodesExpression(contentNodesExpression),
|
|
2837
|
-
fallbackValueExpression: valueExpression,
|
|
2838
|
-
value,
|
|
2839
|
-
isCaseSensitive,
|
|
2840
|
-
queryKey,
|
|
2841
|
-
buildCandidateTermQuery
|
|
2842
|
-
});
|
|
2843
|
-
return {
|
|
2844
|
-
declarations: [],
|
|
2845
|
-
predicate: buildRawStringMatchPredicate({
|
|
2846
|
-
valueExpression,
|
|
2847
|
-
value,
|
|
2848
|
-
matchMode,
|
|
2849
|
-
isCaseSensitive
|
|
2850
|
-
}),
|
|
2851
|
-
candidateQueryVar: null
|
|
2852
|
-
};
|
|
2853
|
-
}
|
|
2854
|
-
function buildCombinedRawStringMatchClause(params) {
|
|
2855
|
-
const { valueExpressions, value, matchMode, isCaseSensitive, version, queryKey, allowCtsPredicates, buildCandidateTermQuery } = params;
|
|
2856
|
-
const predicate = buildCombinedRawStringMatchPredicate({
|
|
2857
|
-
valueExpressions,
|
|
2858
|
-
value,
|
|
2859
|
-
matchMode,
|
|
2860
|
-
isCaseSensitive
|
|
2861
|
-
});
|
|
2862
|
-
if (allowCtsPredicates && matchMode === "includes" && version === 2 && buildCandidateTermQuery != null) {
|
|
2863
|
-
const candidateQuery = buildCtsCandidateQuery({
|
|
2864
|
-
value,
|
|
2865
|
-
isCaseSensitive,
|
|
2866
|
-
queryKey,
|
|
2867
|
-
buildCandidateTermQuery
|
|
2868
|
-
});
|
|
2869
|
-
return {
|
|
2870
|
-
declarations: candidateQuery.declarations,
|
|
2871
|
-
predicate,
|
|
2872
|
-
candidateQueryVar: candidateQuery.candidateQueryVar
|
|
2873
|
-
};
|
|
2874
|
-
}
|
|
2875
|
-
return {
|
|
2876
|
-
declarations: [],
|
|
2877
|
-
predicate,
|
|
2878
|
-
candidateQueryVar: null
|
|
2879
|
-
};
|
|
2880
|
-
}
|
|
2881
|
-
function buildPropertyValueAttributePredicate(params) {
|
|
2882
|
-
const { propertyValue, attributeName } = params;
|
|
2883
|
-
return `value[@${attributeName}=${stringLiteral(propertyValue)}]`;
|
|
2884
|
-
}
|
|
2885
|
-
function buildPropertyPredicateExpression(propertyPredicates) {
|
|
2886
|
-
let propertyExpression = ".//properties//property";
|
|
2887
|
-
for (const propertyPredicate of propertyPredicates) propertyExpression += `[${propertyPredicate}]`;
|
|
2888
|
-
return propertyExpression;
|
|
2889
|
-
}
|
|
2890
|
-
function buildPropertyStringValueClause(params) {
|
|
2891
|
-
const { query, version, queryKey, allowCtsPredicates } = params;
|
|
2892
|
-
return buildStringMatchClause({
|
|
2893
|
-
contentNodesExpression: buildPropertyValueContentNodesExpression({
|
|
2894
|
-
language: query.language,
|
|
2895
|
-
excludeInherited: allowCtsPredicates && query.matchMode === "includes" && version === 2
|
|
2896
|
-
}),
|
|
2897
|
-
value: query.value,
|
|
2898
|
-
matchMode: query.matchMode,
|
|
2899
|
-
isCaseSensitive: query.isCaseSensitive,
|
|
2900
|
-
version,
|
|
2901
|
-
queryKey,
|
|
2902
|
-
allowCtsPredicates,
|
|
2903
|
-
buildCandidateTermQuery: (termExpression) => buildPropertyStringCandidateBranch({
|
|
2904
|
-
termExpression,
|
|
2905
|
-
isCaseSensitive: query.isCaseSensitive,
|
|
2906
|
-
language: query.language
|
|
2907
|
-
})
|
|
2908
|
-
});
|
|
2909
|
-
}
|
|
2910
|
-
function buildPropertyScalarValueClause(params) {
|
|
2911
|
-
const { value, matchMode, isCaseSensitive, version, queryKey, allowCtsPredicates } = params;
|
|
2912
|
-
return buildCombinedRawStringMatchClause({
|
|
2913
|
-
valueExpressions: buildPropertyScalarValueExpressions(),
|
|
2914
|
-
value,
|
|
2915
|
-
matchMode,
|
|
2916
|
-
isCaseSensitive,
|
|
2917
|
-
version,
|
|
2918
|
-
queryKey,
|
|
2919
|
-
allowCtsPredicates,
|
|
2920
|
-
buildCandidateTermQuery: (termExpression) => buildPropertyScalarCandidateBranch({
|
|
2921
|
-
termExpression,
|
|
2922
|
-
isCaseSensitive
|
|
2923
|
-
})
|
|
2924
|
-
});
|
|
2925
|
-
}
|
|
2926
|
-
function buildPropertyAllValueClause(query) {
|
|
2927
|
-
return {
|
|
2928
|
-
declarations: [],
|
|
2929
|
-
predicate: buildCombinedRawStringMatchPredicate({
|
|
2930
|
-
valueExpressions: buildAllPropertyValueExpressions(query.language),
|
|
2931
|
-
value: query.value,
|
|
2932
|
-
matchMode: query.matchMode,
|
|
2933
|
-
isCaseSensitive: query.isCaseSensitive
|
|
2934
|
-
}),
|
|
2935
|
-
candidateQueryVar: null
|
|
2936
|
-
};
|
|
2937
|
-
}
|
|
2938
|
-
function buildNotesValueClause(params) {
|
|
2939
|
-
const { query, version, queryKey, allowCtsPredicates } = params;
|
|
2940
|
-
return buildCombinedRawStringMatchClause({
|
|
2941
|
-
valueExpressions: buildNotesValueExpressions(query.language),
|
|
2942
|
-
value: query.value,
|
|
2943
|
-
matchMode: query.matchMode,
|
|
2944
|
-
isCaseSensitive: query.isCaseSensitive,
|
|
2945
|
-
version,
|
|
2946
|
-
queryKey,
|
|
2947
|
-
allowCtsPredicates,
|
|
2948
|
-
buildCandidateTermQuery: (termExpression) => buildNotesCandidateBranch({
|
|
2949
|
-
termExpression,
|
|
2950
|
-
isCaseSensitive: query.isCaseSensitive,
|
|
2951
|
-
language: query.language
|
|
2952
|
-
})
|
|
2953
|
-
});
|
|
2954
|
-
}
|
|
2955
|
-
/**
|
|
2956
|
-
* Build a property predicate for an XQuery string.
|
|
2957
|
-
*/
|
|
2958
|
-
function buildPropertyClause(params) {
|
|
2959
|
-
const { query, version, queryKey, allowCtsPredicates } = params;
|
|
2960
|
-
const predicateParts = [];
|
|
2961
|
-
const declarations = [];
|
|
2962
|
-
const propertyVariable = query.propertyVariable;
|
|
2963
|
-
let candidateQueryVar = null;
|
|
2964
|
-
if (propertyVariable != null) predicateParts.push(buildPropertyLabelPredicate(propertyVariable));
|
|
2965
|
-
if (query.dataType === "all") {
|
|
2966
|
-
const compiledAllClause = buildPropertyAllValueClause(query);
|
|
2967
|
-
declarations.push(...compiledAllClause.declarations);
|
|
2968
|
-
predicateParts.push(compiledAllClause.predicate);
|
|
2969
|
-
candidateQueryVar = compiledAllClause.candidateQueryVar;
|
|
2970
|
-
} else if (query.dataType === "date" || query.dataType === "dateTime") if ("value" in query && query.value != null) {
|
|
2971
|
-
const compiledScalarClause = buildPropertyScalarValueClause({
|
|
2972
|
-
value: query.value,
|
|
2973
|
-
matchMode: query.matchMode,
|
|
2974
|
-
isCaseSensitive: query.isCaseSensitive,
|
|
2975
|
-
version,
|
|
2976
|
-
queryKey,
|
|
2977
|
-
allowCtsPredicates
|
|
2978
|
-
});
|
|
2979
|
-
declarations.push(...compiledScalarClause.declarations);
|
|
2980
|
-
predicateParts.push(compiledScalarClause.predicate);
|
|
2981
|
-
candidateQueryVar = compiledScalarClause.candidateQueryVar;
|
|
2982
|
-
} else predicateParts.push(buildDateRangePredicate({
|
|
2983
|
-
from: query.from,
|
|
2984
|
-
to: query.to
|
|
2985
|
-
}));
|
|
2986
|
-
else if (query.value != null) switch (query.dataType) {
|
|
2987
|
-
case "IDREF":
|
|
2988
|
-
predicateParts.push(buildPropertyValueAttributePredicate({
|
|
2989
|
-
propertyValue: query.value,
|
|
2990
|
-
attributeName: "uuid"
|
|
2991
|
-
}));
|
|
2992
|
-
break;
|
|
2993
|
-
case "integer":
|
|
2994
|
-
case "decimal":
|
|
2995
|
-
case "time":
|
|
2996
|
-
case "boolean": {
|
|
2997
|
-
const compiledScalarClause = buildPropertyScalarValueClause({
|
|
2998
|
-
value: query.value,
|
|
2999
|
-
matchMode: query.matchMode,
|
|
3000
|
-
isCaseSensitive: query.isCaseSensitive,
|
|
3001
|
-
version,
|
|
3002
|
-
queryKey,
|
|
3003
|
-
allowCtsPredicates
|
|
3004
|
-
});
|
|
3005
|
-
declarations.push(...compiledScalarClause.declarations);
|
|
3006
|
-
predicateParts.push(compiledScalarClause.predicate);
|
|
3007
|
-
candidateQueryVar = compiledScalarClause.candidateQueryVar;
|
|
3008
|
-
break;
|
|
3009
|
-
}
|
|
3010
|
-
case "string": {
|
|
3011
|
-
const compiledStringClause = buildPropertyStringValueClause({
|
|
3012
|
-
query,
|
|
3013
|
-
version,
|
|
3014
|
-
queryKey,
|
|
3015
|
-
allowCtsPredicates
|
|
3016
|
-
});
|
|
3017
|
-
declarations.push(...compiledStringClause.declarations);
|
|
3018
|
-
predicateParts.push(compiledStringClause.predicate);
|
|
3019
|
-
candidateQueryVar = compiledStringClause.candidateQueryVar;
|
|
3020
|
-
break;
|
|
3021
|
-
}
|
|
2607
|
+
if (terms.length === 0) return "cts:false-query()";
|
|
2608
|
+
const members = queries.map((query) => buildIncludesGroupMember(query));
|
|
2609
|
+
const perTermQueryExpressions = [];
|
|
2610
|
+
for (const term of terms) {
|
|
2611
|
+
const fieldQueryExpressions = [];
|
|
2612
|
+
for (const member of members) fieldQueryExpressions.push(member.buildTermQuery(term));
|
|
2613
|
+
perTermQueryExpressions.push(buildOrCtsQueryExpressionInternal(fieldQueryExpressions));
|
|
3022
2614
|
}
|
|
3023
|
-
return
|
|
3024
|
-
declarations,
|
|
3025
|
-
predicate: buildPropertyPredicateExpression(predicateParts),
|
|
3026
|
-
candidateQueryVar
|
|
3027
|
-
};
|
|
2615
|
+
return buildAndCtsQueryExpressionInternal(perTermQueryExpressions);
|
|
3028
2616
|
}
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
const { query, version, queryKey, allowCtsPredicates } = params;
|
|
3034
|
-
switch (query.target) {
|
|
3035
|
-
case "string": return buildItemStringSearchClause({
|
|
3036
|
-
query,
|
|
3037
|
-
version,
|
|
3038
|
-
queryKey,
|
|
3039
|
-
allowCtsPredicates
|
|
3040
|
-
});
|
|
3041
|
-
case "title": return buildStringMatchClause({
|
|
3042
|
-
contentNodesExpression: `identification/label/content[@xml:lang="${query.language}"]`,
|
|
3043
|
-
value: query.value,
|
|
3044
|
-
matchMode: query.matchMode,
|
|
3045
|
-
isCaseSensitive: query.isCaseSensitive,
|
|
3046
|
-
version,
|
|
3047
|
-
queryKey,
|
|
3048
|
-
allowCtsPredicates,
|
|
3049
|
-
buildCandidateTermQuery: (termExpression) => buildContentTargetCandidateBranch({
|
|
3050
|
-
containerElementName: "identification",
|
|
3051
|
-
termExpression,
|
|
3052
|
-
isCaseSensitive: query.isCaseSensitive,
|
|
3053
|
-
language: query.language
|
|
3054
|
-
})
|
|
3055
|
-
});
|
|
3056
|
-
case "description": return buildStringMatchClause({
|
|
3057
|
-
contentNodesExpression: `description/content[@xml:lang="${query.language}"]`,
|
|
3058
|
-
value: query.value,
|
|
3059
|
-
matchMode: query.matchMode,
|
|
3060
|
-
isCaseSensitive: query.isCaseSensitive,
|
|
3061
|
-
version,
|
|
3062
|
-
queryKey,
|
|
3063
|
-
allowCtsPredicates,
|
|
3064
|
-
buildCandidateTermQuery: (termExpression) => buildContentTargetCandidateBranch({
|
|
3065
|
-
containerElementName: "description",
|
|
3066
|
-
termExpression,
|
|
3067
|
-
isCaseSensitive: query.isCaseSensitive,
|
|
3068
|
-
language: query.language
|
|
3069
|
-
})
|
|
3070
|
-
});
|
|
3071
|
-
case "periods": return buildStringMatchClause({
|
|
3072
|
-
contentNodesExpression: `periods/period/identification/label/content[@xml:lang="${query.language}"]`,
|
|
3073
|
-
value: query.value,
|
|
3074
|
-
matchMode: query.matchMode,
|
|
3075
|
-
isCaseSensitive: query.isCaseSensitive,
|
|
3076
|
-
version,
|
|
3077
|
-
queryKey,
|
|
3078
|
-
allowCtsPredicates,
|
|
3079
|
-
buildCandidateTermQuery: (termExpression) => buildContentTargetCandidateBranch({
|
|
3080
|
-
containerElementName: "period",
|
|
3081
|
-
termExpression,
|
|
3082
|
-
isCaseSensitive: query.isCaseSensitive,
|
|
3083
|
-
language: query.language
|
|
3084
|
-
})
|
|
3085
|
-
});
|
|
3086
|
-
case "bibliography": return buildStringMatchClause({
|
|
3087
|
-
contentNodesExpression: `bibliographies/bibliography/identification/label/content[@xml:lang="${query.language}"]`,
|
|
3088
|
-
value: query.value,
|
|
3089
|
-
matchMode: query.matchMode,
|
|
3090
|
-
isCaseSensitive: query.isCaseSensitive,
|
|
3091
|
-
version,
|
|
3092
|
-
queryKey,
|
|
3093
|
-
allowCtsPredicates,
|
|
3094
|
-
buildCandidateTermQuery: (termExpression) => buildContentTargetCandidateBranch({
|
|
3095
|
-
containerElementName: "bibliography",
|
|
3096
|
-
termExpression,
|
|
3097
|
-
isCaseSensitive: query.isCaseSensitive,
|
|
3098
|
-
language: query.language
|
|
3099
|
-
})
|
|
3100
|
-
});
|
|
3101
|
-
case "image": return buildStringMatchClause({
|
|
3102
|
-
contentNodesExpression: `image/identification/label/content[@xml:lang="${query.language}"]`,
|
|
3103
|
-
value: query.value,
|
|
3104
|
-
matchMode: query.matchMode,
|
|
3105
|
-
isCaseSensitive: query.isCaseSensitive,
|
|
3106
|
-
version,
|
|
3107
|
-
queryKey,
|
|
3108
|
-
allowCtsPredicates,
|
|
3109
|
-
buildCandidateTermQuery: (termExpression) => buildContentTargetCandidateBranch({
|
|
3110
|
-
containerElementName: "image",
|
|
3111
|
-
termExpression,
|
|
3112
|
-
isCaseSensitive: query.isCaseSensitive,
|
|
3113
|
-
language: query.language
|
|
3114
|
-
})
|
|
3115
|
-
});
|
|
3116
|
-
case "notes": return buildNotesValueClause({
|
|
3117
|
-
query,
|
|
3118
|
-
version,
|
|
3119
|
-
queryKey,
|
|
3120
|
-
allowCtsPredicates
|
|
3121
|
-
});
|
|
3122
|
-
case "property": return buildPropertyClause({
|
|
3123
|
-
query,
|
|
3124
|
-
version,
|
|
3125
|
-
queryKey,
|
|
3126
|
-
allowCtsPredicates
|
|
3127
|
-
});
|
|
2617
|
+
function buildQueryNode(query) {
|
|
2618
|
+
if (isQueryLeaf(query)) {
|
|
2619
|
+
const queryExpression = buildLeafQueryExpression(query);
|
|
2620
|
+
return query.isNegated === true ? buildNotCtsQueryExpression(queryExpression) : queryExpression;
|
|
3128
2621
|
}
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
|
|
3133
|
-
|
|
3134
|
-
|
|
3135
|
-
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
|
|
3140
|
-
|
|
3141
|
-
|
|
3142
|
-
return {
|
|
3143
|
-
declarations: compiledQueryClause.declarations,
|
|
3144
|
-
predicate: query.isNegated ? `not(${baseClause})` : baseClause,
|
|
3145
|
-
candidateQueryVars: query.isNegated || compiledQueryClause.candidateQueryVar == null ? [] : [compiledQueryClause.candidateQueryVar]
|
|
3146
|
-
};
|
|
3147
|
-
}
|
|
3148
|
-
function buildQueryNode(params) {
|
|
3149
|
-
const { query, version, queryKey, allowCtsPredicates } = params;
|
|
3150
|
-
if (isQueryLeaf(query)) return buildBooleanQueryNode({
|
|
3151
|
-
query,
|
|
3152
|
-
version,
|
|
3153
|
-
queryKey,
|
|
3154
|
-
allowCtsPredicates
|
|
3155
|
-
});
|
|
3156
|
-
const groupQueries = getQueryGroupChildren(query);
|
|
3157
|
-
const optimizedIncludesGroupQueries = getCompatibleIncludesGroupLeaves({
|
|
3158
|
-
query,
|
|
3159
|
-
version
|
|
3160
|
-
});
|
|
3161
|
-
if (groupQueries.length === 1) {
|
|
3162
|
-
const onlyQuery = groupQueries[0];
|
|
3163
|
-
if (onlyQuery == null) return {
|
|
3164
|
-
declarations: [],
|
|
3165
|
-
predicate: "",
|
|
3166
|
-
candidateQueryVars: []
|
|
3167
|
-
};
|
|
3168
|
-
return buildQueryNode({
|
|
3169
|
-
query: onlyQuery,
|
|
3170
|
-
version,
|
|
3171
|
-
queryKey,
|
|
3172
|
-
allowCtsPredicates
|
|
3173
|
-
});
|
|
3174
|
-
}
|
|
3175
|
-
if (allowCtsPredicates && optimizedIncludesGroupQueries != null) {
|
|
3176
|
-
const compiledClause = buildIncludesGroupClause({
|
|
3177
|
-
queries: optimizedIncludesGroupQueries,
|
|
3178
|
-
queryKey
|
|
3179
|
-
});
|
|
3180
|
-
return {
|
|
3181
|
-
declarations: compiledClause.declarations,
|
|
3182
|
-
predicate: compiledClause.predicate,
|
|
3183
|
-
candidateQueryVars: compiledClause.candidateQueryVar == null ? [] : [compiledClause.candidateQueryVar]
|
|
3184
|
-
};
|
|
3185
|
-
}
|
|
3186
|
-
const declarations = [];
|
|
3187
|
-
const predicates = [];
|
|
3188
|
-
const groupOperator = getQueryGroupOperator(query);
|
|
3189
|
-
let compiledChildNodes = groupQueries.map((groupQuery, groupIndex) => buildQueryNode({
|
|
3190
|
-
query: groupQuery,
|
|
3191
|
-
version,
|
|
3192
|
-
queryKey: `${queryKey}_${groupIndex + 1}`,
|
|
3193
|
-
allowCtsPredicates
|
|
3194
|
-
}));
|
|
3195
|
-
if (allowCtsPredicates && groupOperator === "or" && compiledChildNodes.some((node) => node.candidateQueryVars.length === 0)) compiledChildNodes = groupQueries.map((groupQuery, groupIndex) => buildQueryNode({
|
|
3196
|
-
query: groupQuery,
|
|
3197
|
-
version,
|
|
3198
|
-
queryKey: `${queryKey}_${groupIndex + 1}`,
|
|
3199
|
-
allowCtsPredicates: false
|
|
2622
|
+
const optimizedIncludesGroupQueries = getCompatibleIncludesGroupLeaves(query);
|
|
2623
|
+
if (optimizedIncludesGroupQueries != null) return buildIncludesGroupQueryExpression(optimizedIncludesGroupQueries);
|
|
2624
|
+
const childQueryExpressions = [];
|
|
2625
|
+
for (const childQuery of getQueryGroupChildren(query)) childQueryExpressions.push(buildQueryNode(childQuery));
|
|
2626
|
+
return getQueryGroupOperator(query) === "and" ? buildAndCtsQueryExpressionInternal(childQueryExpressions) : buildOrCtsQueryExpressionInternal(childQueryExpressions);
|
|
2627
|
+
}
|
|
2628
|
+
function buildBelongsToCollectionQueryExpression(belongsToCollectionScopeUuids, belongsToCollectionPropertyVariableUuid) {
|
|
2629
|
+
if (belongsToCollectionScopeUuids.length === 0) return null;
|
|
2630
|
+
const collectionValueQueryExpressions = [];
|
|
2631
|
+
for (const uuid of belongsToCollectionScopeUuids) collectionValueQueryExpressions.push(buildPlainElementAttributeValueQueryExpression({
|
|
2632
|
+
elementName: "value",
|
|
2633
|
+
attributeName: "uuid",
|
|
2634
|
+
value: uuid
|
|
3200
2635
|
}));
|
|
3201
|
-
|
|
3202
|
-
|
|
3203
|
-
|
|
3204
|
-
|
|
3205
|
-
candidateQueryVarsByChild.push(compiledQueryNode.candidateQueryVars);
|
|
3206
|
-
}
|
|
3207
|
-
const candidateQueryVars = groupOperator === "and" ? candidateQueryVarsByChild.flat() : candidateQueryVarsByChild.every((vars) => vars.length > 0) ? candidateQueryVarsByChild.flat() : [];
|
|
3208
|
-
return {
|
|
3209
|
-
declarations,
|
|
3210
|
-
predicate: groupOperator === "and" ? buildAndPredicate(predicates) : buildOrPredicate(predicates),
|
|
3211
|
-
candidateQueryVars
|
|
3212
|
-
};
|
|
2636
|
+
return buildPropertyQueryExpression({
|
|
2637
|
+
propertyVariable: belongsToCollectionPropertyVariableUuid,
|
|
2638
|
+
queryExpression: buildNestedElementQuery(["value"], buildOrCtsQueryExpressionInternal(collectionValueQueryExpressions))
|
|
2639
|
+
});
|
|
3213
2640
|
}
|
|
3214
2641
|
function buildQueryPlan(params) {
|
|
3215
|
-
const { queries
|
|
3216
|
-
|
|
3217
|
-
|
|
3218
|
-
let candidateQueryVars = [];
|
|
3219
|
-
if (queries != null) {
|
|
3220
|
-
const compiledQueryNode = buildQueryNode({
|
|
3221
|
-
query: queries,
|
|
3222
|
-
version,
|
|
3223
|
-
queryKey: "1",
|
|
3224
|
-
allowCtsPredicates: true
|
|
3225
|
-
});
|
|
3226
|
-
if (compiledQueryNode.declarations.length > 0) declarations.push(`let ${CTS_INCLUDES_STOP_WORDS_VAR} := (${CTS_INCLUDES_STOP_WORDS.map((stopWord) => stringLiteral(stopWord)).join(", ")})`, ...compiledQueryNode.declarations);
|
|
3227
|
-
predicate = compiledQueryNode.predicate;
|
|
3228
|
-
candidateQueryVars = compiledQueryNode.candidateQueryVars;
|
|
3229
|
-
}
|
|
3230
|
-
let itemsExpression = `(${baseItemsExpression})`;
|
|
3231
|
-
if (candidateQueryVars.length > 0) {
|
|
3232
|
-
const candidateQueriesExpression = `(${candidateQueryVars.join(", ")})`;
|
|
3233
|
-
const candidateItemsQueryVar = "$candidateItemsQuery";
|
|
3234
|
-
declarations.push(`let ${candidateItemsQueryVar} :=
|
|
3235
|
-
if (count(${candidateQueriesExpression}) = 1)
|
|
3236
|
-
then ${candidateQueriesExpression}[1]
|
|
3237
|
-
else if (count(${candidateQueriesExpression}) gt 1)
|
|
3238
|
-
then cts:or-query(${candidateQueriesExpression})
|
|
3239
|
-
else ()`);
|
|
3240
|
-
itemsExpression = `(if (exists(${candidateItemsQueryVar}))
|
|
3241
|
-
then cts:search(${baseItemsExpression}, ${candidateItemsQueryVar})
|
|
3242
|
-
else ${baseItemsExpression})`;
|
|
3243
|
-
}
|
|
3244
|
-
return {
|
|
3245
|
-
declarations,
|
|
3246
|
-
itemsExpression,
|
|
3247
|
-
predicate
|
|
3248
|
-
};
|
|
2642
|
+
const { queries } = params;
|
|
2643
|
+
if (queries == null) return { queryExpression: null };
|
|
2644
|
+
return { queryExpression: buildQueryNode(queries) };
|
|
3249
2645
|
}
|
|
3250
2646
|
//#endregion
|
|
3251
2647
|
//#region src/utils/fetchers/set/items.ts
|
|
@@ -3329,31 +2725,21 @@ function buildOrderedItemsClause(sort) {
|
|
|
3329
2725
|
* For propertyValue sorting, dataType is required and the sort key uses the first valid leaf value (value[not(@i)]).
|
|
3330
2726
|
* @param params.page - The page number (1-indexed)
|
|
3331
2727
|
* @param params.pageSize - The number of items per page
|
|
3332
|
-
* @param options - Options for the fetch
|
|
3333
|
-
* @param options.version - The version of the OCHRE API to use
|
|
3334
2728
|
* @returns An XQuery string
|
|
3335
2729
|
*/
|
|
3336
|
-
function buildXQuery$1(params
|
|
3337
|
-
const version = options?.version ?? 2;
|
|
2730
|
+
function buildXQuery$1(params) {
|
|
3338
2731
|
const { queries, sort, setScopeUuids, belongsToCollectionScopeUuids, page, pageSize } = params;
|
|
3339
2732
|
const startPosition = (page - 1) * pageSize + 1;
|
|
3340
2733
|
const endPosition = page * pageSize;
|
|
3341
|
-
const
|
|
3342
|
-
const compiledQueryPlan = buildQueryPlan({
|
|
3343
|
-
|
|
3344
|
-
|
|
3345
|
-
|
|
3346
|
-
|
|
3347
|
-
const
|
|
3348
|
-
const filterPredicates = [];
|
|
3349
|
-
if (belongsToCollectionScopeUuids.length > 0) {
|
|
3350
|
-
const belongsToCollectionScopeValues = belongsToCollectionScopeUuids.map((uuid) => `@uuid="${uuid}"`).join(" or ");
|
|
3351
|
-
filterPredicates.push(`.//properties[property[label/@uuid="${BELONGS_TO_COLLECTION_UUID}" and value/(${belongsToCollectionScopeValues})]]`);
|
|
3352
|
-
}
|
|
3353
|
-
if (compiledQueryPlan.predicate.length > 0) filterPredicates.push(`(${compiledQueryPlan.predicate})`);
|
|
3354
|
-
const itemFilters = filterPredicates.length > 0 ? `[${filterPredicates.join(" and ")}]` : "";
|
|
2734
|
+
const baseItemsExpression = `doc()/ochre${`/set[(${setScopeUuids.map((uuid) => `@uuid="${uuid}"`).join(" or ")})]/items/*`}`;
|
|
2735
|
+
const compiledQueryPlan = buildQueryPlan({ queries });
|
|
2736
|
+
const itemsQueryExpressions = [];
|
|
2737
|
+
const belongsToCollectionQueryExpression = buildBelongsToCollectionQueryExpression(belongsToCollectionScopeUuids, BELONGS_TO_COLLECTION_UUID);
|
|
2738
|
+
if (compiledQueryPlan.queryExpression != null) itemsQueryExpressions.push(compiledQueryPlan.queryExpression);
|
|
2739
|
+
if (belongsToCollectionQueryExpression != null) itemsQueryExpressions.push(belongsToCollectionQueryExpression);
|
|
2740
|
+
const itemsQueryExpression = buildAndCtsQueryExpression(itemsQueryExpressions);
|
|
3355
2741
|
const orderedItemsClause = buildOrderedItemsClause(sort);
|
|
3356
|
-
return `<ochre>{${`${
|
|
2742
|
+
return `<ochre>{${`${itemsQueryExpression == null ? `let $items := ${baseItemsExpression}` : `let $items := ${baseItemsExpression}[cts:contains(., ${itemsQueryExpression})]`}
|
|
3357
2743
|
|
|
3358
2744
|
let $totalCount := count($items)
|
|
3359
2745
|
${orderedItemsClause}
|
|
@@ -3383,7 +2769,7 @@ function buildXQuery$1(params, options) {
|
|
|
3383
2769
|
*/
|
|
3384
2770
|
async function fetchSetItems(params, itemCategories, options) {
|
|
3385
2771
|
try {
|
|
3386
|
-
|
|
2772
|
+
if (options?.version != null && options.version !== 2) throw new Error("Set item queries only support API version 2");
|
|
3387
2773
|
const { setScopeUuids, belongsToCollectionScopeUuids, queries, sort, page, pageSize } = setItemsParamsSchema.parse(params);
|
|
3388
2774
|
const xquery = buildXQuery$1({
|
|
3389
2775
|
setScopeUuids,
|
|
@@ -3392,14 +2778,12 @@ async function fetchSetItems(params, itemCategories, options) {
|
|
|
3392
2778
|
sort,
|
|
3393
2779
|
page,
|
|
3394
2780
|
pageSize
|
|
3395
|
-
}
|
|
3396
|
-
|
|
3397
|
-
if (version === 2) response = await (options?.fetch ?? fetch)("https://ochre.lib.uchicago.edu/ochre/v2/ochre.php?xquery&format=json", {
|
|
2781
|
+
});
|
|
2782
|
+
const response = await (options?.fetch ?? fetch)("https://ochre.lib.uchicago.edu/ochre/v2/ochre.php?xquery&format=json", {
|
|
3398
2783
|
method: "POST",
|
|
3399
2784
|
body: xquery,
|
|
3400
2785
|
headers: { "Content-Type": "application/xquery" }
|
|
3401
2786
|
});
|
|
3402
|
-
else response = await (options?.fetch ?? fetch)(`https://ochre.lib.uchicago.edu/ochre?xquery=${encodeURIComponent(xquery)}&format=json&lang="*"`);
|
|
3403
2787
|
if (!response.ok) throw new Error(`OCHRE API responded with status: ${response.status}`);
|
|
3404
2788
|
const data = await response.json();
|
|
3405
2789
|
if (Array.isArray(data.result) || Object.keys(data.result).length === 0) throw new Error("Invalid OCHRE API response");
|
|
@@ -3561,6 +2945,23 @@ function getPropertyVariableUuidsFromQueries(queries) {
|
|
|
3561
2945
|
}
|
|
3562
2946
|
return [...propertyVariableUuids];
|
|
3563
2947
|
}
|
|
2948
|
+
function getItemFilterQueriesFromPropertyValueQueries(queries) {
|
|
2949
|
+
if (queries == null) return null;
|
|
2950
|
+
if ("target" in queries) {
|
|
2951
|
+
if (queries.target !== "property") return queries;
|
|
2952
|
+
if (queries.dataType === "date" || queries.dataType === "dateTime") return queries;
|
|
2953
|
+
return "value" in queries && queries.value != null ? queries : null;
|
|
2954
|
+
}
|
|
2955
|
+
const filteredChildren = [];
|
|
2956
|
+
const childQueries = "and" in queries ? queries.and : queries.or;
|
|
2957
|
+
for (const childQuery of childQueries) {
|
|
2958
|
+
const filteredChildQuery = getItemFilterQueriesFromPropertyValueQueries(childQuery);
|
|
2959
|
+
if (filteredChildQuery != null) filteredChildren.push(filteredChildQuery);
|
|
2960
|
+
}
|
|
2961
|
+
if (filteredChildren.length === 0) return null;
|
|
2962
|
+
if (filteredChildren.length === 1) return filteredChildren[0] ?? null;
|
|
2963
|
+
return "and" in queries ? { and: filteredChildren } : { or: filteredChildren };
|
|
2964
|
+
}
|
|
3564
2965
|
/**
|
|
3565
2966
|
* Schema for a single property value query item in the OCHRE API response
|
|
3566
2967
|
*/
|
|
@@ -3634,29 +3035,20 @@ const responseSchema = z.object({ result: z.union([z.object({ ochre: z.object({
|
|
|
3634
3035
|
* @param params.attributes.bibliographies - Whether to return values for bibliographies
|
|
3635
3036
|
* @param params.attributes.periods - Whether to return values for periods
|
|
3636
3037
|
* @param params.isLimitedToLeafPropertyValues - Whether to limit the property values to leaf property values
|
|
3637
|
-
* @param options - Options for the fetch
|
|
3638
|
-
* @param options.version - The version of the OCHRE API to use
|
|
3639
3038
|
* @returns An XQuery string
|
|
3640
3039
|
*/
|
|
3641
|
-
function buildXQuery(params
|
|
3642
|
-
const version = options?.version ?? 2;
|
|
3040
|
+
function buildXQuery(params) {
|
|
3643
3041
|
const { setScopeUuids, belongsToCollectionScopeUuids, queries, attributes, isLimitedToLeafPropertyValues } = params;
|
|
3644
3042
|
let setScopeFilter = "/set/items/*";
|
|
3645
3043
|
if (setScopeUuids.length > 0) setScopeFilter = `/set[(${setScopeUuids.map((uuid) => `@uuid="${uuid}"`).join(" or ")})]/items/*`;
|
|
3646
3044
|
const propertyVariableFilters = getPropertyVariableUuidsFromQueries(queries).map((uuid) => `@uuid="${uuid}"`).join(" or ");
|
|
3647
|
-
const
|
|
3648
|
-
|
|
3649
|
-
|
|
3650
|
-
|
|
3651
|
-
|
|
3652
|
-
|
|
3653
|
-
const
|
|
3654
|
-
if (belongsToCollectionScopeUuids.length > 0) {
|
|
3655
|
-
const belongsToCollectionScopeValues = belongsToCollectionScopeUuids.map((uuid) => `@uuid="${uuid}"`).join(" or ");
|
|
3656
|
-
filterPredicates.push(`.//properties[property[label/@uuid="${BELONGS_TO_COLLECTION_UUID}" and value[${belongsToCollectionScopeValues}]]]`);
|
|
3657
|
-
}
|
|
3658
|
-
if (compiledQueryPlan.predicate.length > 0) filterPredicates.push(`(${compiledQueryPlan.predicate})`);
|
|
3659
|
-
const itemFilters = filterPredicates.length > 0 ? `[${filterPredicates.join(" and ")}]` : "";
|
|
3045
|
+
const baseItemsExpression = `doc()/ochre${setScopeFilter}`;
|
|
3046
|
+
const compiledQueryPlan = buildQueryPlan({ queries: getItemFilterQueriesFromPropertyValueQueries(queries) });
|
|
3047
|
+
const itemsQueryExpressions = [];
|
|
3048
|
+
const belongsToCollectionQueryExpression = buildBelongsToCollectionQueryExpression(belongsToCollectionScopeUuids, BELONGS_TO_COLLECTION_UUID);
|
|
3049
|
+
if (compiledQueryPlan.queryExpression != null) itemsQueryExpressions.push(compiledQueryPlan.queryExpression);
|
|
3050
|
+
if (belongsToCollectionQueryExpression != null) itemsQueryExpressions.push(belongsToCollectionQueryExpression);
|
|
3051
|
+
const itemsQueryExpression = buildAndCtsQueryExpression(itemsQueryExpressions);
|
|
3660
3052
|
const queryBlocks = [`let $matching-props := $items//property[label[${propertyVariableFilters}]]
|
|
3661
3053
|
|
|
3662
3054
|
let $property-values :=
|
|
@@ -3686,7 +3078,7 @@ let $property-values :=
|
|
|
3686
3078
|
return <attributeValue attributeType="periods" itemUuid="{$item/@uuid}" content="{$label}" />`);
|
|
3687
3079
|
returnedSequences.push("$period-values");
|
|
3688
3080
|
}
|
|
3689
|
-
return `<ochre>{${`${
|
|
3081
|
+
return `<ochre>{${`${itemsQueryExpression == null ? `let $items := ${baseItemsExpression}` : `let $items := ${baseItemsExpression}[cts:contains(., ${itemsQueryExpression})]`}
|
|
3690
3082
|
|
|
3691
3083
|
${queryBlocks.join("\n\n")}
|
|
3692
3084
|
|
|
@@ -3710,7 +3102,7 @@ return (${returnedSequences.join(", ")})`}}</ochre>`;
|
|
|
3710
3102
|
*/
|
|
3711
3103
|
async function fetchSetPropertyValues(params, options) {
|
|
3712
3104
|
try {
|
|
3713
|
-
|
|
3105
|
+
if (options?.version != null && options.version !== 2) throw new Error("Set property value queries only support API version 2");
|
|
3714
3106
|
const { setScopeUuids, belongsToCollectionScopeUuids, queries, attributes, isLimitedToLeafPropertyValues } = setPropertyValuesParamsSchema.parse(params);
|
|
3715
3107
|
const xquery = buildXQuery({
|
|
3716
3108
|
setScopeUuids,
|
|
@@ -3718,14 +3110,12 @@ async function fetchSetPropertyValues(params, options) {
|
|
|
3718
3110
|
queries,
|
|
3719
3111
|
attributes,
|
|
3720
3112
|
isLimitedToLeafPropertyValues
|
|
3721
|
-
}
|
|
3722
|
-
|
|
3723
|
-
if (version === 2) response = await (options?.fetch ?? fetch)("https://ochre.lib.uchicago.edu/ochre/v2/ochre.php?xquery&format=json", {
|
|
3113
|
+
});
|
|
3114
|
+
const response = await (options?.fetch ?? fetch)("https://ochre.lib.uchicago.edu/ochre/v2/ochre.php?xquery&format=json", {
|
|
3724
3115
|
method: "POST",
|
|
3725
3116
|
body: xquery,
|
|
3726
3117
|
headers: { "Content-Type": "application/xquery" }
|
|
3727
3118
|
});
|
|
3728
|
-
else response = await (options?.fetch ?? fetch)(`https://ochre.lib.uchicago.edu/ochre?xquery=${encodeURIComponent(xquery)}&format=json&lang="*"`);
|
|
3729
3119
|
if (!response.ok) throw new Error(`OCHRE API responded with status: ${response.status}`);
|
|
3730
3120
|
const data = await response.json();
|
|
3731
3121
|
const parsedResultRaw = responseSchema.parse(data);
|