ochre-sdk 0.21.4 → 0.21.6

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 CHANGED
@@ -670,14 +670,15 @@ type SetItemsSort = {
670
670
  direction?: SetItemsSortDirection;
671
671
  language?: string;
672
672
  };
673
+ type QueryPropertyDataType = Exclude<PropertyValueContentType, "coordinate"> | "all";
673
674
  /**
674
675
  * Represents a leaf query for Set items
675
676
  */
676
677
  type QueryLeaf = {
677
678
  target: "property";
678
679
  propertyVariable?: string;
679
- dataType: Exclude<Exclude<PropertyValueContentType, "coordinate">, "date" | "dateTime">;
680
- propertyValues?: Array<string>;
680
+ dataType: "all";
681
+ value: string;
681
682
  from?: never;
682
683
  to?: never;
683
684
  matchMode: "includes" | "exact";
@@ -686,10 +687,9 @@ type QueryLeaf = {
686
687
  isNegated?: boolean;
687
688
  } | {
688
689
  target: "property";
689
- propertyVariable: string;
690
- dataType: "date" | "dateTime";
691
- propertyValues?: never;
692
- value: string;
690
+ propertyVariable?: string;
691
+ dataType: Exclude<PropertyValueContentType, "coordinate">;
692
+ value?: string;
693
693
  from?: never;
694
694
  to?: never;
695
695
  matchMode: "includes" | "exact";
@@ -700,7 +700,6 @@ type QueryLeaf = {
700
700
  target: "property";
701
701
  propertyVariable: string;
702
702
  dataType: "date" | "dateTime";
703
- propertyValues?: never;
704
703
  value?: never;
705
704
  from: string;
706
705
  to?: string;
@@ -712,7 +711,6 @@ type QueryLeaf = {
712
711
  target: "property";
713
712
  propertyVariable: string;
714
713
  dataType: "date" | "dateTime";
715
- propertyValues?: never;
716
714
  value?: never;
717
715
  from?: string;
718
716
  to: string;
@@ -1530,4 +1528,4 @@ declare const DEFAULT_PAGE_SIZE = 48;
1530
1528
  */
1531
1529
  declare function flattenItemProperties<T extends DataCategory = DataCategory, U extends DataCategory | Array<DataCategory> = (T extends "tree" ? Exclude<DataCategory, "tree"> : T extends "set" ? Array<DataCategory> : never)>(item: Item<T, U>): Item<T, U>;
1532
1530
  //#endregion
1533
- export { ApiVersion, Bibliography, Concept, Context, ContextItem, ContextNode, Coordinate, DEFAULT_API_VERSION, DEFAULT_PAGE_SIZE, Data, DataCategory, Event, FileFormat, Gallery, Identification, Image, ImageMap, ImageMapArea, Interpretation, Item, LevelContext, LevelContextItem, License, Link, Metadata, Note, Observation, Period, Person, Property, PropertyContexts, PropertyValue, PropertyValueContent, PropertyValueContentType, PropertyValueQueryItem, PropertyVariable, Query, QueryGroup, QueryLeaf, Resource, Scope, Section, Set, SetAttributeValueQueryItem, SetItemsSort, SetItemsSortDirection, SpatialUnit, Style, StylesheetCategory, StylesheetItem, Text, Tree, WebBlock, WebBlockLayout, WebElement, WebElementComponent, WebImage, WebSegment, WebSegmentItem, WebTitle, Webpage, Website, WebsitePropertyQuery, WebsitePropertyQueryNode, WebsiteType, fetchGallery, fetchItem, fetchSetItems, fetchSetPropertyValues, fetchWebsite, filterProperties, flattenItemProperties, getLeafPropertyValues, getPropertyByLabel, getPropertyByLabelAndValue, getPropertyByLabelAndValues, getPropertyByUuid, getPropertyValueByLabel, getPropertyValueByUuid, getPropertyValuesByLabel, getPropertyValuesByUuid, getUniqueProperties, getUniquePropertyLabels };
1531
+ export { ApiVersion, Bibliography, Concept, Context, ContextItem, ContextNode, Coordinate, DEFAULT_API_VERSION, DEFAULT_PAGE_SIZE, Data, DataCategory, Event, FileFormat, Gallery, Identification, Image, ImageMap, ImageMapArea, Interpretation, Item, LevelContext, LevelContextItem, License, Link, Metadata, Note, Observation, Period, Person, Property, PropertyContexts, PropertyValue, PropertyValueContent, PropertyValueContentType, PropertyValueQueryItem, PropertyVariable, Query, QueryGroup, QueryLeaf, QueryPropertyDataType, Resource, Scope, Section, Set, SetAttributeValueQueryItem, SetItemsSort, SetItemsSortDirection, SpatialUnit, Style, StylesheetCategory, StylesheetItem, Text, Tree, WebBlock, WebBlockLayout, WebElement, WebElementComponent, WebImage, WebSegment, WebSegmentItem, WebTitle, Webpage, Website, WebsitePropertyQuery, WebsitePropertyQueryNode, WebsiteType, fetchGallery, fetchItem, fetchSetItems, fetchSetPropertyValues, fetchWebsite, filterProperties, flattenItemProperties, getLeafPropertyValues, getPropertyByLabel, getPropertyByLabelAndValue, getPropertyByLabelAndValues, getPropertyByUuid, getPropertyValueByLabel, getPropertyValueByUuid, getPropertyValuesByLabel, getPropertyValuesByUuid, getUniqueProperties, getUniquePropertyLabels };
package/dist/index.mjs CHANGED
@@ -757,6 +757,18 @@ const boundsSchema = z.string().transform((str, ctx) => {
757
757
  * @internal
758
758
  */
759
759
  const setQueryLeafSchema = z.union([
760
+ z.object({
761
+ target: z.literal("property"),
762
+ propertyVariable: uuidSchema.optional(),
763
+ dataType: z.literal("all"),
764
+ value: z.string(),
765
+ from: z.never().optional(),
766
+ to: z.never().optional(),
767
+ matchMode: z.enum(["includes", "exact"]),
768
+ isCaseSensitive: z.boolean(),
769
+ language: z.string().default("eng"),
770
+ isNegated: z.boolean().optional().default(false)
771
+ }).strict(),
760
772
  z.object({
761
773
  target: z.literal("property"),
762
774
  propertyVariable: uuidSchema.optional(),
@@ -765,32 +777,24 @@ const setQueryLeafSchema = z.union([
765
777
  "integer",
766
778
  "decimal",
767
779
  "boolean",
768
- "time",
769
- "IDREF"
780
+ "IDREF",
781
+ "date",
782
+ "dateTime",
783
+ "time"
770
784
  ]),
771
- propertyValues: z.array(z.string()).min(1, "At least one property value is required").optional(),
785
+ value: z.string().optional(),
786
+ from: z.never().optional(),
787
+ to: z.never().optional(),
772
788
  matchMode: z.enum(["includes", "exact"]),
773
789
  isCaseSensitive: z.boolean(),
774
790
  language: z.string().default("eng"),
775
791
  isNegated: z.boolean().optional().default(false)
776
792
  }).strict().superRefine((value, ctx) => {
777
- if (value.propertyVariable == null && value.propertyValues == null) ctx.addIssue({
793
+ if (value.propertyVariable == null && value.value == null) ctx.addIssue({
778
794
  code: "custom",
779
- message: "Property queries must include at least one propertyVariable or propertyValue"
795
+ message: "Property queries must include at least one propertyVariable or value"
780
796
  });
781
797
  }),
782
- z.object({
783
- target: z.literal("property"),
784
- propertyVariable: uuidSchema,
785
- dataType: z.enum(["date", "dateTime"]),
786
- value: z.string(),
787
- from: z.never().optional(),
788
- to: z.never().optional(),
789
- matchMode: z.enum(["includes", "exact"]),
790
- isCaseSensitive: z.boolean(),
791
- language: z.string().default("eng"),
792
- isNegated: z.boolean().optional().default(false)
793
- }).strict(),
794
798
  z.object({
795
799
  target: z.literal("property"),
796
800
  propertyVariable: uuidSchema,
@@ -2158,6 +2162,28 @@ const CTS_INCLUDES_STOP_WORDS = [
2158
2162
  ];
2159
2163
  const CTS_INCLUDES_STOP_WORDS_VAR = "$ctsIncludesStopWords";
2160
2164
  const CTS_INCLUDES_TOKEN_SPLIT_REGEX = /\W+/u;
2165
+ const CONTENT_TARGET_CONFIGS = {
2166
+ title: {
2167
+ containerElementName: "identification",
2168
+ getContentNodesExpression: (language) => `identification/label/content[@xml:lang="${language}"]`
2169
+ },
2170
+ description: {
2171
+ containerElementName: "description",
2172
+ getContentNodesExpression: (language) => `description/content[@xml:lang="${language}"]`
2173
+ },
2174
+ image: {
2175
+ containerElementName: "image",
2176
+ getContentNodesExpression: (language) => `image/identification/label/content[@xml:lang="${language}"]`
2177
+ },
2178
+ periods: {
2179
+ containerElementName: "period",
2180
+ getContentNodesExpression: (language) => `periods/period/identification/label/content[@xml:lang="${language}"]`
2181
+ },
2182
+ bibliography: {
2183
+ containerElementName: "bibliography",
2184
+ getContentNodesExpression: (language) => `bibliographies/bibliography/identification/label/content[@xml:lang="${language}"]`
2185
+ }
2186
+ };
2161
2187
  function tokenizeIncludesSearchValue(params) {
2162
2188
  const { value, isCaseSensitive } = params;
2163
2189
  const rawTerms = (isCaseSensitive ? value : value.toLowerCase()).split(CTS_INCLUDES_TOKEN_SPLIT_REGEX);
@@ -2172,6 +2198,10 @@ function buildFlattenedContentValuesExpression(contentNodesExpression) {
2172
2198
  return `for $content in ${contentNodesExpression}
2173
2199
  return string-join($content//text(), "")`;
2174
2200
  }
2201
+ function buildNodeStringValuesExpression(nodesExpression) {
2202
+ return `for $node in ${nodesExpression}
2203
+ return string($node)`;
2204
+ }
2175
2205
  function buildSearchableContentNodesExpression(contentNodesExpression) {
2176
2206
  return `for $content in ${contentNodesExpression}
2177
2207
  return (
@@ -2186,6 +2216,18 @@ function buildCombinedSearchableContentNodesExpression(contentNodesExpressions)
2186
2216
  if (searchableExpressions.length === 1) return searchableExpressions[0] ?? "()";
2187
2217
  return `(${searchableExpressions.join(", ")})`;
2188
2218
  }
2219
+ function buildTokenizedSearchPredicate(params) {
2220
+ const { searchableNodesExpression, termsExpression, isCaseSensitive } = params;
2221
+ return `(every $term in ${termsExpression}
2222
+ satisfies some $searchNode in (${searchableNodesExpression})
2223
+ satisfies cts:contains(
2224
+ $searchNode,
2225
+ ${buildCtsWordQueryExpression({
2226
+ termExpression: "$term",
2227
+ isCaseSensitive
2228
+ })}
2229
+ ))`;
2230
+ }
2189
2231
  /**
2190
2232
  * Build a string match predicate for an XQuery string.
2191
2233
  */
@@ -2252,8 +2294,8 @@ function buildTokenizedSearchDeclarations(params) {
2252
2294
  termsVar
2253
2295
  };
2254
2296
  }
2255
- function buildCtsIncludesPredicate(params) {
2256
- const { searchableNodesExpression, fallbackValueExpression, value, isCaseSensitive, queryKey, buildCandidateTermQuery } = params;
2297
+ function buildTokenizedCtsClause(params) {
2298
+ const { value, isCaseSensitive, queryKey, fallbackPredicate, buildTermQueryExpression, buildTokenPredicate } = params;
2257
2299
  const tokenizedSearchDeclarations = buildTokenizedSearchDeclarations({
2258
2300
  value,
2259
2301
  isCaseSensitive,
@@ -2261,12 +2303,6 @@ function buildCtsIncludesPredicate(params) {
2261
2303
  });
2262
2304
  const termQueriesVar = `$query${queryKey}TermQueries`;
2263
2305
  const candidateQueryVar = `$query${queryKey}CandidateQuery`;
2264
- const fallbackPredicate = buildRawStringMatchPredicate({
2265
- valueExpression: fallbackValueExpression,
2266
- value,
2267
- matchMode: "includes",
2268
- isCaseSensitive
2269
- });
2270
2306
  if (tokenizeIncludesSearchValue({
2271
2307
  value,
2272
2308
  isCaseSensitive
@@ -2280,7 +2316,7 @@ function buildCtsIncludesPredicate(params) {
2280
2316
  ...tokenizedSearchDeclarations.declarations,
2281
2317
  `let ${termQueriesVar} :=
2282
2318
  for $term in ${tokenizedSearchDeclarations.termsVar}
2283
- return ${buildCandidateTermQuery("$term")}`,
2319
+ return ${buildTermQueryExpression("$term")}`,
2284
2320
  `let ${candidateQueryVar} :=
2285
2321
  if (count(${tokenizedSearchDeclarations.termsVar}) = 1)
2286
2322
  then ${termQueriesVar}[1]
@@ -2288,18 +2324,30 @@ function buildCtsIncludesPredicate(params) {
2288
2324
  then cts:and-query(${termQueriesVar})
2289
2325
  else ()`
2290
2326
  ],
2291
- predicate: `(every $term in ${tokenizedSearchDeclarations.termsVar}
2292
- satisfies some $searchNode in (${searchableNodesExpression})
2293
- satisfies cts:contains(
2294
- $searchNode,
2295
- ${buildCtsWordQueryExpression({
2296
- termExpression: "$term",
2297
- isCaseSensitive
2298
- })}
2299
- ))`,
2327
+ predicate: buildTokenPredicate(tokenizedSearchDeclarations.termsVar),
2300
2328
  candidateQueryVar
2301
2329
  };
2302
2330
  }
2331
+ function buildCtsIncludesPredicate(params) {
2332
+ const { searchableNodesExpression, fallbackValueExpression, value, isCaseSensitive, queryKey, buildCandidateTermQuery } = params;
2333
+ return buildTokenizedCtsClause({
2334
+ value,
2335
+ isCaseSensitive,
2336
+ queryKey,
2337
+ fallbackPredicate: buildRawStringMatchPredicate({
2338
+ valueExpression: fallbackValueExpression,
2339
+ value,
2340
+ matchMode: "includes",
2341
+ isCaseSensitive
2342
+ }),
2343
+ buildTermQueryExpression: buildCandidateTermQuery,
2344
+ buildTokenPredicate: (termsExpression) => buildTokenizedSearchPredicate({
2345
+ searchableNodesExpression,
2346
+ termsExpression,
2347
+ isCaseSensitive
2348
+ })
2349
+ });
2350
+ }
2303
2351
  /**
2304
2352
  * Build the raw search paths for item-level string search.
2305
2353
  */
@@ -2423,6 +2471,9 @@ function getQueryGroupChildren(query) {
2423
2471
  function getQueryGroupOperator(query) {
2424
2472
  return "and" in query ? "and" : "or";
2425
2473
  }
2474
+ function getContentTargetConfig(target) {
2475
+ return CONTENT_TARGET_CONFIGS[target];
2476
+ }
2426
2477
  function buildContentTargetCandidateBranch(params) {
2427
2478
  const { containerElementName, termExpression, isCaseSensitive, language } = params;
2428
2479
  return `cts:element-query(xs:QName("${containerElementName}"),
@@ -2456,6 +2507,114 @@ function buildPropertyStringCandidateBranch(params) {
2456
2507
  )
2457
2508
  )`;
2458
2509
  }
2510
+ function buildPropertyContentNodesExpression(params) {
2511
+ const { language, excludeInherited } = params;
2512
+ return `${excludeInherited ? `value[not(@inherited="true")]` : "value"}/content[@xml:lang="${language}"]`;
2513
+ }
2514
+ function buildPropertyRawValueCandidateBranch(params) {
2515
+ const { termExpression, isCaseSensitive } = params;
2516
+ return `cts:element-query(xs:QName("properties"),
2517
+ cts:element-query(xs:QName("property"),
2518
+ cts:element-attribute-word-query(
2519
+ xs:QName("value"),
2520
+ xs:QName("rawValue"),
2521
+ ${termExpression},
2522
+ ${buildCtsQueryOptionsExpression(isCaseSensitive)}
2523
+ )
2524
+ )
2525
+ )`;
2526
+ }
2527
+ function buildPropertySimpleValueTextCandidateBranch(params) {
2528
+ const { termExpression, isCaseSensitive } = params;
2529
+ return `cts:element-query(xs:QName("properties"),
2530
+ cts:element-query(xs:QName("property"),
2531
+ cts:element-word-query(
2532
+ xs:QName("value"),
2533
+ ${termExpression},
2534
+ ${buildCtsQueryOptionsExpression(isCaseSensitive)}
2535
+ )
2536
+ )
2537
+ )`;
2538
+ }
2539
+ function buildPropertySimpleValueRawValueExpression() {
2540
+ return buildNodeStringValuesExpression("value/@rawValue");
2541
+ }
2542
+ function buildPropertySimpleValueTextExpression() {
2543
+ return buildNodeStringValuesExpression("value[not(*)]/text()");
2544
+ }
2545
+ function buildPropertySimpleValueSearchableNodesExpression() {
2546
+ return "value[not(*)]";
2547
+ }
2548
+ function buildPropertyContentRawPredicate(params) {
2549
+ const { value, matchMode, isCaseSensitive, language, excludeInherited } = params;
2550
+ return buildRawStringMatchPredicate({
2551
+ valueExpression: buildFlattenedContentValuesExpression(buildPropertyContentNodesExpression({
2552
+ language,
2553
+ excludeInherited
2554
+ })),
2555
+ value,
2556
+ matchMode,
2557
+ isCaseSensitive
2558
+ });
2559
+ }
2560
+ function buildPropertyRawValueOrTextRawPredicate(params) {
2561
+ const { value, matchMode, isCaseSensitive } = params;
2562
+ return buildOrPredicate([buildRawStringMatchPredicate({
2563
+ valueExpression: buildPropertySimpleValueRawValueExpression(),
2564
+ value,
2565
+ matchMode,
2566
+ isCaseSensitive
2567
+ }), buildRawStringMatchPredicate({
2568
+ valueExpression: buildPropertySimpleValueTextExpression(),
2569
+ value,
2570
+ matchMode,
2571
+ isCaseSensitive
2572
+ })]);
2573
+ }
2574
+ function buildPropertyRawValueOrTextTokenPredicate(params) {
2575
+ const { termsExpression, isCaseSensitive } = params;
2576
+ const rawValueQuery = `cts:element-attribute-word-query(
2577
+ xs:QName("value"),
2578
+ xs:QName("rawValue"),
2579
+ $term,
2580
+ ${buildCtsQueryOptionsExpression(isCaseSensitive)}
2581
+ )`;
2582
+ const textQuery = `cts:element-word-query(
2583
+ xs:QName("value"),
2584
+ $term,
2585
+ ${buildCtsQueryOptionsExpression(isCaseSensitive)}
2586
+ )`;
2587
+ return `(every $term in ${termsExpression}
2588
+ satisfies some $searchNode in (${buildPropertySimpleValueSearchableNodesExpression()})
2589
+ satisfies (
2590
+ cts:contains($searchNode, ${rawValueQuery})
2591
+ or
2592
+ cts:contains($searchNode, ${textQuery})
2593
+ ))`;
2594
+ }
2595
+ function buildPropertyContentTokenPredicate(params) {
2596
+ const { termsExpression, isCaseSensitive, language, excludeInherited } = params;
2597
+ return buildTokenizedSearchPredicate({
2598
+ searchableNodesExpression: buildSearchableContentNodesExpression(buildPropertyContentNodesExpression({
2599
+ language,
2600
+ excludeInherited
2601
+ })),
2602
+ termsExpression,
2603
+ isCaseSensitive
2604
+ });
2605
+ }
2606
+ function buildIncludesGroupMemberFromTokenSource(params) {
2607
+ const { rawPredicate, searchableNodesExpression, isCaseSensitive, buildCandidateTermQuery } = params;
2608
+ return {
2609
+ rawPredicate,
2610
+ buildTokenPredicate: (termsExpression) => buildTokenizedSearchPredicate({
2611
+ searchableNodesExpression,
2612
+ termsExpression,
2613
+ isCaseSensitive
2614
+ }),
2615
+ buildCandidateTermQuery
2616
+ };
2617
+ }
2459
2618
  function getGroupableIncludesValue(query) {
2460
2619
  switch (query.target) {
2461
2620
  case "string":
@@ -2465,8 +2624,9 @@ function getGroupableIncludesValue(query) {
2465
2624
  case "periods":
2466
2625
  case "bibliography": return query.value;
2467
2626
  case "property":
2468
- if (query.dataType !== "string" || query.propertyValues?.length !== 1) return null;
2469
- return query.propertyValues[0] ?? null;
2627
+ if (query.dataType === "IDREF") return null;
2628
+ if (query.dataType === "date" || query.dataType === "dateTime") return "value" in query && query.value != null ? query.value : null;
2629
+ return query.value ?? null;
2470
2630
  }
2471
2631
  }
2472
2632
  function isCompatibleIncludesGroupQuery(params) {
@@ -2476,30 +2636,36 @@ function isCompatibleIncludesGroupQuery(params) {
2476
2636
  return queryValue != null && queryValue === value && query.isCaseSensitive === isCaseSensitive && query.language === language;
2477
2637
  }
2478
2638
  function buildContentTargetIncludesGroupMember(params) {
2479
- const { contentNodesExpression, value, isCaseSensitive, language, containerElementName } = params;
2480
- return {
2639
+ const { query } = params;
2640
+ const config = getContentTargetConfig(query.target);
2641
+ const contentNodesExpression = config.getContentNodesExpression(query.language);
2642
+ return buildIncludesGroupMemberFromTokenSource({
2481
2643
  rawPredicate: buildRawStringMatchPredicate({
2482
2644
  valueExpression: buildFlattenedContentValuesExpression(contentNodesExpression),
2483
- value,
2645
+ value: query.value,
2484
2646
  matchMode: "includes",
2485
- isCaseSensitive
2647
+ isCaseSensitive: query.isCaseSensitive
2486
2648
  }),
2649
+ searchableNodesExpression: buildSearchableContentNodesExpression(contentNodesExpression),
2650
+ isCaseSensitive: query.isCaseSensitive,
2487
2651
  buildCandidateTermQuery: (termExpression) => buildContentTargetCandidateBranch({
2488
- containerElementName,
2652
+ containerElementName: config.containerElementName,
2489
2653
  termExpression,
2490
- isCaseSensitive,
2491
- language
2654
+ isCaseSensitive: query.isCaseSensitive,
2655
+ language: query.language
2492
2656
  })
2493
- };
2657
+ });
2494
2658
  }
2495
2659
  function buildItemStringIncludesGroupMember(query) {
2496
- return {
2660
+ return buildIncludesGroupMemberFromTokenSource({
2497
2661
  rawPredicate: buildCombinedRawStringMatchPredicate({
2498
2662
  valueExpressions: buildItemStringSearchPaths(query.language),
2499
2663
  value: query.value,
2500
2664
  matchMode: "includes",
2501
2665
  isCaseSensitive: query.isCaseSensitive
2502
2666
  }),
2667
+ searchableNodesExpression: buildItemStringSearchableNodesExpression(query.language),
2668
+ isCaseSensitive: query.isCaseSensitive,
2503
2669
  buildCandidateTermQuery: (termExpression) => `cts:or-query((
2504
2670
  ${buildItemStringIdentificationBranch({
2505
2671
  termExpression,
@@ -2512,67 +2678,112 @@ function buildItemStringIncludesGroupMember(query) {
2512
2678
  language: query.language
2513
2679
  })}
2514
2680
  ))`
2515
- };
2681
+ });
2516
2682
  }
2517
2683
  function buildPropertyStringIncludesGroupMember(query) {
2518
2684
  const propertyVariable = query.propertyVariable;
2519
2685
  const predicateParts = [];
2520
- const valueExpression = buildFlattenedContentValuesExpression(`value[not(@inherited="true")]/content[@xml:lang="${query.language}"]`);
2521
- const propertyValue = query.propertyValues[0] ?? "";
2686
+ const propertyValue = query.value;
2522
2687
  if (propertyVariable != null) predicateParts.push(buildPropertyLabelPredicate(propertyVariable));
2523
- return {
2524
- rawPredicate: buildPropertyPredicateExpression([...predicateParts, buildRawStringMatchPredicate({
2525
- valueExpression,
2688
+ return buildIncludesGroupMemberFromTokenSource({
2689
+ rawPredicate: buildPropertyPredicateExpression([...predicateParts, buildPropertyContentRawPredicate({
2526
2690
  value: propertyValue,
2527
2691
  matchMode: "includes",
2528
- isCaseSensitive: query.isCaseSensitive
2692
+ isCaseSensitive: query.isCaseSensitive,
2693
+ language: query.language,
2694
+ excludeInherited: true
2529
2695
  })]),
2696
+ searchableNodesExpression: buildPropertyPredicateExpression([...predicateParts, buildSearchableContentNodesExpression(buildPropertyContentNodesExpression({
2697
+ language: query.language,
2698
+ excludeInherited: true
2699
+ }))]),
2700
+ isCaseSensitive: query.isCaseSensitive,
2530
2701
  buildCandidateTermQuery: (termExpression) => buildPropertyStringCandidateBranch({
2531
2702
  termExpression,
2532
2703
  isCaseSensitive: query.isCaseSensitive,
2533
2704
  language: query.language
2534
2705
  })
2706
+ });
2707
+ }
2708
+ function buildPropertyRawValueOrTextIncludesGroupMember(query) {
2709
+ const propertyVariable = query.propertyVariable;
2710
+ const predicateParts = [];
2711
+ const propertyValue = getGroupableIncludesValue(query);
2712
+ if (propertyValue == null) throw new Error("Cannot build a rawValue/text includes group without a search value");
2713
+ if (propertyVariable != null) predicateParts.push(buildPropertyLabelPredicate(propertyVariable));
2714
+ return {
2715
+ rawPredicate: buildPropertyPredicateExpression([...predicateParts, buildPropertyRawValueOrTextRawPredicate({
2716
+ value: propertyValue,
2717
+ matchMode: "includes",
2718
+ isCaseSensitive: query.isCaseSensitive
2719
+ })]),
2720
+ buildTokenPredicate: (termsExpression) => buildPropertyPredicateExpression([...predicateParts, buildPropertyRawValueOrTextTokenPredicate({
2721
+ termsExpression,
2722
+ isCaseSensitive: query.isCaseSensitive
2723
+ })]),
2724
+ buildCandidateTermQuery: (termExpression) => buildOrCtsQueryExpression([buildPropertyRawValueCandidateBranch({
2725
+ termExpression,
2726
+ isCaseSensitive: query.isCaseSensitive
2727
+ }), buildPropertySimpleValueTextCandidateBranch({
2728
+ termExpression,
2729
+ isCaseSensitive: query.isCaseSensitive
2730
+ })])
2535
2731
  };
2536
2732
  }
2537
- function buildIncludesGroupMember(query) {
2538
- switch (query.target) {
2539
- case "string": return buildItemStringIncludesGroupMember(query);
2540
- case "title": return buildContentTargetIncludesGroupMember({
2541
- contentNodesExpression: `identification/label/content[@xml:lang="${query.language}"]`,
2542
- value: query.value,
2543
- isCaseSensitive: query.isCaseSensitive,
2544
- language: query.language,
2545
- containerElementName: "identification"
2546
- });
2547
- case "description": return buildContentTargetIncludesGroupMember({
2548
- contentNodesExpression: `description/content[@xml:lang="${query.language}"]`,
2549
- value: query.value,
2550
- isCaseSensitive: query.isCaseSensitive,
2551
- language: query.language,
2552
- containerElementName: "description"
2553
- });
2554
- case "periods": return buildContentTargetIncludesGroupMember({
2555
- contentNodesExpression: `periods/period/identification/label/content[@xml:lang="${query.language}"]`,
2733
+ function buildPropertyAllIncludesGroupMember(query) {
2734
+ const propertyVariable = query.propertyVariable;
2735
+ const predicateParts = [];
2736
+ if (propertyVariable != null) predicateParts.push(buildPropertyLabelPredicate(propertyVariable));
2737
+ return {
2738
+ rawPredicate: buildPropertyPredicateExpression([...predicateParts, buildOrPredicate([buildPropertyRawValueOrTextRawPredicate({
2556
2739
  value: query.value,
2557
- isCaseSensitive: query.isCaseSensitive,
2558
- language: query.language,
2559
- containerElementName: "period"
2560
- });
2561
- case "bibliography": return buildContentTargetIncludesGroupMember({
2562
- contentNodesExpression: `bibliographies/bibliography/identification/label/content[@xml:lang="${query.language}"]`,
2740
+ matchMode: "includes",
2741
+ isCaseSensitive: query.isCaseSensitive
2742
+ }), buildPropertyContentRawPredicate({
2563
2743
  value: query.value,
2744
+ matchMode: "includes",
2564
2745
  isCaseSensitive: query.isCaseSensitive,
2565
2746
  language: query.language,
2566
- containerElementName: "bibliography"
2567
- });
2568
- case "image": return buildContentTargetIncludesGroupMember({
2569
- contentNodesExpression: `image/identification/label/content[@xml:lang="${query.language}"]`,
2570
- value: query.value,
2747
+ excludeInherited: true
2748
+ })])]),
2749
+ buildTokenPredicate: (termsExpression) => buildPropertyPredicateExpression([...predicateParts, buildOrPredicate([buildPropertyRawValueOrTextTokenPredicate({
2750
+ termsExpression,
2751
+ isCaseSensitive: query.isCaseSensitive
2752
+ }), buildPropertyContentTokenPredicate({
2753
+ termsExpression,
2571
2754
  isCaseSensitive: query.isCaseSensitive,
2572
2755
  language: query.language,
2573
- containerElementName: "image"
2574
- });
2575
- case "property": return buildPropertyStringIncludesGroupMember(query);
2756
+ excludeInherited: true
2757
+ })])]),
2758
+ buildCandidateTermQuery: (termExpression) => buildOrCtsQueryExpression([
2759
+ buildPropertyRawValueCandidateBranch({
2760
+ termExpression,
2761
+ isCaseSensitive: query.isCaseSensitive
2762
+ }),
2763
+ buildPropertySimpleValueTextCandidateBranch({
2764
+ termExpression,
2765
+ isCaseSensitive: query.isCaseSensitive
2766
+ }),
2767
+ buildPropertyStringCandidateBranch({
2768
+ termExpression,
2769
+ isCaseSensitive: query.isCaseSensitive,
2770
+ language: query.language
2771
+ })
2772
+ ])
2773
+ };
2774
+ }
2775
+ function buildIncludesGroupMember(query) {
2776
+ switch (query.target) {
2777
+ case "string": return buildItemStringIncludesGroupMember(query);
2778
+ case "title":
2779
+ case "description":
2780
+ case "periods":
2781
+ case "bibliography":
2782
+ case "image": return buildContentTargetIncludesGroupMember({ query });
2783
+ case "property":
2784
+ if (query.dataType === "string") return buildPropertyStringIncludesGroupMember(query);
2785
+ if (query.dataType === "all") return buildPropertyAllIncludesGroupMember(query);
2786
+ return buildPropertyRawValueOrTextIncludesGroupMember(query);
2576
2787
  }
2577
2788
  }
2578
2789
  function getCompatibleIncludesGroupLeaves(params) {
@@ -2609,38 +2820,14 @@ function buildIncludesGroupClause(params) {
2609
2820
  const groupValue = getGroupableIncludesValue(firstQuery);
2610
2821
  if (groupValue == null) throw new Error("Cannot build an includes group without a search value");
2611
2822
  const members = queries.map((query) => buildIncludesGroupMember(query));
2612
- const tokenizedSearchDeclarations = buildTokenizedSearchDeclarations({
2823
+ return buildTokenizedCtsClause({
2613
2824
  value: groupValue,
2614
2825
  isCaseSensitive: firstQuery.isCaseSensitive,
2615
- queryKey
2616
- });
2617
- const tokenizedTerms = tokenizeIncludesSearchValue({
2618
- value: groupValue,
2619
- isCaseSensitive: firstQuery.isCaseSensitive
2826
+ queryKey,
2827
+ fallbackPredicate: buildOrPredicate(members.map((member) => member.rawPredicate)),
2828
+ buildTermQueryExpression: (termExpression) => buildOrCtsQueryExpression(members.map((member) => member.buildCandidateTermQuery(termExpression))),
2829
+ buildTokenPredicate: (termsExpression) => buildOrPredicate(members.map((member) => member.buildTokenPredicate(termsExpression)))
2620
2830
  });
2621
- const termQueriesVar = `$query${queryKey}TermQueries`;
2622
- const candidateQueryVar = `$query${queryKey}CandidateQuery`;
2623
- if (tokenizedTerms.length === 0) return {
2624
- declarations: [],
2625
- predicate: buildOrPredicate(members.map((member) => member.rawPredicate)),
2626
- candidateQueryVar: null
2627
- };
2628
- return {
2629
- declarations: [
2630
- ...tokenizedSearchDeclarations.declarations,
2631
- `let ${termQueriesVar} :=
2632
- for $term in ${tokenizedSearchDeclarations.termsVar}
2633
- return ${buildOrCtsQueryExpression(members.map((member) => member.buildCandidateTermQuery("$term")))}`,
2634
- `let ${candidateQueryVar} :=
2635
- if (count(${tokenizedSearchDeclarations.termsVar}) = 1)
2636
- then ${termQueriesVar}[1]
2637
- else if (count(${tokenizedSearchDeclarations.termsVar}) gt 1)
2638
- then cts:and-query(${termQueriesVar})
2639
- else ()`
2640
- ],
2641
- predicate: `cts:contains(., ${candidateQueryVar})`,
2642
- candidateQueryVar
2643
- };
2644
2831
  }
2645
2832
  /**
2646
2833
  * Build a string match predicate for an XQuery string.
@@ -2668,10 +2855,34 @@ function buildStringMatchClause(params) {
2668
2855
  };
2669
2856
  }
2670
2857
  function buildPropertyValueAttributePredicate(params) {
2671
- const { propertyValues, attributeName } = params;
2672
- const valuePredicates = [];
2673
- for (const propertyValue of propertyValues) valuePredicates.push(`value[@${attributeName}=${stringLiteral(propertyValue)}]`);
2674
- return buildOrPredicate(valuePredicates);
2858
+ const { value, attributeName } = params;
2859
+ return `value[@${attributeName}=${stringLiteral(value)}]`;
2860
+ }
2861
+ function buildPropertyRawValueOrTextMatchClause(params) {
2862
+ const { value, matchMode, isCaseSensitive, version, queryKey } = params;
2863
+ const fallbackPredicate = buildPropertyRawValueOrTextRawPredicate(params);
2864
+ if (matchMode !== "includes" || version !== 2) return {
2865
+ declarations: [],
2866
+ predicate: fallbackPredicate,
2867
+ candidateQueryVar: null
2868
+ };
2869
+ return buildTokenizedCtsClause({
2870
+ value,
2871
+ isCaseSensitive,
2872
+ queryKey,
2873
+ fallbackPredicate,
2874
+ buildTermQueryExpression: (termExpression) => buildOrCtsQueryExpression([buildPropertyRawValueCandidateBranch({
2875
+ termExpression,
2876
+ isCaseSensitive
2877
+ }), buildPropertySimpleValueTextCandidateBranch({
2878
+ termExpression,
2879
+ isCaseSensitive
2880
+ })]),
2881
+ buildTokenPredicate: (termsExpression) => buildPropertyRawValueOrTextTokenPredicate({
2882
+ termsExpression,
2883
+ isCaseSensitive
2884
+ })
2885
+ });
2675
2886
  }
2676
2887
  function buildPropertyPredicateExpression(propertyPredicates) {
2677
2888
  let propertyExpression = ".//properties//property";
@@ -2680,44 +2891,72 @@ function buildPropertyPredicateExpression(propertyPredicates) {
2680
2891
  }
2681
2892
  function buildPropertyStringValueClause(params) {
2682
2893
  const { query, version, queryKey } = params;
2683
- const propertyContentNodesExpression = query.matchMode === "includes" && version === 2 ? `value[not(@inherited="true")]/content[@xml:lang="${query.language}"]` : `value/content[@xml:lang="${query.language}"]`;
2684
- const declarations = [];
2685
- const valuePredicates = [];
2686
- const candidateQueryVars = [];
2687
- for (const [propertyValueIndex, propertyValue] of query.propertyValues.entries()) {
2688
- const compiledStringClause = buildStringMatchClause({
2689
- contentNodesExpression: propertyContentNodesExpression,
2690
- value: propertyValue,
2691
- matchMode: query.matchMode,
2894
+ return buildStringMatchClause({
2895
+ contentNodesExpression: buildPropertyContentNodesExpression({
2896
+ language: query.language,
2897
+ excludeInherited: query.matchMode === "includes" && version === 2
2898
+ }),
2899
+ value: query.value,
2900
+ matchMode: query.matchMode,
2901
+ isCaseSensitive: query.isCaseSensitive,
2902
+ version,
2903
+ queryKey,
2904
+ buildCandidateTermQuery: (termExpression) => buildPropertyStringCandidateBranch({
2905
+ termExpression,
2692
2906
  isCaseSensitive: query.isCaseSensitive,
2693
- version,
2694
- queryKey: `${queryKey}_${propertyValueIndex + 1}`,
2695
- buildCandidateTermQuery: (termExpression) => buildPropertyStringCandidateBranch({
2907
+ language: query.language
2908
+ })
2909
+ });
2910
+ }
2911
+ function buildPropertyAllValueClause(params) {
2912
+ const { query, version, queryKey } = params;
2913
+ const excludeInheritedContent = query.matchMode === "includes" && version === 2;
2914
+ const fallbackPredicate = buildOrPredicate([buildPropertyRawValueOrTextRawPredicate({
2915
+ value: query.value,
2916
+ matchMode: query.matchMode,
2917
+ isCaseSensitive: query.isCaseSensitive
2918
+ }), buildPropertyContentRawPredicate({
2919
+ value: query.value,
2920
+ matchMode: query.matchMode,
2921
+ isCaseSensitive: query.isCaseSensitive,
2922
+ language: query.language,
2923
+ excludeInherited: excludeInheritedContent
2924
+ })]);
2925
+ if (query.matchMode !== "includes" || version !== 2) return {
2926
+ declarations: [],
2927
+ predicate: fallbackPredicate,
2928
+ candidateQueryVar: null
2929
+ };
2930
+ return buildTokenizedCtsClause({
2931
+ value: query.value,
2932
+ isCaseSensitive: query.isCaseSensitive,
2933
+ queryKey,
2934
+ fallbackPredicate,
2935
+ buildTermQueryExpression: (termExpression) => buildOrCtsQueryExpression([
2936
+ buildPropertyRawValueCandidateBranch({
2937
+ termExpression,
2938
+ isCaseSensitive: query.isCaseSensitive
2939
+ }),
2940
+ buildPropertySimpleValueTextCandidateBranch({
2941
+ termExpression,
2942
+ isCaseSensitive: query.isCaseSensitive
2943
+ }),
2944
+ buildPropertyStringCandidateBranch({
2696
2945
  termExpression,
2697
2946
  isCaseSensitive: query.isCaseSensitive,
2698
2947
  language: query.language
2699
2948
  })
2700
- });
2701
- declarations.push(...compiledStringClause.declarations);
2702
- valuePredicates.push(compiledStringClause.predicate);
2703
- if (compiledStringClause.candidateQueryVar != null) candidateQueryVars.push(compiledStringClause.candidateQueryVar);
2704
- }
2705
- let candidateQueryVar = null;
2706
- if (candidateQueryVars.length > 0) {
2707
- const candidateQueriesExpression = `(${candidateQueryVars.join(", ")})`;
2708
- candidateQueryVar = `$query${queryKey}CandidateQuery`;
2709
- declarations.push(`let ${candidateQueryVar} :=
2710
- if (count(${candidateQueriesExpression}) = 1)
2711
- then ${candidateQueriesExpression}[1]
2712
- else if (count(${candidateQueriesExpression}) gt 1)
2713
- then cts:or-query(${candidateQueriesExpression})
2714
- else ()`);
2715
- }
2716
- return {
2717
- declarations,
2718
- predicate: buildOrPredicate(valuePredicates),
2719
- candidateQueryVar
2720
- };
2949
+ ]),
2950
+ buildTokenPredicate: (termsExpression) => buildOrPredicate([buildPropertyRawValueOrTextTokenPredicate({
2951
+ termsExpression,
2952
+ isCaseSensitive: query.isCaseSensitive
2953
+ }), buildPropertyContentTokenPredicate({
2954
+ termsExpression,
2955
+ isCaseSensitive: query.isCaseSensitive,
2956
+ language: query.language,
2957
+ excludeInherited: true
2958
+ })])
2959
+ });
2721
2960
  }
2722
2961
  /**
2723
2962
  * Build a property predicate for an XQuery string.
@@ -2729,27 +2968,55 @@ function buildPropertyClause(params) {
2729
2968
  const propertyVariable = query.propertyVariable;
2730
2969
  let candidateQueryVar = null;
2731
2970
  if (propertyVariable != null) predicateParts.push(buildPropertyLabelPredicate(propertyVariable));
2732
- if (query.dataType === "date" || query.dataType === "dateTime") if ("value" in query && query.value != null) predicateParts.push(`value/@rawValue = ${stringLiteral(query.value)}`);
2733
- else predicateParts.push(buildDateRangePredicate({
2971
+ if (query.dataType === "date" || query.dataType === "dateTime") if ("value" in query && query.value != null) {
2972
+ const compiledScalarClause = buildPropertyRawValueOrTextMatchClause({
2973
+ value: query.value,
2974
+ matchMode: query.matchMode,
2975
+ isCaseSensitive: query.isCaseSensitive,
2976
+ version,
2977
+ queryKey
2978
+ });
2979
+ declarations.push(...compiledScalarClause.declarations);
2980
+ predicateParts.push(compiledScalarClause.predicate);
2981
+ candidateQueryVar = compiledScalarClause.candidateQueryVar;
2982
+ } else predicateParts.push(buildDateRangePredicate({
2734
2983
  from: query.from,
2735
2984
  to: query.to
2736
2985
  }));
2737
- else if (query.propertyValues != null) switch (query.dataType) {
2986
+ else if (query.value != null) switch (query.dataType) {
2738
2987
  case "IDREF":
2739
2988
  predicateParts.push(buildPropertyValueAttributePredicate({
2740
- propertyValues: query.propertyValues,
2989
+ value: query.value,
2741
2990
  attributeName: "uuid"
2742
2991
  }));
2743
2992
  break;
2993
+ case "all": {
2994
+ const compiledAllClause = buildPropertyAllValueClause({
2995
+ query,
2996
+ version,
2997
+ queryKey
2998
+ });
2999
+ declarations.push(...compiledAllClause.declarations);
3000
+ predicateParts.push(compiledAllClause.predicate);
3001
+ candidateQueryVar = compiledAllClause.candidateQueryVar;
3002
+ break;
3003
+ }
2744
3004
  case "integer":
2745
3005
  case "decimal":
2746
3006
  case "time":
2747
- case "boolean":
2748
- predicateParts.push(buildPropertyValueAttributePredicate({
2749
- propertyValues: query.propertyValues,
2750
- attributeName: "rawValue"
2751
- }));
3007
+ case "boolean": {
3008
+ const compiledScalarClause = buildPropertyRawValueOrTextMatchClause({
3009
+ value: query.value,
3010
+ matchMode: query.matchMode,
3011
+ isCaseSensitive: query.isCaseSensitive,
3012
+ version,
3013
+ queryKey
3014
+ });
3015
+ declarations.push(...compiledScalarClause.declarations);
3016
+ predicateParts.push(compiledScalarClause.predicate);
3017
+ candidateQueryVar = compiledScalarClause.candidateQueryVar;
2752
3018
  break;
3019
+ }
2753
3020
  case "string": {
2754
3021
  const compiledStringClause = buildPropertyStringValueClause({
2755
3022
  query,
@@ -2779,76 +3046,27 @@ function buildQueryClause(params) {
2779
3046
  version,
2780
3047
  queryKey
2781
3048
  });
2782
- case "title": return buildStringMatchClause({
2783
- contentNodesExpression: `identification/label/content[@xml:lang="${query.language}"]`,
2784
- value: query.value,
2785
- matchMode: query.matchMode,
2786
- isCaseSensitive: query.isCaseSensitive,
2787
- version,
2788
- queryKey,
2789
- buildCandidateTermQuery: (termExpression) => buildContentTargetCandidateBranch({
2790
- containerElementName: "identification",
2791
- termExpression,
2792
- isCaseSensitive: query.isCaseSensitive,
2793
- language: query.language
2794
- })
2795
- });
2796
- case "description": return buildStringMatchClause({
2797
- contentNodesExpression: `description/content[@xml:lang="${query.language}"]`,
2798
- value: query.value,
2799
- matchMode: query.matchMode,
2800
- isCaseSensitive: query.isCaseSensitive,
2801
- version,
2802
- queryKey,
2803
- buildCandidateTermQuery: (termExpression) => buildContentTargetCandidateBranch({
2804
- containerElementName: "description",
2805
- termExpression,
2806
- isCaseSensitive: query.isCaseSensitive,
2807
- language: query.language
2808
- })
2809
- });
2810
- case "periods": return buildStringMatchClause({
2811
- contentNodesExpression: `periods/period/identification/label/content[@xml:lang="${query.language}"]`,
2812
- value: query.value,
2813
- matchMode: query.matchMode,
2814
- isCaseSensitive: query.isCaseSensitive,
2815
- version,
2816
- queryKey,
2817
- buildCandidateTermQuery: (termExpression) => buildContentTargetCandidateBranch({
2818
- containerElementName: "period",
2819
- termExpression,
2820
- isCaseSensitive: query.isCaseSensitive,
2821
- language: query.language
2822
- })
2823
- });
2824
- case "bibliography": return buildStringMatchClause({
2825
- contentNodesExpression: `bibliographies/bibliography/identification/label/content[@xml:lang="${query.language}"]`,
2826
- value: query.value,
2827
- matchMode: query.matchMode,
2828
- isCaseSensitive: query.isCaseSensitive,
2829
- version,
2830
- queryKey,
2831
- buildCandidateTermQuery: (termExpression) => buildContentTargetCandidateBranch({
2832
- containerElementName: "bibliography",
2833
- termExpression,
2834
- isCaseSensitive: query.isCaseSensitive,
2835
- language: query.language
2836
- })
2837
- });
2838
- case "image": return buildStringMatchClause({
2839
- contentNodesExpression: `image/identification/label/content[@xml:lang="${query.language}"]`,
2840
- value: query.value,
2841
- matchMode: query.matchMode,
2842
- isCaseSensitive: query.isCaseSensitive,
2843
- version,
2844
- queryKey,
2845
- buildCandidateTermQuery: (termExpression) => buildContentTargetCandidateBranch({
2846
- containerElementName: "image",
2847
- termExpression,
3049
+ case "title":
3050
+ case "description":
3051
+ case "periods":
3052
+ case "bibliography":
3053
+ case "image": {
3054
+ const config = getContentTargetConfig(query.target);
3055
+ return buildStringMatchClause({
3056
+ contentNodesExpression: config.getContentNodesExpression(query.language),
3057
+ value: query.value,
3058
+ matchMode: query.matchMode,
2848
3059
  isCaseSensitive: query.isCaseSensitive,
2849
- language: query.language
2850
- })
2851
- });
3060
+ version,
3061
+ queryKey,
3062
+ buildCandidateTermQuery: (termExpression) => buildContentTargetCandidateBranch({
3063
+ containerElementName: config.containerElementName,
3064
+ termExpression,
3065
+ isCaseSensitive: query.isCaseSensitive,
3066
+ language: query.language
3067
+ })
3068
+ });
3069
+ }
2852
3070
  case "property": return buildPropertyClause({
2853
3071
  query,
2854
3072
  version,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ochre-sdk",
3
- "version": "0.21.4",
3
+ "version": "0.21.6",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "description": "Node.js library for working with OCHRE (Online Cultural and Historical Research Environment) data",