ochre-sdk 0.20.25 → 0.20.26
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.d.mts +1 -1
- package/dist/index.mjs +462 -131
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -675,7 +675,7 @@ type SetItemsSort = {
|
|
|
675
675
|
*/
|
|
676
676
|
type Query = {
|
|
677
677
|
target: "property";
|
|
678
|
-
propertyVariables
|
|
678
|
+
propertyVariables?: Array<string>;
|
|
679
679
|
dataType: Exclude<Exclude<PropertyValueContentType, "coordinate">, "date" | "dateTime">;
|
|
680
680
|
propertyValues?: Array<string>;
|
|
681
681
|
from?: never;
|
package/dist/index.mjs
CHANGED
|
@@ -759,7 +759,7 @@ const boundsSchema = z.string().transform((str, ctx) => {
|
|
|
759
759
|
const setQuerySchema = z.union([
|
|
760
760
|
z.object({
|
|
761
761
|
target: z.literal("property"),
|
|
762
|
-
propertyVariables: z.array(uuidSchema).
|
|
762
|
+
propertyVariables: z.array(uuidSchema).default([]),
|
|
763
763
|
dataType: z.enum([
|
|
764
764
|
"string",
|
|
765
765
|
"integer",
|
|
@@ -774,7 +774,12 @@ const setQuerySchema = z.union([
|
|
|
774
774
|
language: z.string().default("eng"),
|
|
775
775
|
operator: z.enum(["AND", "OR"]).optional(),
|
|
776
776
|
isNegated: z.boolean().optional().default(false)
|
|
777
|
-
}).strict(),
|
|
777
|
+
}).strict().superRefine((value, ctx) => {
|
|
778
|
+
if (value.propertyVariables.length === 0 && value.propertyValues == null) ctx.addIssue({
|
|
779
|
+
code: "custom",
|
|
780
|
+
message: "Property queries must include at least one propertyVariable or propertyValue"
|
|
781
|
+
});
|
|
782
|
+
}),
|
|
778
783
|
z.object({
|
|
779
784
|
target: z.literal("property"),
|
|
780
785
|
propertyVariables: z.array(uuidSchema).min(1, "At least one property variable UUID is required"),
|
|
@@ -878,10 +883,10 @@ const setPropertyValuesParamsSchema = z.object({
|
|
|
878
883
|
isLimitedToLeafPropertyValues: z.boolean().default(false)
|
|
879
884
|
}).superRefine((value, ctx) => {
|
|
880
885
|
validateSetQueriesOperators(value.queries, ctx);
|
|
881
|
-
if (!value.queries.some((query) => query.target === "property")) ctx.addIssue({
|
|
886
|
+
if (!value.queries.some((query) => query.target === "property" && query.propertyVariables.length > 0)) ctx.addIssue({
|
|
882
887
|
code: "custom",
|
|
883
888
|
path: ["queries"],
|
|
884
|
-
message: "At least one property query is required"
|
|
889
|
+
message: "At least one property query with propertyVariables is required"
|
|
885
890
|
});
|
|
886
891
|
});
|
|
887
892
|
const setItemsParamsSchema = z.object({
|
|
@@ -2145,10 +2150,35 @@ const CTS_INCLUDES_STOP_WORDS = [
|
|
|
2145
2150
|
"to"
|
|
2146
2151
|
];
|
|
2147
2152
|
const CTS_INCLUDES_STOP_WORDS_VAR = "$ctsIncludesStopWords";
|
|
2153
|
+
const CTS_INCLUDES_TOKEN_SPLIT_REGEX = /\W+/u;
|
|
2154
|
+
function tokenizeIncludesSearchValue(params) {
|
|
2155
|
+
const { value, isCaseSensitive } = params;
|
|
2156
|
+
const rawTerms = (isCaseSensitive ? value : value.toLowerCase()).split(CTS_INCLUDES_TOKEN_SPLIT_REGEX);
|
|
2157
|
+
const terms = [];
|
|
2158
|
+
for (const term of rawTerms) {
|
|
2159
|
+
const normalizedTerm = term.toLowerCase();
|
|
2160
|
+
if (normalizedTerm !== "" && !CTS_INCLUDES_STOP_WORDS.includes(normalizedTerm)) terms.push(term);
|
|
2161
|
+
}
|
|
2162
|
+
return terms;
|
|
2163
|
+
}
|
|
2148
2164
|
function buildFlattenedContentValuesExpression(contentNodesExpression) {
|
|
2149
2165
|
return `for $content in ${contentNodesExpression}
|
|
2150
2166
|
return string-join($content//text(), "")`;
|
|
2151
2167
|
}
|
|
2168
|
+
function buildSearchableContentNodesExpression(contentNodesExpression) {
|
|
2169
|
+
return `for $content in ${contentNodesExpression}
|
|
2170
|
+
return (
|
|
2171
|
+
$content//string[not(ancestor::string)],
|
|
2172
|
+
$content[not(.//string)]
|
|
2173
|
+
)`;
|
|
2174
|
+
}
|
|
2175
|
+
function buildCombinedSearchableContentNodesExpression(contentNodesExpressions) {
|
|
2176
|
+
const searchableExpressions = [];
|
|
2177
|
+
for (const contentNodesExpression of contentNodesExpressions) searchableExpressions.push(buildSearchableContentNodesExpression(contentNodesExpression));
|
|
2178
|
+
if (searchableExpressions.length === 0) return "()";
|
|
2179
|
+
if (searchableExpressions.length === 1) return searchableExpressions[0] ?? "()";
|
|
2180
|
+
return `(${searchableExpressions.join(", ")})`;
|
|
2181
|
+
}
|
|
2152
2182
|
/**
|
|
2153
2183
|
* Build a string match predicate for an XQuery string.
|
|
2154
2184
|
*/
|
|
@@ -2174,8 +2204,7 @@ function buildCombinedRawStringMatchPredicate(params) {
|
|
|
2174
2204
|
matchMode,
|
|
2175
2205
|
isCaseSensitive
|
|
2176
2206
|
}));
|
|
2177
|
-
|
|
2178
|
-
return `(${predicates.join(" or ")})`;
|
|
2207
|
+
return buildOrPredicate(predicates);
|
|
2179
2208
|
}
|
|
2180
2209
|
/**
|
|
2181
2210
|
* Build CTS word-query options for API v2 includes search.
|
|
@@ -2216,44 +2245,52 @@ function buildTokenizedSearchDeclarations(params) {
|
|
|
2216
2245
|
termsVar
|
|
2217
2246
|
};
|
|
2218
2247
|
}
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
*/
|
|
2222
|
-
function buildCtsFieldIncludesPredicate(params) {
|
|
2223
|
-
const { contentNodesExpression, valueExpression, value, isCaseSensitive, queryKey } = params;
|
|
2248
|
+
function buildCtsIncludesPredicate(params) {
|
|
2249
|
+
const { searchableNodesExpression, fallbackValueExpression, value, isCaseSensitive, queryKey, buildCandidateTermQuery } = params;
|
|
2224
2250
|
const tokenizedSearchDeclarations = buildTokenizedSearchDeclarations({
|
|
2225
2251
|
value,
|
|
2226
2252
|
isCaseSensitive,
|
|
2227
2253
|
queryKey
|
|
2228
2254
|
});
|
|
2229
|
-
const
|
|
2230
|
-
const
|
|
2255
|
+
const termQueriesVar = `$query${queryKey}TermQueries`;
|
|
2256
|
+
const candidateQueryVar = `$query${queryKey}CandidateQuery`;
|
|
2231
2257
|
const fallbackPredicate = buildRawStringMatchPredicate({
|
|
2232
|
-
valueExpression,
|
|
2258
|
+
valueExpression: fallbackValueExpression,
|
|
2233
2259
|
value,
|
|
2234
2260
|
matchMode: "includes",
|
|
2235
2261
|
isCaseSensitive
|
|
2236
2262
|
});
|
|
2263
|
+
if (tokenizeIncludesSearchValue({
|
|
2264
|
+
value,
|
|
2265
|
+
isCaseSensitive
|
|
2266
|
+
}).length === 0) return {
|
|
2267
|
+
declarations: [],
|
|
2268
|
+
predicate: fallbackPredicate,
|
|
2269
|
+
candidateQueryVar: null
|
|
2270
|
+
};
|
|
2237
2271
|
return {
|
|
2238
|
-
declarations: [
|
|
2272
|
+
declarations: [
|
|
2273
|
+
...tokenizedSearchDeclarations.declarations,
|
|
2274
|
+
`let ${termQueriesVar} :=
|
|
2275
|
+
for $term in ${tokenizedSearchDeclarations.termsVar}
|
|
2276
|
+
return ${buildCandidateTermQuery("$term")}`,
|
|
2277
|
+
`let ${candidateQueryVar} :=
|
|
2239
2278
|
if (count(${tokenizedSearchDeclarations.termsVar}) = 1)
|
|
2240
|
-
then ${
|
|
2241
|
-
termExpression: `${tokenizedSearchDeclarations.termsVar}[1]`,
|
|
2242
|
-
isCaseSensitive
|
|
2243
|
-
})}
|
|
2279
|
+
then ${termQueriesVar}[1]
|
|
2244
2280
|
else if (count(${tokenizedSearchDeclarations.termsVar}) gt 1)
|
|
2245
|
-
then cts:and-query(
|
|
2246
|
-
|
|
2247
|
-
|
|
2281
|
+
then cts:and-query(${termQueriesVar})
|
|
2282
|
+
else ()`
|
|
2283
|
+
],
|
|
2284
|
+
predicate: `(every $term in ${tokenizedSearchDeclarations.termsVar}
|
|
2285
|
+
satisfies some $searchNode in (${searchableNodesExpression})
|
|
2286
|
+
satisfies cts:contains(
|
|
2287
|
+
$searchNode,
|
|
2288
|
+
${buildCtsWordQueryExpression({
|
|
2248
2289
|
termExpression: "$term",
|
|
2249
2290
|
isCaseSensitive
|
|
2250
2291
|
})}
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
predicate: `(if (exists(${ctsQueryVar}))
|
|
2254
|
-
then some ${contentNodeVar} in (${contentNodesExpression})
|
|
2255
|
-
satisfies cts:contains(${contentNodeVar}, ${ctsQueryVar})
|
|
2256
|
-
else ${fallbackPredicate})`
|
|
2292
|
+
))`,
|
|
2293
|
+
candidateQueryVar
|
|
2257
2294
|
};
|
|
2258
2295
|
}
|
|
2259
2296
|
/**
|
|
@@ -2262,6 +2299,9 @@ function buildCtsFieldIncludesPredicate(params) {
|
|
|
2262
2299
|
function buildItemStringSearchPaths(language) {
|
|
2263
2300
|
return [buildFlattenedContentValuesExpression(`identification/label/content[@xml:lang="${language}"]`), buildFlattenedContentValuesExpression(`properties//property/value[not(@inherited="true")]/content[@xml:lang="${language}"]`)];
|
|
2264
2301
|
}
|
|
2302
|
+
function buildItemStringSearchableNodesExpression(language) {
|
|
2303
|
+
return buildCombinedSearchableContentNodesExpression([`identification/label/content[@xml:lang="${language}"]`, `properties//property/value[not(@inherited="true")]/content[@xml:lang="${language}"]`]);
|
|
2304
|
+
}
|
|
2265
2305
|
/**
|
|
2266
2306
|
* Build the identification branch for an item-level CTS string search.
|
|
2267
2307
|
*/
|
|
@@ -2304,7 +2344,7 @@ function buildItemStringPropertyValueBranch(params) {
|
|
|
2304
2344
|
/**
|
|
2305
2345
|
* Build an item-level CTS string search predicate.
|
|
2306
2346
|
*/
|
|
2307
|
-
function
|
|
2347
|
+
function buildItemStringSearchClause(params) {
|
|
2308
2348
|
const { query, version, queryKey } = params;
|
|
2309
2349
|
const fallbackPredicate = buildCombinedRawStringMatchPredicate({
|
|
2310
2350
|
valueExpressions: buildItemStringSearchPaths(query.language),
|
|
@@ -2314,55 +2354,292 @@ function buildItemStringSearchPredicate(params) {
|
|
|
2314
2354
|
});
|
|
2315
2355
|
if (query.matchMode !== "includes" || version !== 2) return {
|
|
2316
2356
|
declarations: [],
|
|
2317
|
-
predicate: fallbackPredicate
|
|
2357
|
+
predicate: fallbackPredicate,
|
|
2358
|
+
candidateQueryVar: null
|
|
2318
2359
|
};
|
|
2319
|
-
|
|
2360
|
+
return buildCtsIncludesPredicate({
|
|
2361
|
+
searchableNodesExpression: buildItemStringSearchableNodesExpression(query.language),
|
|
2362
|
+
fallbackValueExpression: `(${buildItemStringSearchPaths(query.language).join(", ")})`,
|
|
2320
2363
|
value: query.value,
|
|
2321
2364
|
isCaseSensitive: query.isCaseSensitive,
|
|
2365
|
+
queryKey,
|
|
2366
|
+
buildCandidateTermQuery: (termExpression) => `cts:or-query((
|
|
2367
|
+
${buildItemStringIdentificationBranch({
|
|
2368
|
+
termExpression,
|
|
2369
|
+
isCaseSensitive: query.isCaseSensitive,
|
|
2370
|
+
language: query.language
|
|
2371
|
+
})},
|
|
2372
|
+
${buildItemStringPropertyValueBranch({
|
|
2373
|
+
termExpression,
|
|
2374
|
+
isCaseSensitive: query.isCaseSensitive,
|
|
2375
|
+
language: query.language
|
|
2376
|
+
})}
|
|
2377
|
+
))`
|
|
2378
|
+
});
|
|
2379
|
+
}
|
|
2380
|
+
function buildOrPredicate(predicates) {
|
|
2381
|
+
if (predicates.length === 0) return "false()";
|
|
2382
|
+
if (predicates.length === 1) return predicates[0] ?? "false()";
|
|
2383
|
+
return `(${predicates.join(" or ")})`;
|
|
2384
|
+
}
|
|
2385
|
+
function buildAndPredicate(predicates) {
|
|
2386
|
+
if (predicates.length === 0) return "true()";
|
|
2387
|
+
if (predicates.length === 1) return predicates[0] ?? "true()";
|
|
2388
|
+
return `(${predicates.join(" and ")})`;
|
|
2389
|
+
}
|
|
2390
|
+
/**
|
|
2391
|
+
* Build a date/dateTime range predicate for an XQuery string.
|
|
2392
|
+
*/
|
|
2393
|
+
function buildDateRangePredicate(params) {
|
|
2394
|
+
const { from, to } = params;
|
|
2395
|
+
const conditions = [];
|
|
2396
|
+
if (from != null) conditions.push(`(value/@rawValue ge ${stringLiteral(from)})`);
|
|
2397
|
+
if (to != null) conditions.push(`(value/@rawValue le ${stringLiteral(to)})`);
|
|
2398
|
+
return buildAndPredicate(conditions);
|
|
2399
|
+
}
|
|
2400
|
+
/**
|
|
2401
|
+
* Build a property label predicate for an XQuery string.
|
|
2402
|
+
*/
|
|
2403
|
+
function buildPropertyLabelPredicate(propertyVariables) {
|
|
2404
|
+
const labelPredicates = [];
|
|
2405
|
+
for (const propertyVariable of propertyVariables) labelPredicates.push(`@uuid=${stringLiteral(propertyVariable)}`);
|
|
2406
|
+
return `label[${buildOrPredicate(labelPredicates)}]`;
|
|
2407
|
+
}
|
|
2408
|
+
function buildOrCtsQueryExpression(queries) {
|
|
2409
|
+
if (queries.length === 1) return queries[0] ?? "cts:false-query()";
|
|
2410
|
+
return `cts:or-query((${queries.join(", ")}))`;
|
|
2411
|
+
}
|
|
2412
|
+
function buildContentTargetCandidateBranch(params) {
|
|
2413
|
+
const { containerElementName, termExpression, isCaseSensitive, language } = params;
|
|
2414
|
+
return `cts:element-query(xs:QName("${containerElementName}"),
|
|
2415
|
+
cts:and-query((
|
|
2416
|
+
cts:element-attribute-value-query(xs:QName("content"), xs:QName("xml:lang"), ${stringLiteral(language)}),
|
|
2417
|
+
${buildCtsWordQueryExpression({
|
|
2418
|
+
termExpression,
|
|
2419
|
+
isCaseSensitive
|
|
2420
|
+
})}
|
|
2421
|
+
))
|
|
2422
|
+
)`;
|
|
2423
|
+
}
|
|
2424
|
+
function buildPropertyStringCandidateBranch(params) {
|
|
2425
|
+
const { termExpression, isCaseSensitive, language } = params;
|
|
2426
|
+
return `cts:element-query(xs:QName("properties"),
|
|
2427
|
+
cts:element-query(xs:QName("property"),
|
|
2428
|
+
cts:element-query(xs:QName("value"),
|
|
2429
|
+
cts:and-query((
|
|
2430
|
+
cts:not-query(cts:element-attribute-value-query(xs:QName("value"), xs:QName("inherited"), "true")),
|
|
2431
|
+
cts:element-query(xs:QName("content"),
|
|
2432
|
+
cts:and-query((
|
|
2433
|
+
cts:element-attribute-value-query(xs:QName("content"), xs:QName("xml:lang"), ${stringLiteral(language)}),
|
|
2434
|
+
${buildCtsWordQueryExpression({
|
|
2435
|
+
termExpression,
|
|
2436
|
+
isCaseSensitive
|
|
2437
|
+
})}
|
|
2438
|
+
))
|
|
2439
|
+
)
|
|
2440
|
+
))
|
|
2441
|
+
)
|
|
2442
|
+
)
|
|
2443
|
+
)`;
|
|
2444
|
+
}
|
|
2445
|
+
function getGroupableIncludesValue(query) {
|
|
2446
|
+
switch (query.target) {
|
|
2447
|
+
case "string":
|
|
2448
|
+
case "title":
|
|
2449
|
+
case "description":
|
|
2450
|
+
case "image":
|
|
2451
|
+
case "periods":
|
|
2452
|
+
case "bibliography": return query.value;
|
|
2453
|
+
case "property":
|
|
2454
|
+
if (query.dataType !== "string" || query.propertyValues?.length !== 1) return null;
|
|
2455
|
+
return query.propertyValues[0] ?? null;
|
|
2456
|
+
}
|
|
2457
|
+
}
|
|
2458
|
+
function isCompatibleIncludesGroupQuery(params) {
|
|
2459
|
+
const { query, value, isCaseSensitive, language, version } = params;
|
|
2460
|
+
if (version !== 2 || query.matchMode !== "includes" || query.isNegated === true) return false;
|
|
2461
|
+
const queryValue = getGroupableIncludesValue(query);
|
|
2462
|
+
return queryValue != null && queryValue === value && query.isCaseSensitive === isCaseSensitive && query.language === language;
|
|
2463
|
+
}
|
|
2464
|
+
function buildContentTargetIncludesGroupMember(params) {
|
|
2465
|
+
const { contentNodesExpression, value, isCaseSensitive, language, containerElementName } = params;
|
|
2466
|
+
return {
|
|
2467
|
+
rawPredicate: buildRawStringMatchPredicate({
|
|
2468
|
+
valueExpression: buildFlattenedContentValuesExpression(contentNodesExpression),
|
|
2469
|
+
value,
|
|
2470
|
+
matchMode: "includes",
|
|
2471
|
+
isCaseSensitive
|
|
2472
|
+
}),
|
|
2473
|
+
buildCandidateTermQuery: (termExpression) => buildContentTargetCandidateBranch({
|
|
2474
|
+
containerElementName,
|
|
2475
|
+
termExpression,
|
|
2476
|
+
isCaseSensitive,
|
|
2477
|
+
language
|
|
2478
|
+
})
|
|
2479
|
+
};
|
|
2480
|
+
}
|
|
2481
|
+
function buildItemStringIncludesGroupMember(query) {
|
|
2482
|
+
return {
|
|
2483
|
+
rawPredicate: buildCombinedRawStringMatchPredicate({
|
|
2484
|
+
valueExpressions: buildItemStringSearchPaths(query.language),
|
|
2485
|
+
value: query.value,
|
|
2486
|
+
matchMode: "includes",
|
|
2487
|
+
isCaseSensitive: query.isCaseSensitive
|
|
2488
|
+
}),
|
|
2489
|
+
buildCandidateTermQuery: (termExpression) => `cts:or-query((
|
|
2490
|
+
${buildItemStringIdentificationBranch({
|
|
2491
|
+
termExpression,
|
|
2492
|
+
isCaseSensitive: query.isCaseSensitive,
|
|
2493
|
+
language: query.language
|
|
2494
|
+
})},
|
|
2495
|
+
${buildItemStringPropertyValueBranch({
|
|
2496
|
+
termExpression,
|
|
2497
|
+
isCaseSensitive: query.isCaseSensitive,
|
|
2498
|
+
language: query.language
|
|
2499
|
+
})}
|
|
2500
|
+
))`
|
|
2501
|
+
};
|
|
2502
|
+
}
|
|
2503
|
+
function buildPropertyStringIncludesGroupMember(query) {
|
|
2504
|
+
const propertyVariables = query.propertyVariables ?? [];
|
|
2505
|
+
const predicateParts = [];
|
|
2506
|
+
const valueExpression = buildFlattenedContentValuesExpression(`value[not(@inherited="true")]/content[@xml:lang="${query.language}"]`);
|
|
2507
|
+
const propertyValue = query.propertyValues[0] ?? "";
|
|
2508
|
+
if (propertyVariables.length > 0) predicateParts.push(buildPropertyLabelPredicate(propertyVariables));
|
|
2509
|
+
return {
|
|
2510
|
+
rawPredicate: `.//properties//property[${buildAndPredicate([...predicateParts, buildRawStringMatchPredicate({
|
|
2511
|
+
valueExpression,
|
|
2512
|
+
value: propertyValue,
|
|
2513
|
+
matchMode: "includes",
|
|
2514
|
+
isCaseSensitive: query.isCaseSensitive
|
|
2515
|
+
})])}]`,
|
|
2516
|
+
buildCandidateTermQuery: (termExpression) => buildPropertyStringCandidateBranch({
|
|
2517
|
+
termExpression,
|
|
2518
|
+
isCaseSensitive: query.isCaseSensitive,
|
|
2519
|
+
language: query.language
|
|
2520
|
+
})
|
|
2521
|
+
};
|
|
2522
|
+
}
|
|
2523
|
+
function buildIncludesGroupMember(query) {
|
|
2524
|
+
switch (query.target) {
|
|
2525
|
+
case "string": return buildItemStringIncludesGroupMember(query);
|
|
2526
|
+
case "title": return buildContentTargetIncludesGroupMember({
|
|
2527
|
+
contentNodesExpression: `identification/label/content[@xml:lang="${query.language}"]`,
|
|
2528
|
+
value: query.value,
|
|
2529
|
+
isCaseSensitive: query.isCaseSensitive,
|
|
2530
|
+
language: query.language,
|
|
2531
|
+
containerElementName: "identification"
|
|
2532
|
+
});
|
|
2533
|
+
case "description": return buildContentTargetIncludesGroupMember({
|
|
2534
|
+
contentNodesExpression: `description/content[@xml:lang="${query.language}"]`,
|
|
2535
|
+
value: query.value,
|
|
2536
|
+
isCaseSensitive: query.isCaseSensitive,
|
|
2537
|
+
language: query.language,
|
|
2538
|
+
containerElementName: "description"
|
|
2539
|
+
});
|
|
2540
|
+
case "periods": return buildContentTargetIncludesGroupMember({
|
|
2541
|
+
contentNodesExpression: `periods/period/identification/label/content[@xml:lang="${query.language}"]`,
|
|
2542
|
+
value: query.value,
|
|
2543
|
+
isCaseSensitive: query.isCaseSensitive,
|
|
2544
|
+
language: query.language,
|
|
2545
|
+
containerElementName: "period"
|
|
2546
|
+
});
|
|
2547
|
+
case "bibliography": return buildContentTargetIncludesGroupMember({
|
|
2548
|
+
contentNodesExpression: `bibliographies/bibliography/identification/label/content[@xml:lang="${query.language}"]`,
|
|
2549
|
+
value: query.value,
|
|
2550
|
+
isCaseSensitive: query.isCaseSensitive,
|
|
2551
|
+
language: query.language,
|
|
2552
|
+
containerElementName: "bibliography"
|
|
2553
|
+
});
|
|
2554
|
+
case "image": return buildContentTargetIncludesGroupMember({
|
|
2555
|
+
contentNodesExpression: `image/identification/label/content[@xml:lang="${query.language}"]`,
|
|
2556
|
+
value: query.value,
|
|
2557
|
+
isCaseSensitive: query.isCaseSensitive,
|
|
2558
|
+
language: query.language,
|
|
2559
|
+
containerElementName: "image"
|
|
2560
|
+
});
|
|
2561
|
+
case "property": return buildPropertyStringIncludesGroupMember(query);
|
|
2562
|
+
}
|
|
2563
|
+
}
|
|
2564
|
+
function findCompatibleIncludesGroup(params) {
|
|
2565
|
+
const { queries, startIndex, version } = params;
|
|
2566
|
+
const startQuery = queries[startIndex];
|
|
2567
|
+
if (startQuery == null) return null;
|
|
2568
|
+
const groupValue = getGroupableIncludesValue(startQuery);
|
|
2569
|
+
if (groupValue == null || !isCompatibleIncludesGroupQuery({
|
|
2570
|
+
query: startQuery,
|
|
2571
|
+
value: groupValue,
|
|
2572
|
+
isCaseSensitive: startQuery.isCaseSensitive,
|
|
2573
|
+
language: startQuery.language,
|
|
2574
|
+
version
|
|
2575
|
+
}) || startIndex > 0 && queries[startIndex]?.operator === "AND") return null;
|
|
2576
|
+
const groupedQueries = [startQuery];
|
|
2577
|
+
for (let index = startIndex + 1; index < queries.length; index += 1) {
|
|
2578
|
+
const query = queries[index];
|
|
2579
|
+
if (query?.operator !== "OR" || !isCompatibleIncludesGroupQuery({
|
|
2580
|
+
query,
|
|
2581
|
+
value: groupValue,
|
|
2582
|
+
isCaseSensitive: startQuery.isCaseSensitive,
|
|
2583
|
+
language: startQuery.language,
|
|
2584
|
+
version
|
|
2585
|
+
})) break;
|
|
2586
|
+
groupedQueries.push(query);
|
|
2587
|
+
}
|
|
2588
|
+
return groupedQueries.length > 1 ? groupedQueries : null;
|
|
2589
|
+
}
|
|
2590
|
+
function buildIncludesGroupClause(params) {
|
|
2591
|
+
const { queries, queryKey } = params;
|
|
2592
|
+
const firstQuery = queries[0];
|
|
2593
|
+
if (firstQuery == null) throw new Error("Cannot build an includes group without queries");
|
|
2594
|
+
const groupValue = getGroupableIncludesValue(firstQuery);
|
|
2595
|
+
if (groupValue == null) throw new Error("Cannot build an includes group without a search value");
|
|
2596
|
+
const members = queries.map((query) => buildIncludesGroupMember(query));
|
|
2597
|
+
const tokenizedSearchDeclarations = buildTokenizedSearchDeclarations({
|
|
2598
|
+
value: groupValue,
|
|
2599
|
+
isCaseSensitive: firstQuery.isCaseSensitive,
|
|
2322
2600
|
queryKey
|
|
2323
2601
|
});
|
|
2602
|
+
const tokenizedTerms = tokenizeIncludesSearchValue({
|
|
2603
|
+
value: groupValue,
|
|
2604
|
+
isCaseSensitive: firstQuery.isCaseSensitive
|
|
2605
|
+
});
|
|
2324
2606
|
const termQueriesVar = `$query${queryKey}TermQueries`;
|
|
2325
|
-
const
|
|
2607
|
+
const candidateQueryVar = `$query${queryKey}CandidateQuery`;
|
|
2608
|
+
if (tokenizedTerms.length === 0) return {
|
|
2609
|
+
declarations: [],
|
|
2610
|
+
predicate: buildOrPredicate(members.map((member) => member.rawPredicate)),
|
|
2611
|
+
candidateQueryVar: null
|
|
2612
|
+
};
|
|
2326
2613
|
return {
|
|
2327
2614
|
declarations: [
|
|
2328
2615
|
...tokenizedSearchDeclarations.declarations,
|
|
2329
2616
|
`let ${termQueriesVar} :=
|
|
2330
2617
|
for $term in ${tokenizedSearchDeclarations.termsVar}
|
|
2331
|
-
return
|
|
2332
|
-
|
|
2333
|
-
${buildItemStringIdentificationBranch({
|
|
2334
|
-
termExpression: "$term",
|
|
2335
|
-
isCaseSensitive: query.isCaseSensitive,
|
|
2336
|
-
language: query.language
|
|
2337
|
-
})},
|
|
2338
|
-
${buildItemStringPropertyValueBranch({
|
|
2339
|
-
termExpression: "$term",
|
|
2340
|
-
isCaseSensitive: query.isCaseSensitive,
|
|
2341
|
-
language: query.language
|
|
2342
|
-
})}
|
|
2343
|
-
))`,
|
|
2344
|
-
`let ${ctsQueryVar} :=
|
|
2618
|
+
return ${buildOrCtsQueryExpression(members.map((member) => member.buildCandidateTermQuery("$term")))}`,
|
|
2619
|
+
`let ${candidateQueryVar} :=
|
|
2345
2620
|
if (count(${tokenizedSearchDeclarations.termsVar}) = 1)
|
|
2346
2621
|
then ${termQueriesVar}[1]
|
|
2347
2622
|
else if (count(${tokenizedSearchDeclarations.termsVar}) gt 1)
|
|
2348
2623
|
then cts:and-query(${termQueriesVar})
|
|
2349
2624
|
else ()`
|
|
2350
2625
|
],
|
|
2351
|
-
predicate: `
|
|
2626
|
+
predicate: `cts:contains(., ${candidateQueryVar})`,
|
|
2627
|
+
candidateQueryVar
|
|
2352
2628
|
};
|
|
2353
2629
|
}
|
|
2354
2630
|
/**
|
|
2355
2631
|
* Build a string match predicate for an XQuery string.
|
|
2356
2632
|
*/
|
|
2357
|
-
function
|
|
2358
|
-
const { contentNodesExpression, value, matchMode, isCaseSensitive, version, queryKey } = params;
|
|
2633
|
+
function buildStringMatchClause(params) {
|
|
2634
|
+
const { contentNodesExpression, value, matchMode, isCaseSensitive, version, queryKey, buildCandidateTermQuery } = params;
|
|
2359
2635
|
const valueExpression = buildFlattenedContentValuesExpression(contentNodesExpression);
|
|
2360
|
-
if (matchMode === "includes" && version === 2) return
|
|
2361
|
-
contentNodesExpression,
|
|
2362
|
-
valueExpression,
|
|
2636
|
+
if (matchMode === "includes" && version === 2 && buildCandidateTermQuery != null) return buildCtsIncludesPredicate({
|
|
2637
|
+
searchableNodesExpression: buildSearchableContentNodesExpression(contentNodesExpression),
|
|
2638
|
+
fallbackValueExpression: valueExpression,
|
|
2363
2639
|
value,
|
|
2364
2640
|
isCaseSensitive,
|
|
2365
|
-
queryKey
|
|
2641
|
+
queryKey,
|
|
2642
|
+
buildCandidateTermQuery
|
|
2366
2643
|
});
|
|
2367
2644
|
return {
|
|
2368
2645
|
declarations: [],
|
|
@@ -2371,70 +2648,67 @@ function buildStringMatchPredicate(params) {
|
|
|
2371
2648
|
value,
|
|
2372
2649
|
matchMode,
|
|
2373
2650
|
isCaseSensitive
|
|
2374
|
-
})
|
|
2651
|
+
}),
|
|
2652
|
+
candidateQueryVar: null
|
|
2375
2653
|
};
|
|
2376
2654
|
}
|
|
2377
|
-
function buildOrPredicate(predicates) {
|
|
2378
|
-
if (predicates.length === 1) return predicates[0] ?? "false()";
|
|
2379
|
-
return `(${predicates.join(" or ")})`;
|
|
2380
|
-
}
|
|
2381
|
-
function buildAndPredicate(predicates) {
|
|
2382
|
-
if (predicates.length === 1) return predicates[0] ?? "false()";
|
|
2383
|
-
return `(${predicates.join(" and ")})`;
|
|
2384
|
-
}
|
|
2385
|
-
/**
|
|
2386
|
-
* Build a date/dateTime range predicate for an XQuery string.
|
|
2387
|
-
*/
|
|
2388
|
-
function buildDateRangePredicate(params) {
|
|
2389
|
-
const { from, to } = params;
|
|
2390
|
-
const conditions = [];
|
|
2391
|
-
if (from != null) conditions.push(`(value/@rawValue ge ${stringLiteral(from)})`);
|
|
2392
|
-
if (to != null) conditions.push(`(value/@rawValue le ${stringLiteral(to)})`);
|
|
2393
|
-
return conditions.join(" and ");
|
|
2394
|
-
}
|
|
2395
|
-
/**
|
|
2396
|
-
* Build a property label predicate for an XQuery string.
|
|
2397
|
-
*/
|
|
2398
|
-
function buildPropertyLabelPredicate(propertyVariables) {
|
|
2399
|
-
const labelPredicates = [];
|
|
2400
|
-
for (const propertyVariable of propertyVariables) labelPredicates.push(`@uuid=${stringLiteral(propertyVariable)}`);
|
|
2401
|
-
return `label[${buildOrPredicate(labelPredicates)}]`;
|
|
2402
|
-
}
|
|
2403
2655
|
function buildPropertyValueAttributePredicate(params) {
|
|
2404
2656
|
const { propertyValues, attributeName } = params;
|
|
2405
2657
|
const valuePredicates = [];
|
|
2406
2658
|
for (const propertyValue of propertyValues) valuePredicates.push(`value[@${attributeName}=${stringLiteral(propertyValue)}]`);
|
|
2407
2659
|
return buildOrPredicate(valuePredicates);
|
|
2408
2660
|
}
|
|
2409
|
-
function
|
|
2661
|
+
function buildPropertyStringValueClause(params) {
|
|
2410
2662
|
const { query, version, queryKey } = params;
|
|
2411
2663
|
const propertyContentNodesExpression = query.matchMode === "includes" && version === 2 ? `value[not(@inherited="true")]/content[@xml:lang="${query.language}"]` : `value/content[@xml:lang="${query.language}"]`;
|
|
2412
2664
|
const declarations = [];
|
|
2413
2665
|
const valuePredicates = [];
|
|
2666
|
+
const candidateQueryVars = [];
|
|
2414
2667
|
for (const [propertyValueIndex, propertyValue] of query.propertyValues.entries()) {
|
|
2415
|
-
const
|
|
2668
|
+
const compiledStringClause = buildStringMatchClause({
|
|
2416
2669
|
contentNodesExpression: propertyContentNodesExpression,
|
|
2417
2670
|
value: propertyValue,
|
|
2418
2671
|
matchMode: query.matchMode,
|
|
2419
2672
|
isCaseSensitive: query.isCaseSensitive,
|
|
2420
2673
|
version,
|
|
2421
|
-
queryKey: `${queryKey}_${propertyValueIndex + 1}
|
|
2674
|
+
queryKey: `${queryKey}_${propertyValueIndex + 1}`,
|
|
2675
|
+
buildCandidateTermQuery: (termExpression) => buildPropertyStringCandidateBranch({
|
|
2676
|
+
termExpression,
|
|
2677
|
+
isCaseSensitive: query.isCaseSensitive,
|
|
2678
|
+
language: query.language
|
|
2679
|
+
})
|
|
2422
2680
|
});
|
|
2423
|
-
declarations.push(...
|
|
2424
|
-
valuePredicates.push(
|
|
2681
|
+
declarations.push(...compiledStringClause.declarations);
|
|
2682
|
+
valuePredicates.push(compiledStringClause.predicate);
|
|
2683
|
+
if (compiledStringClause.candidateQueryVar != null) candidateQueryVars.push(compiledStringClause.candidateQueryVar);
|
|
2684
|
+
}
|
|
2685
|
+
let candidateQueryVar = null;
|
|
2686
|
+
if (candidateQueryVars.length > 0) {
|
|
2687
|
+
const candidateQueriesExpression = `(${candidateQueryVars.join(", ")})`;
|
|
2688
|
+
candidateQueryVar = `$query${queryKey}CandidateQuery`;
|
|
2689
|
+
declarations.push(`let ${candidateQueryVar} :=
|
|
2690
|
+
if (count(${candidateQueriesExpression}) = 1)
|
|
2691
|
+
then ${candidateQueriesExpression}[1]
|
|
2692
|
+
else if (count(${candidateQueriesExpression}) gt 1)
|
|
2693
|
+
then cts:or-query(${candidateQueriesExpression})
|
|
2694
|
+
else ()`);
|
|
2425
2695
|
}
|
|
2426
2696
|
return {
|
|
2427
2697
|
declarations,
|
|
2428
|
-
predicate: buildOrPredicate(valuePredicates)
|
|
2698
|
+
predicate: buildOrPredicate(valuePredicates),
|
|
2699
|
+
candidateQueryVar
|
|
2429
2700
|
};
|
|
2430
2701
|
}
|
|
2431
2702
|
/**
|
|
2432
2703
|
* Build a property predicate for an XQuery string.
|
|
2433
2704
|
*/
|
|
2434
|
-
function
|
|
2705
|
+
function buildPropertyClause(params) {
|
|
2435
2706
|
const { query, version, queryKey } = params;
|
|
2436
|
-
const predicateParts = [
|
|
2707
|
+
const predicateParts = [];
|
|
2437
2708
|
const declarations = [];
|
|
2709
|
+
const propertyVariables = query.propertyVariables ?? [];
|
|
2710
|
+
let candidateQueryVar = null;
|
|
2711
|
+
if (propertyVariables.length > 0) predicateParts.push(buildPropertyLabelPredicate(propertyVariables));
|
|
2438
2712
|
if (query.dataType === "date" || query.dataType === "dateTime") predicateParts.push(buildDateRangePredicate({
|
|
2439
2713
|
from: query.from,
|
|
2440
2714
|
to: query.to
|
|
@@ -2456,73 +2730,105 @@ function buildPropertyPredicate(params) {
|
|
|
2456
2730
|
}));
|
|
2457
2731
|
break;
|
|
2458
2732
|
case "string": {
|
|
2459
|
-
const
|
|
2733
|
+
const compiledStringClause = buildPropertyStringValueClause({
|
|
2460
2734
|
query,
|
|
2461
2735
|
version,
|
|
2462
2736
|
queryKey
|
|
2463
2737
|
});
|
|
2464
|
-
declarations.push(...
|
|
2465
|
-
predicateParts.push(
|
|
2738
|
+
declarations.push(...compiledStringClause.declarations);
|
|
2739
|
+
predicateParts.push(compiledStringClause.predicate);
|
|
2740
|
+
candidateQueryVar = compiledStringClause.candidateQueryVar;
|
|
2466
2741
|
break;
|
|
2467
2742
|
}
|
|
2468
2743
|
}
|
|
2469
2744
|
return {
|
|
2470
2745
|
declarations,
|
|
2471
|
-
predicate: `.//properties//property[${buildAndPredicate(predicateParts)}]
|
|
2746
|
+
predicate: `.//properties//property[${buildAndPredicate(predicateParts)}]`,
|
|
2747
|
+
candidateQueryVar
|
|
2472
2748
|
};
|
|
2473
2749
|
}
|
|
2474
2750
|
/**
|
|
2475
2751
|
* Build a query predicate for an XQuery string.
|
|
2476
2752
|
*/
|
|
2477
|
-
function
|
|
2753
|
+
function buildQueryClause(params) {
|
|
2478
2754
|
const { query, version, queryKey } = params;
|
|
2479
2755
|
switch (query.target) {
|
|
2480
|
-
case "string": return
|
|
2756
|
+
case "string": return buildItemStringSearchClause({
|
|
2481
2757
|
query,
|
|
2482
2758
|
version,
|
|
2483
2759
|
queryKey
|
|
2484
2760
|
});
|
|
2485
|
-
case "title": return
|
|
2761
|
+
case "title": return buildStringMatchClause({
|
|
2486
2762
|
contentNodesExpression: `identification/label/content[@xml:lang="${query.language}"]`,
|
|
2487
2763
|
value: query.value,
|
|
2488
2764
|
matchMode: query.matchMode,
|
|
2489
2765
|
isCaseSensitive: query.isCaseSensitive,
|
|
2490
2766
|
version,
|
|
2491
|
-
queryKey
|
|
2767
|
+
queryKey,
|
|
2768
|
+
buildCandidateTermQuery: (termExpression) => buildContentTargetCandidateBranch({
|
|
2769
|
+
containerElementName: "identification",
|
|
2770
|
+
termExpression,
|
|
2771
|
+
isCaseSensitive: query.isCaseSensitive,
|
|
2772
|
+
language: query.language
|
|
2773
|
+
})
|
|
2492
2774
|
});
|
|
2493
|
-
case "description": return
|
|
2775
|
+
case "description": return buildStringMatchClause({
|
|
2494
2776
|
contentNodesExpression: `description/content[@xml:lang="${query.language}"]`,
|
|
2495
2777
|
value: query.value,
|
|
2496
2778
|
matchMode: query.matchMode,
|
|
2497
2779
|
isCaseSensitive: query.isCaseSensitive,
|
|
2498
2780
|
version,
|
|
2499
|
-
queryKey
|
|
2781
|
+
queryKey,
|
|
2782
|
+
buildCandidateTermQuery: (termExpression) => buildContentTargetCandidateBranch({
|
|
2783
|
+
containerElementName: "description",
|
|
2784
|
+
termExpression,
|
|
2785
|
+
isCaseSensitive: query.isCaseSensitive,
|
|
2786
|
+
language: query.language
|
|
2787
|
+
})
|
|
2500
2788
|
});
|
|
2501
|
-
case "periods": return
|
|
2789
|
+
case "periods": return buildStringMatchClause({
|
|
2502
2790
|
contentNodesExpression: `periods/period/identification/label/content[@xml:lang="${query.language}"]`,
|
|
2503
2791
|
value: query.value,
|
|
2504
2792
|
matchMode: query.matchMode,
|
|
2505
2793
|
isCaseSensitive: query.isCaseSensitive,
|
|
2506
2794
|
version,
|
|
2507
|
-
queryKey
|
|
2795
|
+
queryKey,
|
|
2796
|
+
buildCandidateTermQuery: (termExpression) => buildContentTargetCandidateBranch({
|
|
2797
|
+
containerElementName: "period",
|
|
2798
|
+
termExpression,
|
|
2799
|
+
isCaseSensitive: query.isCaseSensitive,
|
|
2800
|
+
language: query.language
|
|
2801
|
+
})
|
|
2508
2802
|
});
|
|
2509
|
-
case "bibliography": return
|
|
2803
|
+
case "bibliography": return buildStringMatchClause({
|
|
2510
2804
|
contentNodesExpression: `bibliographies/bibliography/identification/label/content[@xml:lang="${query.language}"]`,
|
|
2511
2805
|
value: query.value,
|
|
2512
2806
|
matchMode: query.matchMode,
|
|
2513
2807
|
isCaseSensitive: query.isCaseSensitive,
|
|
2514
2808
|
version,
|
|
2515
|
-
queryKey
|
|
2809
|
+
queryKey,
|
|
2810
|
+
buildCandidateTermQuery: (termExpression) => buildContentTargetCandidateBranch({
|
|
2811
|
+
containerElementName: "bibliography",
|
|
2812
|
+
termExpression,
|
|
2813
|
+
isCaseSensitive: query.isCaseSensitive,
|
|
2814
|
+
language: query.language
|
|
2815
|
+
})
|
|
2516
2816
|
});
|
|
2517
|
-
case "image": return
|
|
2817
|
+
case "image": return buildStringMatchClause({
|
|
2518
2818
|
contentNodesExpression: `image/identification/label/content[@xml:lang="${query.language}"]`,
|
|
2519
2819
|
value: query.value,
|
|
2520
2820
|
matchMode: query.matchMode,
|
|
2521
2821
|
isCaseSensitive: query.isCaseSensitive,
|
|
2522
2822
|
version,
|
|
2523
|
-
queryKey
|
|
2823
|
+
queryKey,
|
|
2824
|
+
buildCandidateTermQuery: (termExpression) => buildContentTargetCandidateBranch({
|
|
2825
|
+
containerElementName: "image",
|
|
2826
|
+
termExpression,
|
|
2827
|
+
isCaseSensitive: query.isCaseSensitive,
|
|
2828
|
+
language: query.language
|
|
2829
|
+
})
|
|
2524
2830
|
});
|
|
2525
|
-
case "property": return
|
|
2831
|
+
case "property": return buildPropertyClause({
|
|
2526
2832
|
query,
|
|
2527
2833
|
version,
|
|
2528
2834
|
queryKey
|
|
@@ -2534,27 +2840,36 @@ function buildQueryPredicate(params) {
|
|
|
2534
2840
|
*/
|
|
2535
2841
|
function buildBooleanQueryClause(params) {
|
|
2536
2842
|
const { query, version, queryKey } = params;
|
|
2537
|
-
const
|
|
2843
|
+
const compiledQueryClause = buildQueryClause({
|
|
2538
2844
|
query,
|
|
2539
2845
|
version,
|
|
2540
2846
|
queryKey
|
|
2541
2847
|
});
|
|
2542
|
-
const baseClause = `(${
|
|
2848
|
+
const baseClause = `(${compiledQueryClause.predicate})`;
|
|
2543
2849
|
return {
|
|
2544
|
-
declarations:
|
|
2545
|
-
predicate: query.isNegated ? `not(${baseClause})` : baseClause
|
|
2850
|
+
declarations: compiledQueryClause.declarations,
|
|
2851
|
+
predicate: query.isNegated ? `not(${baseClause})` : baseClause,
|
|
2852
|
+
candidateQueryVar: query.isNegated ? null : compiledQueryClause.candidateQueryVar
|
|
2546
2853
|
};
|
|
2547
2854
|
}
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
*/
|
|
2551
|
-
function buildQueryFilters(params) {
|
|
2552
|
-
const { queries, version } = params;
|
|
2855
|
+
function buildQueryPlan(params) {
|
|
2856
|
+
const { queries, version, baseItemsExpression } = params;
|
|
2553
2857
|
const declarations = [];
|
|
2554
2858
|
const predicateParts = [];
|
|
2859
|
+
const candidateQueryVars = [];
|
|
2555
2860
|
let hasCtsIncludesClauses = false;
|
|
2556
|
-
for (
|
|
2557
|
-
const
|
|
2861
|
+
for (let index = 0; index < queries.length;) {
|
|
2862
|
+
const groupedQueries = findCompatibleIncludesGroup({
|
|
2863
|
+
queries,
|
|
2864
|
+
startIndex: index,
|
|
2865
|
+
version
|
|
2866
|
+
});
|
|
2867
|
+
const query = queries[index];
|
|
2868
|
+
if (query == null) break;
|
|
2869
|
+
const compiledClause = groupedQueries != null ? buildIncludesGroupClause({
|
|
2870
|
+
queries: groupedQueries,
|
|
2871
|
+
queryKey: `${index + 1}`
|
|
2872
|
+
}) : buildBooleanQueryClause({
|
|
2558
2873
|
query,
|
|
2559
2874
|
version,
|
|
2560
2875
|
queryKey: `${index + 1}`
|
|
@@ -2563,15 +2878,33 @@ function buildQueryFilters(params) {
|
|
|
2563
2878
|
hasCtsIncludesClauses = true;
|
|
2564
2879
|
declarations.push(...compiledClause.declarations);
|
|
2565
2880
|
}
|
|
2881
|
+
if (compiledClause.candidateQueryVar != null) candidateQueryVars.push(compiledClause.candidateQueryVar);
|
|
2566
2882
|
if (index === 0) {
|
|
2567
2883
|
predicateParts.push(compiledClause.predicate);
|
|
2884
|
+
index += groupedQueries?.length ?? 1;
|
|
2568
2885
|
continue;
|
|
2569
2886
|
}
|
|
2570
2887
|
predicateParts.push(`${query.operator === "AND" ? "and" : "or"} ${compiledClause.predicate}`);
|
|
2888
|
+
index += groupedQueries?.length ?? 1;
|
|
2571
2889
|
}
|
|
2572
2890
|
if (hasCtsIncludesClauses) declarations.unshift(`let ${CTS_INCLUDES_STOP_WORDS_VAR} := (${CTS_INCLUDES_STOP_WORDS.map((stopWord) => stringLiteral(stopWord)).join(", ")})`);
|
|
2891
|
+
let itemsExpression = `(${baseItemsExpression})`;
|
|
2892
|
+
if (candidateQueryVars.length > 0) {
|
|
2893
|
+
const candidateQueriesExpression = `(${candidateQueryVars.join(", ")})`;
|
|
2894
|
+
const candidateItemsQueryVar = "$candidateItemsQuery";
|
|
2895
|
+
declarations.push(`let ${candidateItemsQueryVar} :=
|
|
2896
|
+
if (count(${candidateQueriesExpression}) = 1)
|
|
2897
|
+
then ${candidateQueriesExpression}[1]
|
|
2898
|
+
else if (count(${candidateQueriesExpression}) gt 1)
|
|
2899
|
+
then cts:or-query(${candidateQueriesExpression})
|
|
2900
|
+
else ()`);
|
|
2901
|
+
itemsExpression = `(if (exists(${candidateItemsQueryVar}))
|
|
2902
|
+
then cts:search(${baseItemsExpression}, ${candidateItemsQueryVar})
|
|
2903
|
+
else ${baseItemsExpression})`;
|
|
2904
|
+
}
|
|
2573
2905
|
return {
|
|
2574
2906
|
declarations,
|
|
2907
|
+
itemsExpression,
|
|
2575
2908
|
predicate: predicateParts.join(" ")
|
|
2576
2909
|
};
|
|
2577
2910
|
}
|
|
@@ -2667,22 +3000,21 @@ function buildXQuery$1(params, options) {
|
|
|
2667
3000
|
const startPosition = (page - 1) * pageSize + 1;
|
|
2668
3001
|
const endPosition = page * pageSize;
|
|
2669
3002
|
const setScopeFilter = `/set[(${setScopeUuids.map((uuid) => `@uuid="${uuid}"`).join(" or ")})]/items/*`;
|
|
2670
|
-
const
|
|
3003
|
+
const compiledQueryPlan = buildQueryPlan({
|
|
2671
3004
|
queries,
|
|
2672
|
-
version
|
|
3005
|
+
version,
|
|
3006
|
+
baseItemsExpression: `${version === 2 ? "doc()" : "input()"}/ochre${setScopeFilter}`
|
|
2673
3007
|
});
|
|
2674
|
-
const queryFilterDeclarations =
|
|
3008
|
+
const queryFilterDeclarations = compiledQueryPlan.declarations.length > 0 ? `${compiledQueryPlan.declarations.join("\n")}\n\n` : "";
|
|
2675
3009
|
const filterPredicates = [];
|
|
2676
3010
|
if (belongsToCollectionScopeUuids.length > 0) {
|
|
2677
3011
|
const belongsToCollectionScopeValues = belongsToCollectionScopeUuids.map((uuid) => `@uuid="${uuid}"`).join(" or ");
|
|
2678
3012
|
filterPredicates.push(`.//properties[property[label/@uuid="${BELONGS_TO_COLLECTION_UUID}" and value/(${belongsToCollectionScopeValues})]]`);
|
|
2679
3013
|
}
|
|
2680
|
-
if (
|
|
3014
|
+
if (compiledQueryPlan.predicate.length > 0) filterPredicates.push(`(${compiledQueryPlan.predicate})`);
|
|
2681
3015
|
const itemFilters = filterPredicates.length > 0 ? `[${filterPredicates.join(" and ")}]` : "";
|
|
2682
3016
|
const orderedItemsClause = buildOrderedItemsClause(sort);
|
|
2683
|
-
return `<ochre>{${`${queryFilterDeclarations}let $items := ${
|
|
2684
|
-
${setScopeFilter}
|
|
2685
|
-
${itemFilters}
|
|
3017
|
+
return `<ochre>{${`${queryFilterDeclarations}let $items := ${compiledQueryPlan.itemsExpression}${itemFilters}
|
|
2686
3018
|
|
|
2687
3019
|
let $totalCount := count($items)
|
|
2688
3020
|
${orderedItemsClause}
|
|
@@ -2883,7 +3215,7 @@ function getPropertyVariableUuidsFromQueries(queries) {
|
|
|
2883
3215
|
const propertyVariableUuids = /* @__PURE__ */ new Set();
|
|
2884
3216
|
for (const query of queries) {
|
|
2885
3217
|
if (query.target !== "property") continue;
|
|
2886
|
-
for (const propertyVariableUuid of query.propertyVariables) propertyVariableUuids.add(propertyVariableUuid);
|
|
3218
|
+
for (const propertyVariableUuid of query.propertyVariables ?? []) propertyVariableUuids.add(propertyVariableUuid);
|
|
2887
3219
|
}
|
|
2888
3220
|
return [...propertyVariableUuids];
|
|
2889
3221
|
}
|
|
@@ -2970,17 +3302,18 @@ function buildXQuery(params, options) {
|
|
|
2970
3302
|
let setScopeFilter = "/set/items/*";
|
|
2971
3303
|
if (setScopeUuids.length > 0) setScopeFilter = `/set[(${setScopeUuids.map((uuid) => `@uuid="${uuid}"`).join(" or ")})]/items/*`;
|
|
2972
3304
|
const propertyVariableFilters = getPropertyVariableUuidsFromQueries(queries).map((uuid) => `@uuid="${uuid}"`).join(" or ");
|
|
2973
|
-
const
|
|
3305
|
+
const compiledQueryPlan = buildQueryPlan({
|
|
2974
3306
|
queries,
|
|
2975
|
-
version
|
|
3307
|
+
version,
|
|
3308
|
+
baseItemsExpression: `${version === 2 ? "doc()" : "input()"}/ochre${setScopeFilter}`
|
|
2976
3309
|
});
|
|
2977
|
-
const queryFilterDeclarations =
|
|
3310
|
+
const queryFilterDeclarations = compiledQueryPlan.declarations.length > 0 ? `${compiledQueryPlan.declarations.join("\n")}\n\n` : "";
|
|
2978
3311
|
const filterPredicates = [];
|
|
2979
3312
|
if (belongsToCollectionScopeUuids.length > 0) {
|
|
2980
3313
|
const belongsToCollectionScopeValues = belongsToCollectionScopeUuids.map((uuid) => `@uuid="${uuid}"`).join(" or ");
|
|
2981
3314
|
filterPredicates.push(`.//properties[property[label/@uuid="${BELONGS_TO_COLLECTION_UUID}" and value[${belongsToCollectionScopeValues}]]]`);
|
|
2982
3315
|
}
|
|
2983
|
-
if (
|
|
3316
|
+
if (compiledQueryPlan.predicate.length > 0) filterPredicates.push(`(${compiledQueryPlan.predicate})`);
|
|
2984
3317
|
const itemFilters = filterPredicates.length > 0 ? `[${filterPredicates.join(" and ")}]` : "";
|
|
2985
3318
|
const queryBlocks = [`let $matching-props := $items//property[label[${propertyVariableFilters}]]
|
|
2986
3319
|
|
|
@@ -3011,9 +3344,7 @@ let $property-values :=
|
|
|
3011
3344
|
return <attributeValue attributeType="periods" itemUuid="{$item/@uuid}" content="{$label}" />`);
|
|
3012
3345
|
returnedSequences.push("$period-values");
|
|
3013
3346
|
}
|
|
3014
|
-
return `<ochre>{${`${queryFilterDeclarations}let $items := ${
|
|
3015
|
-
${setScopeFilter}
|
|
3016
|
-
${itemFilters}
|
|
3347
|
+
return `<ochre>{${`${queryFilterDeclarations}let $items := ${compiledQueryPlan.itemsExpression}${itemFilters}
|
|
3017
3348
|
|
|
3018
3349
|
${queryBlocks.join("\n\n")}
|
|
3019
3350
|
|