ochre-sdk 0.21.4 → 0.21.5

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.
Files changed (2) hide show
  1. package/dist/index.mjs +254 -19
  2. package/package.json +1 -1
package/dist/index.mjs CHANGED
@@ -2172,6 +2172,10 @@ function buildFlattenedContentValuesExpression(contentNodesExpression) {
2172
2172
  return `for $content in ${contentNodesExpression}
2173
2173
  return string-join($content//text(), "")`;
2174
2174
  }
2175
+ function buildNodeStringValuesExpression(nodesExpression) {
2176
+ return `for $node in ${nodesExpression}
2177
+ return string($node)`;
2178
+ }
2175
2179
  function buildSearchableContentNodesExpression(contentNodesExpression) {
2176
2180
  return `for $content in ${contentNodesExpression}
2177
2181
  return (
@@ -2186,6 +2190,18 @@ function buildCombinedSearchableContentNodesExpression(contentNodesExpressions)
2186
2190
  if (searchableExpressions.length === 1) return searchableExpressions[0] ?? "()";
2187
2191
  return `(${searchableExpressions.join(", ")})`;
2188
2192
  }
2193
+ function buildTokenizedSearchPredicate(params) {
2194
+ const { searchableNodesExpression, termsExpression, isCaseSensitive } = params;
2195
+ return `(every $term in ${termsExpression}
2196
+ satisfies some $searchNode in (${searchableNodesExpression})
2197
+ satisfies cts:contains(
2198
+ $searchNode,
2199
+ ${buildCtsWordQueryExpression({
2200
+ termExpression: "$term",
2201
+ isCaseSensitive
2202
+ })}
2203
+ ))`;
2204
+ }
2189
2205
  /**
2190
2206
  * Build a string match predicate for an XQuery string.
2191
2207
  */
@@ -2288,15 +2304,11 @@ function buildCtsIncludesPredicate(params) {
2288
2304
  then cts:and-query(${termQueriesVar})
2289
2305
  else ()`
2290
2306
  ],
2291
- predicate: `(every $term in ${tokenizedSearchDeclarations.termsVar}
2292
- satisfies some $searchNode in (${searchableNodesExpression})
2293
- satisfies cts:contains(
2294
- $searchNode,
2295
- ${buildCtsWordQueryExpression({
2296
- termExpression: "$term",
2307
+ predicate: buildTokenizedSearchPredicate({
2308
+ searchableNodesExpression,
2309
+ termsExpression: tokenizedSearchDeclarations.termsVar,
2297
2310
  isCaseSensitive
2298
- })}
2299
- ))`,
2311
+ }),
2300
2312
  candidateQueryVar
2301
2313
  };
2302
2314
  }
@@ -2456,6 +2468,75 @@ function buildPropertyStringCandidateBranch(params) {
2456
2468
  )
2457
2469
  )`;
2458
2470
  }
2471
+ function buildPropertyRawValueCandidateBranch(params) {
2472
+ const { termExpression, isCaseSensitive } = params;
2473
+ return `cts:element-query(xs:QName("properties"),
2474
+ cts:element-query(xs:QName("property"),
2475
+ cts:element-attribute-word-query(
2476
+ xs:QName("value"),
2477
+ xs:QName("rawValue"),
2478
+ ${termExpression},
2479
+ ${buildCtsQueryOptionsExpression(isCaseSensitive)}
2480
+ )
2481
+ )
2482
+ )`;
2483
+ }
2484
+ function buildPropertySimpleValueTextCandidateBranch(params) {
2485
+ const { termExpression, isCaseSensitive } = params;
2486
+ return `cts:element-query(xs:QName("properties"),
2487
+ cts:element-query(xs:QName("property"),
2488
+ cts:element-word-query(
2489
+ xs:QName("value"),
2490
+ ${termExpression},
2491
+ ${buildCtsQueryOptionsExpression(isCaseSensitive)}
2492
+ )
2493
+ )
2494
+ )`;
2495
+ }
2496
+ function buildPropertySimpleValueRawValueExpression() {
2497
+ return buildNodeStringValuesExpression("value/@rawValue");
2498
+ }
2499
+ function buildPropertySimpleValueTextExpression() {
2500
+ return buildNodeStringValuesExpression("value[not(*)]/text()");
2501
+ }
2502
+ function buildPropertySimpleValueSearchableNodesExpression() {
2503
+ return "value[not(*)]";
2504
+ }
2505
+ function buildPropertyRawValueOrTextRawPredicate(params) {
2506
+ const { value, matchMode, isCaseSensitive } = params;
2507
+ return buildOrPredicate([buildRawStringMatchPredicate({
2508
+ valueExpression: buildPropertySimpleValueRawValueExpression(),
2509
+ value,
2510
+ matchMode,
2511
+ isCaseSensitive
2512
+ }), buildRawStringMatchPredicate({
2513
+ valueExpression: buildPropertySimpleValueTextExpression(),
2514
+ value,
2515
+ matchMode,
2516
+ isCaseSensitive
2517
+ })]);
2518
+ }
2519
+ function buildPropertyRawValueOrTextTokenPredicate(params) {
2520
+ const { termsExpression, isCaseSensitive } = params;
2521
+ const rawValueQuery = `cts:element-attribute-word-query(
2522
+ xs:QName("value"),
2523
+ xs:QName("rawValue"),
2524
+ $term,
2525
+ ${buildCtsQueryOptionsExpression(isCaseSensitive)}
2526
+ )`;
2527
+ const textQuery = `cts:element-word-query(
2528
+ xs:QName("value"),
2529
+ $term,
2530
+ ${buildCtsQueryOptionsExpression(isCaseSensitive)}
2531
+ )`;
2532
+ return `(every $term in ${termsExpression}
2533
+ satisfies some $searchNode in (${buildPropertySimpleValueSearchableNodesExpression()})
2534
+ satisfies (
2535
+ cts:contains($searchNode, ${rawValueQuery})
2536
+ or
2537
+ cts:contains($searchNode, ${textQuery})
2538
+ ))`;
2539
+ }
2459
2540
  function getGroupableIncludesValue(query) {
2460
2541
  switch (query.target) {
2461
2542
  case "string":
@@ -2465,7 +2546,13 @@ function getGroupableIncludesValue(query) {
2465
2546
  case "periods":
2466
2547
  case "bibliography": return query.value;
2467
2548
  case "property":
2468
- if (query.dataType !== "string" || query.propertyValues?.length !== 1) return null;
2549
+ if (query.dataType === "string") {
2550
+ if (query.propertyValues?.length !== 1) return null;
2551
+ return query.propertyValues[0] ?? null;
2552
+ }
2553
+ if (query.dataType === "IDREF") return null;
2554
+ if (query.dataType === "date" || query.dataType === "dateTime") return "value" in query && query.value != null ? query.value : null;
2555
+ if (query.propertyValues?.length !== 1) return null;
2469
2556
  return query.propertyValues[0] ?? null;
2470
2557
  }
2471
2558
  }
@@ -2484,6 +2571,11 @@ function buildContentTargetIncludesGroupMember(params) {
2484
2571
  matchMode: "includes",
2485
2572
  isCaseSensitive
2486
2573
  }),
2574
+ buildTokenPredicate: (termsExpression) => buildTokenizedSearchPredicate({
2575
+ searchableNodesExpression: buildSearchableContentNodesExpression(contentNodesExpression),
2576
+ termsExpression,
2577
+ isCaseSensitive
2578
+ }),
2487
2579
  buildCandidateTermQuery: (termExpression) => buildContentTargetCandidateBranch({
2488
2580
  containerElementName,
2489
2581
  termExpression,
@@ -2500,6 +2592,11 @@ function buildItemStringIncludesGroupMember(query) {
2500
2592
  matchMode: "includes",
2501
2593
  isCaseSensitive: query.isCaseSensitive
2502
2594
  }),
2595
+ buildTokenPredicate: (termsExpression) => buildTokenizedSearchPredicate({
2596
+ searchableNodesExpression: buildItemStringSearchableNodesExpression(query.language),
2597
+ termsExpression,
2598
+ isCaseSensitive: query.isCaseSensitive
2599
+ }),
2503
2600
  buildCandidateTermQuery: (termExpression) => `cts:or-query((
2504
2601
  ${buildItemStringIdentificationBranch({
2505
2602
  termExpression,
@@ -2517,7 +2614,8 @@ function buildItemStringIncludesGroupMember(query) {
2517
2614
  function buildPropertyStringIncludesGroupMember(query) {
2518
2615
  const propertyVariable = query.propertyVariable;
2519
2616
  const predicateParts = [];
2520
- const valueExpression = buildFlattenedContentValuesExpression(`value[not(@inherited="true")]/content[@xml:lang="${query.language}"]`);
2617
+ const propertyContentNodesExpression = `value[not(@inherited="true")]/content[@xml:lang="${query.language}"]`;
2618
+ const valueExpression = buildFlattenedContentValuesExpression(propertyContentNodesExpression);
2521
2619
  const propertyValue = query.propertyValues[0] ?? "";
2522
2620
  if (propertyVariable != null) predicateParts.push(buildPropertyLabelPredicate(propertyVariable));
2523
2621
  return {
@@ -2527,6 +2625,11 @@ function buildPropertyStringIncludesGroupMember(query) {
2527
2625
  matchMode: "includes",
2528
2626
  isCaseSensitive: query.isCaseSensitive
2529
2627
  })]),
2628
+ buildTokenPredicate: (termsExpression) => buildPropertyPredicateExpression([...predicateParts, buildTokenizedSearchPredicate({
2629
+ searchableNodesExpression: buildSearchableContentNodesExpression(propertyContentNodesExpression),
2630
+ termsExpression,
2631
+ isCaseSensitive: query.isCaseSensitive
2632
+ })]),
2530
2633
  buildCandidateTermQuery: (termExpression) => buildPropertyStringCandidateBranch({
2531
2634
  termExpression,
2532
2635
  isCaseSensitive: query.isCaseSensitive,
@@ -2534,6 +2637,31 @@ function buildPropertyStringIncludesGroupMember(query) {
2534
2637
  })
2535
2638
  };
2536
2639
  }
2640
+ function buildPropertyRawValueOrTextIncludesGroupMember(query) {
2641
+ const propertyVariable = query.propertyVariable;
2642
+ const predicateParts = [];
2643
+ const propertyValue = getGroupableIncludesValue(query);
2644
+ if (propertyValue == null) throw new Error("Cannot build a rawValue/text includes group without a search value");
2645
+ if (propertyVariable != null) predicateParts.push(buildPropertyLabelPredicate(propertyVariable));
2646
+ return {
2647
+ rawPredicate: buildPropertyPredicateExpression([...predicateParts, buildPropertyRawValueOrTextRawPredicate({
2648
+ value: propertyValue,
2649
+ matchMode: "includes",
2650
+ isCaseSensitive: query.isCaseSensitive
2651
+ })]),
2652
+ buildTokenPredicate: (termsExpression) => buildPropertyPredicateExpression([...predicateParts, buildPropertyRawValueOrTextTokenPredicate({
2653
+ termsExpression,
2654
+ isCaseSensitive: query.isCaseSensitive
2655
+ })]),
2656
+ buildCandidateTermQuery: (termExpression) => buildOrCtsQueryExpression([buildPropertyRawValueCandidateBranch({
2657
+ termExpression,
2658
+ isCaseSensitive: query.isCaseSensitive
2659
+ }), buildPropertySimpleValueTextCandidateBranch({
2660
+ termExpression,
2661
+ isCaseSensitive: query.isCaseSensitive
2662
+ })])
2663
+ };
2664
+ }
2537
2665
  function buildIncludesGroupMember(query) {
2538
2666
  switch (query.target) {
2539
2667
  case "string": return buildItemStringIncludesGroupMember(query);
@@ -2572,7 +2700,9 @@ function buildIncludesGroupMember(query) {
2572
2700
  language: query.language,
2573
2701
  containerElementName: "image"
2574
2702
  });
2575
- case "property": return buildPropertyStringIncludesGroupMember(query);
2703
+ case "property":
2704
+ if (query.dataType === "string") return buildPropertyStringIncludesGroupMember(query);
2705
+ return buildPropertyRawValueOrTextIncludesGroupMember(query);
2576
2706
  }
2577
2707
  }
2578
2708
  function getCompatibleIncludesGroupLeaves(params) {
@@ -2638,7 +2768,7 @@ function buildIncludesGroupClause(params) {
2638
2768
  then cts:and-query(${termQueriesVar})
2639
2769
  else ()`
2640
2770
  ],
2641
- predicate: `cts:contains(., ${candidateQueryVar})`,
2771
+ predicate: buildOrPredicate(members.map((member) => member.buildTokenPredicate(tokenizedSearchDeclarations.termsVar))),
2642
2772
  candidateQueryVar
2643
2773
  };
2644
2774
  }
@@ -2673,6 +2803,94 @@ function buildPropertyValueAttributePredicate(params) {
2673
2803
  for (const propertyValue of propertyValues) valuePredicates.push(`value[@${attributeName}=${stringLiteral(propertyValue)}]`);
2674
2804
  return buildOrPredicate(valuePredicates);
2675
2805
  }
2806
+ function buildPropertyRawValueOrTextMatchClause(params) {
2807
+ const { value, matchMode, isCaseSensitive, version, queryKey } = params;
2808
+ const fallbackPredicate = buildPropertyRawValueOrTextRawPredicate({
2809
+ value,
2810
+ matchMode,
2811
+ isCaseSensitive
2812
+ });
2813
+ if (matchMode !== "includes" || version !== 2) return {
2814
+ declarations: [],
2815
+ predicate: fallbackPredicate,
2816
+ candidateQueryVar: null
2817
+ };
2818
+ const tokenizedSearchDeclarations = buildTokenizedSearchDeclarations({
2819
+ value,
2820
+ isCaseSensitive,
2821
+ queryKey
2822
+ });
2823
+ const tokenizedTerms = tokenizeIncludesSearchValue({
2824
+ value,
2825
+ isCaseSensitive
2826
+ });
2827
+ const termQueriesVar = `$query${queryKey}TermQueries`;
2828
+ const candidateQueryVar = `$query${queryKey}CandidateQuery`;
2829
+ if (tokenizedTerms.length === 0) return {
2830
+ declarations: [],
2831
+ predicate: fallbackPredicate,
2832
+ candidateQueryVar: null
2833
+ };
2834
+ return {
2835
+ declarations: [
2836
+ ...tokenizedSearchDeclarations.declarations,
2837
+ `let ${termQueriesVar} :=
2838
+ for $term in ${tokenizedSearchDeclarations.termsVar}
2839
+ return ${buildOrCtsQueryExpression([buildPropertyRawValueCandidateBranch({
2840
+ termExpression: "$term",
2841
+ isCaseSensitive
2842
+ }), buildPropertySimpleValueTextCandidateBranch({
2843
+ termExpression: "$term",
2844
+ isCaseSensitive
2845
+ })])}`,
2846
+ `let ${candidateQueryVar} :=
2847
+ if (count(${tokenizedSearchDeclarations.termsVar}) = 1)
2848
+ then ${termQueriesVar}[1]
2849
+ else if (count(${tokenizedSearchDeclarations.termsVar}) gt 1)
2850
+ then cts:and-query(${termQueriesVar})
2851
+ else ()`
2852
+ ],
2853
+ predicate: buildPropertyRawValueOrTextTokenPredicate({
2854
+ termsExpression: tokenizedSearchDeclarations.termsVar,
2855
+ isCaseSensitive
2856
+ }),
2857
+ candidateQueryVar
2858
+ };
2859
+ }
2860
+ function buildPropertyRawValueOrTextClause(params) {
2861
+ const { values, matchMode, isCaseSensitive, version, queryKey } = params;
2862
+ const declarations = [];
2863
+ const valuePredicates = [];
2864
+ const candidateQueryVars = [];
2865
+ for (const [valueIndex, value] of values.entries()) {
2866
+ const compiledClause = buildPropertyRawValueOrTextMatchClause({
2867
+ value,
2868
+ matchMode,
2869
+ isCaseSensitive,
2870
+ version,
2871
+ queryKey: `${queryKey}_${valueIndex + 1}`
2872
+ });
2873
+ declarations.push(...compiledClause.declarations);
2874
+ valuePredicates.push(compiledClause.predicate);
2875
+ if (compiledClause.candidateQueryVar != null) candidateQueryVars.push(compiledClause.candidateQueryVar);
2876
+ }
2877
+ let candidateQueryVar = null;
2878
+ if (candidateQueryVars.length > 0) {
2879
+ const candidateQueriesExpression = `(${candidateQueryVars.join(", ")})`;
2880
+ candidateQueryVar = `$query${queryKey}CandidateQuery`;
2881
+ declarations.push(`let ${candidateQueryVar} :=
2882
+ if (count(${candidateQueriesExpression}) = 1)
2883
+ then ${candidateQueriesExpression}[1]
2884
+ else if (count(${candidateQueriesExpression}) gt 1)
2885
+ then cts:or-query(${candidateQueriesExpression})
2886
+ else ()`);
2887
+ }
2888
+ return {
2889
+ declarations,
2890
+ predicate: buildOrPredicate(valuePredicates),
2891
+ candidateQueryVar
2892
+ };
2893
+ }
2676
2894
  function buildPropertyPredicateExpression(propertyPredicates) {
2677
2895
  let propertyExpression = ".//properties//property";
2678
2896
  for (const propertyPredicate of propertyPredicates) propertyExpression += `[${propertyPredicate}]`;
@@ -2729,8 +2947,18 @@ function buildPropertyClause(params) {
2729
2947
  const propertyVariable = query.propertyVariable;
2730
2948
  let candidateQueryVar = null;
2731
2949
  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({
2950
+ if (query.dataType === "date" || query.dataType === "dateTime") if ("value" in query && query.value != null) {
2951
+ const compiledScalarClause = buildPropertyRawValueOrTextClause({
2952
+ values: [query.value],
2953
+ matchMode: query.matchMode,
2954
+ isCaseSensitive: query.isCaseSensitive,
2955
+ version,
2956
+ queryKey
2957
+ });
2958
+ declarations.push(...compiledScalarClause.declarations);
2959
+ predicateParts.push(compiledScalarClause.predicate);
2960
+ candidateQueryVar = compiledScalarClause.candidateQueryVar;
2961
+ } else predicateParts.push(buildDateRangePredicate({
2734
2962
  from: query.from,
2735
2963
  to: query.to
2736
2964
  }));
@@ -2744,12 +2972,19 @@ function buildPropertyClause(params) {
2744
2972
  case "integer":
2745
2973
  case "decimal":
2746
2974
  case "time":
2747
- case "boolean":
2748
- predicateParts.push(buildPropertyValueAttributePredicate({
2749
- propertyValues: query.propertyValues,
2750
- attributeName: "rawValue"
2751
- }));
2975
+ case "boolean": {
2976
+ const compiledScalarClause = buildPropertyRawValueOrTextClause({
2977
+ values: query.propertyValues,
2978
+ matchMode: query.matchMode,
2979
+ isCaseSensitive: query.isCaseSensitive,
2980
+ version,
2981
+ queryKey
2982
+ });
2983
+ declarations.push(...compiledScalarClause.declarations);
2984
+ predicateParts.push(compiledScalarClause.predicate);
2985
+ candidateQueryVar = compiledScalarClause.candidateQueryVar;
2752
2986
  break;
2987
+ }
2753
2988
  case "string": {
2754
2989
  const compiledStringClause = buildPropertyStringValueClause({
2755
2990
  query,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ochre-sdk",
3
- "version": "0.21.4",
3
+ "version": "0.21.5",
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",