ochre-sdk 0.20.22 → 0.20.24
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 +17 -10
- package/dist/index.mjs +348 -159
- package/package.json +5 -5
package/dist/index.d.mts
CHANGED
|
@@ -674,9 +674,10 @@ type SetItemsSort = {
|
|
|
674
674
|
* Represents a query for Set items
|
|
675
675
|
*/
|
|
676
676
|
type Query = {
|
|
677
|
-
target: "
|
|
677
|
+
target: "property";
|
|
678
|
+
propertyVariables: Array<string>;
|
|
678
679
|
dataType: Exclude<Exclude<PropertyValueContentType, "coordinate">, "date" | "dateTime">;
|
|
679
|
-
|
|
680
|
+
propertyValues?: Array<string>;
|
|
680
681
|
from?: never;
|
|
681
682
|
to?: never;
|
|
682
683
|
matchMode: "includes" | "exact";
|
|
@@ -685,9 +686,10 @@ type Query = {
|
|
|
685
686
|
operator?: "AND" | "OR";
|
|
686
687
|
isNegated?: boolean;
|
|
687
688
|
} | {
|
|
688
|
-
target: "
|
|
689
|
+
target: "property";
|
|
690
|
+
propertyVariables: Array<string>;
|
|
689
691
|
dataType: "date" | "dateTime";
|
|
690
|
-
|
|
692
|
+
propertyValues?: never;
|
|
691
693
|
from: string;
|
|
692
694
|
to?: string;
|
|
693
695
|
matchMode: "includes" | "exact";
|
|
@@ -696,9 +698,10 @@ type Query = {
|
|
|
696
698
|
operator?: "AND" | "OR";
|
|
697
699
|
isNegated?: boolean;
|
|
698
700
|
} | {
|
|
699
|
-
target: "
|
|
701
|
+
target: "property";
|
|
702
|
+
propertyVariables: Array<string>;
|
|
700
703
|
dataType: "date" | "dateTime";
|
|
701
|
-
|
|
704
|
+
propertyValues?: never;
|
|
702
705
|
from?: string;
|
|
703
706
|
to: string;
|
|
704
707
|
matchMode: "includes" | "exact";
|
|
@@ -706,6 +709,14 @@ type Query = {
|
|
|
706
709
|
language: string;
|
|
707
710
|
operator?: "AND" | "OR";
|
|
708
711
|
isNegated?: boolean;
|
|
712
|
+
} | {
|
|
713
|
+
target: "string";
|
|
714
|
+
value: string;
|
|
715
|
+
matchMode: "includes" | "exact";
|
|
716
|
+
isCaseSensitive: boolean;
|
|
717
|
+
language: string;
|
|
718
|
+
operator?: "AND" | "OR";
|
|
719
|
+
isNegated?: boolean;
|
|
709
720
|
} | {
|
|
710
721
|
target: "title" | "description" | "image" | "periods" | "bibliography";
|
|
711
722
|
value: string;
|
|
@@ -1223,7 +1234,6 @@ declare function fetchItem<T extends DataCategory = DataCategory, U extends Data
|
|
|
1223
1234
|
*
|
|
1224
1235
|
* @param params - The parameters for the fetch
|
|
1225
1236
|
* @param params.setScopeUuids - The Set scope UUIDs to filter by
|
|
1226
|
-
* @param params.propertyVariableUuids - The property variable UUIDs to filter by
|
|
1227
1237
|
* @param params.queries - Ordered queries to combine with AND/OR and optional NOT via negation
|
|
1228
1238
|
* @param params.sort - Optional sorting configuration applied before pagination.
|
|
1229
1239
|
* For propertyValue sorting, dataType is required and the sort key uses the first valid leaf value (value[not(@i)]).
|
|
@@ -1237,7 +1247,6 @@ declare function fetchItem<T extends DataCategory = DataCategory, U extends Data
|
|
|
1237
1247
|
*/
|
|
1238
1248
|
declare function fetchSetItems<U extends Array<DataCategory> = Array<DataCategory>>(params: {
|
|
1239
1249
|
setScopeUuids: Array<string>;
|
|
1240
|
-
propertyVariableUuids: Array<string>;
|
|
1241
1250
|
queries: Array<Query>;
|
|
1242
1251
|
sort?: SetItemsSort;
|
|
1243
1252
|
page: number;
|
|
@@ -1265,7 +1274,6 @@ declare function fetchSetItems<U extends Array<DataCategory> = Array<DataCategor
|
|
|
1265
1274
|
*
|
|
1266
1275
|
* @param params - The parameters for the fetch
|
|
1267
1276
|
* @param params.setScopeUuids - An array of set scope UUIDs to filter by
|
|
1268
|
-
* @param params.propertyVariableUuids - The property variable UUIDs to query by
|
|
1269
1277
|
* @param params.queries - Ordered queries to combine with AND/OR and optional NOT via negation
|
|
1270
1278
|
* @param params.attributes - Whether to return values for bibliographies and periods
|
|
1271
1279
|
* @param params.attributes.bibliographies - Whether to return values for bibliographies
|
|
@@ -1279,7 +1287,6 @@ declare function fetchSetItems<U extends Array<DataCategory> = Array<DataCategor
|
|
|
1279
1287
|
*/
|
|
1280
1288
|
declare function fetchSetPropertyValuesByPropertyVariables(params: {
|
|
1281
1289
|
setScopeUuids: Array<string>;
|
|
1282
|
-
propertyVariableUuids: Array<string>;
|
|
1283
1290
|
queries?: Array<Query>;
|
|
1284
1291
|
attributes?: {
|
|
1285
1292
|
bibliographies: boolean;
|
package/dist/index.mjs
CHANGED
|
@@ -2,18 +2,13 @@ import * as z from "zod";
|
|
|
2
2
|
import { parseISO, set } from "date-fns";
|
|
3
3
|
import { UTCDate } from "@date-fns/utc";
|
|
4
4
|
import { deepEqual } from "fast-equals";
|
|
5
|
-
|
|
6
5
|
//#region src/constants.ts
|
|
7
6
|
const BELONGS_TO_COLLECTION_UUID = "30054cb2-909a-4f34-8db9-8fe7369d691d";
|
|
8
|
-
const PRESENTATION_ITEM_UUID = "f1c131b6-1498-48a4-95bf-a9edae9fd518";
|
|
9
|
-
const TEXT_ANNOTATION_UUID = "b9ca2732-78f4-416e-b77f-dae7647e68a9";
|
|
10
7
|
const TEXT_ANNOTATION_HOVER_CARD_UUID = "c7f6a08a-f07b-49b6-bcb1-af485da3c58f";
|
|
11
8
|
const TEXT_ANNOTATION_ITEM_PAGE_VARIANT_UUID = "bf4476ab-6bc8-40d0-a001-1446213c72ce";
|
|
12
9
|
const TEXT_ANNOTATION_ENTRY_PAGE_VARIANT_UUID = "9d52db95-a9cf-45f7-a0bf-fc9ba9f0aae0";
|
|
13
|
-
const TEXT_ANNOTATION_TEXT_STYLING_UUID = "3e6f86ab-df81-45ae-8257-e2867357df56";
|
|
14
10
|
const TEXT_ANNOTATION_TEXT_STYLING_VARIANT_UUID = "e1647bef-d801-4100-bdde-d081c422f763";
|
|
15
11
|
const TEXT_ANNOTATION_TEXT_STYLING_HEADING_LEVEL_UUID = "d4266f0b-3f8d-4b32-8c15-4b229c8bb11e";
|
|
16
|
-
|
|
17
12
|
//#endregion
|
|
18
13
|
//#region src/utils/string.ts
|
|
19
14
|
const EMAIL_BRACKET_CLEANUP_REGEX = /(?<=\s|^)[([{]+|[)\]}]+(?=\s|$)/g;
|
|
@@ -180,7 +175,7 @@ function extractAnnotationMetadata(item) {
|
|
|
180
175
|
if (itemProperty == null) return result;
|
|
181
176
|
const itemPropertyLabelUuid = itemProperty.label.uuid;
|
|
182
177
|
const itemPropertyValueUuid = typeof itemProperty.value === "object" && "uuid" in itemProperty.value && itemProperty.value.uuid != null ? itemProperty.value.uuid : null;
|
|
183
|
-
if (itemPropertyLabelUuid !==
|
|
178
|
+
if (itemPropertyLabelUuid !== "f1c131b6-1498-48a4-95bf-a9edae9fd518" || itemPropertyValueUuid !== "b9ca2732-78f4-416e-b77f-dae7647e68a9") return result;
|
|
184
179
|
const textAnnotationProperties = itemProperty.property != null ? Array.isArray(itemProperty.property) ? itemProperty.property : [itemProperty.property] : [];
|
|
185
180
|
for (const textAnnotationProperty of textAnnotationProperties) {
|
|
186
181
|
const textAnnotationPropertyValueUuid = typeof textAnnotationProperty.value === "object" && "uuid" in textAnnotationProperty.value && textAnnotationProperty.value.uuid != null ? textAnnotationProperty.value.uuid : null;
|
|
@@ -194,7 +189,7 @@ function extractAnnotationMetadata(item) {
|
|
|
194
189
|
case TEXT_ANNOTATION_ENTRY_PAGE_VARIANT_UUID:
|
|
195
190
|
result.linkVariant = "entry-page";
|
|
196
191
|
break;
|
|
197
|
-
default: if (textAnnotationPropertyValueUuid ===
|
|
192
|
+
default: if (textAnnotationPropertyValueUuid === "3e6f86ab-df81-45ae-8257-e2867357df56" && textAnnotationProperty.property != null) {
|
|
198
193
|
let textStylingVariant = "block";
|
|
199
194
|
let textStylingSize = "md";
|
|
200
195
|
let textStylingHeadingLevel = null;
|
|
@@ -211,7 +206,7 @@ function extractAnnotationMetadata(item) {
|
|
|
211
206
|
}
|
|
212
207
|
const textStylingHeadingLevelProperty = textStylingProperties.find((property) => property.label.uuid === TEXT_ANNOTATION_TEXT_STYLING_HEADING_LEVEL_UUID);
|
|
213
208
|
if (textStylingHeadingLevelProperty != null) textStylingHeadingLevel = parseFakeString(textStylingHeadingLevelProperty.value.content);
|
|
214
|
-
const textStylingCssProperties = textStylingProperties.filter((property) => property.label.uuid !==
|
|
209
|
+
const textStylingCssProperties = textStylingProperties.filter((property) => property.label.uuid !== "e1647bef-d801-4100-bdde-d081c422f763" && property.label.uuid !== "d4266f0b-3f8d-4b32-8c15-4b229c8bb11e");
|
|
215
210
|
if (textStylingCssProperties.length > 0) textStylingCss = textStylingCssProperties.map((property) => ({
|
|
216
211
|
label: parseFakeString(property.label.content),
|
|
217
212
|
value: parseFakeString(property.value.content)
|
|
@@ -371,10 +366,10 @@ function parseStringDocumentItem(item) {
|
|
|
371
366
|
if (itemProperty != null) {
|
|
372
367
|
const itemPropertyLabelUuid = itemProperty.label.uuid;
|
|
373
368
|
const itemPropertyValueUuid = typeof itemProperty.value === "object" && "uuid" in itemProperty.value && itemProperty.value.uuid != null ? itemProperty.value.uuid : null;
|
|
374
|
-
if (itemPropertyLabelUuid ===
|
|
369
|
+
if (itemPropertyLabelUuid === "f1c131b6-1498-48a4-95bf-a9edae9fd518" && itemPropertyValueUuid === "b9ca2732-78f4-416e-b77f-dae7647e68a9") {
|
|
375
370
|
const textAnnotationProperty = itemProperty.property != null ? Array.isArray(itemProperty.property) ? itemProperty.property[0] : itemProperty.property : null;
|
|
376
371
|
if (textAnnotationProperty != null) {
|
|
377
|
-
if ((typeof textAnnotationProperty.value === "object" && "uuid" in textAnnotationProperty.value && textAnnotationProperty.value.uuid != null ? textAnnotationProperty.value.uuid : null) ===
|
|
372
|
+
if ((typeof textAnnotationProperty.value === "object" && "uuid" in textAnnotationProperty.value && textAnnotationProperty.value.uuid != null ? textAnnotationProperty.value.uuid : null) === "3e6f86ab-df81-45ae-8257-e2867357df56" && textAnnotationProperty.property != null) {
|
|
378
373
|
const textStylingType = "text-styling";
|
|
379
374
|
let textStylingVariant = "block";
|
|
380
375
|
let textStylingSize = "md";
|
|
@@ -391,7 +386,7 @@ function parseStringDocumentItem(item) {
|
|
|
391
386
|
}
|
|
392
387
|
const textStylingHeadingLevelProperty = textStylingProperties.find((property) => property.label.uuid === TEXT_ANNOTATION_TEXT_STYLING_HEADING_LEVEL_UUID);
|
|
393
388
|
if (textStylingHeadingLevelProperty != null) textStylingHeadingLevel = parseFakeString(textStylingHeadingLevelProperty.value.content);
|
|
394
|
-
const textStylingCssProperties = textStylingProperties.filter((property) => property.label.uuid !==
|
|
389
|
+
const textStylingCssProperties = textStylingProperties.filter((property) => property.label.uuid !== "e1647bef-d801-4100-bdde-d081c422f763" && property.label.uuid !== "d4266f0b-3f8d-4b32-8c15-4b229c8bb11e");
|
|
395
390
|
if (textStylingCssProperties.length > 0) textStylingCss = textStylingCssProperties.map((property) => ({
|
|
396
391
|
label: parseFakeString(property.label.content),
|
|
397
392
|
value: parseFakeString(property.value.content)
|
|
@@ -442,7 +437,6 @@ function parseStringContent(content, language = "eng") {
|
|
|
442
437
|
default: return String(content.content);
|
|
443
438
|
}
|
|
444
439
|
}
|
|
445
|
-
|
|
446
440
|
//#endregion
|
|
447
441
|
//#region src/utils/internal.ts
|
|
448
442
|
const PSEUDO_UUID_REGEX = /^[\da-f]{8}(?:-[\da-f]{4}){3}-[\da-f]{12}$/i;
|
|
@@ -572,7 +566,6 @@ function cleanObject(object) {
|
|
|
572
566
|
function stringLiteral(value) {
|
|
573
567
|
return `"${value.replaceAll("\"", "\"\"")}"`;
|
|
574
568
|
}
|
|
575
|
-
|
|
576
569
|
//#endregion
|
|
577
570
|
//#region src/utils/helpers.ts
|
|
578
571
|
/**
|
|
@@ -606,7 +599,6 @@ function flattenItemProperties(item) {
|
|
|
606
599
|
properties: flattenProperties(allProperties)
|
|
607
600
|
};
|
|
608
601
|
}
|
|
609
|
-
|
|
610
602
|
//#endregion
|
|
611
603
|
//#region src/schemas.ts
|
|
612
604
|
/**
|
|
@@ -636,25 +628,13 @@ const richTextStringSchema = z.object({
|
|
|
636
628
|
]),
|
|
637
629
|
lang: z.string().optional()
|
|
638
630
|
});
|
|
639
|
-
|
|
640
|
-
* Schema for validating identification
|
|
641
|
-
* @internal
|
|
642
|
-
*/
|
|
643
|
-
const identificationSchema = z.object({
|
|
631
|
+
z.object({
|
|
644
632
|
label: z.object({ content: z.union([richTextStringSchema, z.array(richTextStringSchema)]) }),
|
|
645
633
|
abbreviation: z.object({ content: z.union([richTextStringSchema, z.array(richTextStringSchema)]).optional() }),
|
|
646
634
|
code: z.string().optional()
|
|
647
635
|
});
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
* @internal
|
|
651
|
-
*/
|
|
652
|
-
const filterSchema = z.string().optional();
|
|
653
|
-
/**
|
|
654
|
-
* Schema for validating data options
|
|
655
|
-
* @internal
|
|
656
|
-
*/
|
|
657
|
-
const dataOptionsSchema = z.object({
|
|
636
|
+
z.string().optional();
|
|
637
|
+
z.object({
|
|
658
638
|
filter: z.string().optional().default(""),
|
|
659
639
|
start: z.number().positive({ error: "Start must be positive" }).optional().default(1),
|
|
660
640
|
limit: z.number().positive({ error: "Limit must be positive" }).optional().default(40)
|
|
@@ -778,7 +758,8 @@ const boundsSchema = z.string().transform((str, ctx) => {
|
|
|
778
758
|
*/
|
|
779
759
|
const setQuerySchema = z.union([
|
|
780
760
|
z.object({
|
|
781
|
-
target: z.literal("
|
|
761
|
+
target: z.literal("property"),
|
|
762
|
+
propertyVariables: z.array(uuidSchema).min(1, "At least one property variable UUID is required"),
|
|
782
763
|
dataType: z.enum([
|
|
783
764
|
"string",
|
|
784
765
|
"integer",
|
|
@@ -787,7 +768,7 @@ const setQuerySchema = z.union([
|
|
|
787
768
|
"time",
|
|
788
769
|
"IDREF"
|
|
789
770
|
]),
|
|
790
|
-
|
|
771
|
+
propertyValues: z.array(z.string()).min(1, "At least one property value is required").optional(),
|
|
791
772
|
matchMode: z.enum(["includes", "exact"]),
|
|
792
773
|
isCaseSensitive: z.boolean(),
|
|
793
774
|
language: z.string().default("eng"),
|
|
@@ -795,9 +776,9 @@ const setQuerySchema = z.union([
|
|
|
795
776
|
isNegated: z.boolean().optional().default(false)
|
|
796
777
|
}).strict(),
|
|
797
778
|
z.object({
|
|
798
|
-
target: z.literal("
|
|
779
|
+
target: z.literal("property"),
|
|
780
|
+
propertyVariables: z.array(uuidSchema).min(1, "At least one property variable UUID is required"),
|
|
799
781
|
dataType: z.enum(["date", "dateTime"]),
|
|
800
|
-
value: z.string(),
|
|
801
782
|
from: z.string(),
|
|
802
783
|
to: z.string().optional(),
|
|
803
784
|
matchMode: z.enum(["includes", "exact"]),
|
|
@@ -807,9 +788,9 @@ const setQuerySchema = z.union([
|
|
|
807
788
|
isNegated: z.boolean().optional().default(false)
|
|
808
789
|
}).strict(),
|
|
809
790
|
z.object({
|
|
810
|
-
target: z.literal("
|
|
791
|
+
target: z.literal("property"),
|
|
792
|
+
propertyVariables: z.array(uuidSchema).min(1, "At least one property variable UUID is required"),
|
|
811
793
|
dataType: z.enum(["date", "dateTime"]),
|
|
812
|
-
value: z.string(),
|
|
813
794
|
from: z.string().optional(),
|
|
814
795
|
to: z.string(),
|
|
815
796
|
matchMode: z.enum(["includes", "exact"]),
|
|
@@ -818,6 +799,15 @@ const setQuerySchema = z.union([
|
|
|
818
799
|
operator: z.enum(["AND", "OR"]).optional(),
|
|
819
800
|
isNegated: z.boolean().optional().default(false)
|
|
820
801
|
}).strict(),
|
|
802
|
+
z.object({
|
|
803
|
+
target: z.literal("string"),
|
|
804
|
+
value: z.string(),
|
|
805
|
+
matchMode: z.enum(["includes", "exact"]),
|
|
806
|
+
isCaseSensitive: z.boolean(),
|
|
807
|
+
language: z.string().default("eng"),
|
|
808
|
+
operator: z.enum(["AND", "OR"]).optional(),
|
|
809
|
+
isNegated: z.boolean().optional().default(false)
|
|
810
|
+
}).strict(),
|
|
821
811
|
z.object({
|
|
822
812
|
target: z.enum([
|
|
823
813
|
"title",
|
|
@@ -877,7 +867,6 @@ function validateSetQueriesOperators(queries, ctx) {
|
|
|
877
867
|
const setPropertyValuesByPropertyVariablesParamsSchema = z.object({
|
|
878
868
|
setScopeUuids: z.array(uuidSchema).min(1, "At least one set scope UUID is required"),
|
|
879
869
|
belongsToCollectionScopeUuids: z.array(uuidSchema).default([]),
|
|
880
|
-
propertyVariableUuids: z.array(uuidSchema).min(1, "At least one property variable UUID is required"),
|
|
881
870
|
queries: setQueriesSchema,
|
|
882
871
|
attributes: z.object({
|
|
883
872
|
bibliographies: z.boolean().default(false),
|
|
@@ -889,19 +878,22 @@ const setPropertyValuesByPropertyVariablesParamsSchema = z.object({
|
|
|
889
878
|
isLimitedToLeafPropertyValues: z.boolean().default(false)
|
|
890
879
|
}).superRefine((value, ctx) => {
|
|
891
880
|
validateSetQueriesOperators(value.queries, ctx);
|
|
881
|
+
if (!value.queries.some((query) => query.target === "property")) ctx.addIssue({
|
|
882
|
+
code: "custom",
|
|
883
|
+
path: ["queries"],
|
|
884
|
+
message: "At least one property query is required"
|
|
885
|
+
});
|
|
892
886
|
});
|
|
893
887
|
const setItemsParamsSchema = z.object({
|
|
894
888
|
setScopeUuids: z.array(uuidSchema).min(1, "At least one set scope UUID is required"),
|
|
895
889
|
belongsToCollectionScopeUuids: z.array(uuidSchema).default([]),
|
|
896
|
-
propertyVariableUuids: z.array(uuidSchema).default([]),
|
|
897
890
|
queries: setQueriesSchema,
|
|
898
891
|
sort: setItemsSortSchema,
|
|
899
892
|
page: z.number().min(1, "Page must be positive").default(1),
|
|
900
|
-
pageSize: z.number().min(1, "Page size must be positive").default(
|
|
893
|
+
pageSize: z.number().min(1, "Page size must be positive").default(48)
|
|
901
894
|
}).superRefine((value, ctx) => {
|
|
902
895
|
validateSetQueriesOperators(value.queries, ctx);
|
|
903
896
|
});
|
|
904
|
-
|
|
905
897
|
//#endregion
|
|
906
898
|
//#region src/utils/parse/index.ts
|
|
907
899
|
const TRAILING_ELLIPSIS_REGEX = /\s*\.{3}$/;
|
|
@@ -1977,7 +1969,6 @@ function parseConcept(concept, metadata, persistentUrl, belongsTo) {
|
|
|
1977
1969
|
function parseConcepts(concepts) {
|
|
1978
1970
|
return concepts.map((concept) => parseConcept(concept));
|
|
1979
1971
|
}
|
|
1980
|
-
|
|
1981
1972
|
//#endregion
|
|
1982
1973
|
//#region src/utils/fetchers/gallery.ts
|
|
1983
1974
|
/**
|
|
@@ -2004,7 +1995,7 @@ const galleryParamsSchema = z.object({
|
|
|
2004
1995
|
*/
|
|
2005
1996
|
async function fetchGallery(params, options) {
|
|
2006
1997
|
try {
|
|
2007
|
-
const version = options?.version ??
|
|
1998
|
+
const version = options?.version ?? 2;
|
|
2008
1999
|
const { uuid, filter, page, pageSize } = galleryParamsSchema.parse(params);
|
|
2009
2000
|
const response = await (options?.fetch ?? fetch)(`${version === 2 ? "https://ochre.lib.uchicago.edu/ochre/v2/ochre.php" : "https://ochre.lib.uchicago.edu/ochre"}?xquery=${encodeURIComponent(`<ochre>{
|
|
2010
2001
|
for $q in ${version === 2 ? "doc()" : "input()"}/ochre[@uuid='${uuid}']
|
|
@@ -2036,7 +2027,6 @@ async function fetchGallery(params, options) {
|
|
|
2036
2027
|
};
|
|
2037
2028
|
}
|
|
2038
2029
|
}
|
|
2039
|
-
|
|
2040
2030
|
//#endregion
|
|
2041
2031
|
//#region src/utils/fetchers/uuid.ts
|
|
2042
2032
|
/**
|
|
@@ -2048,7 +2038,7 @@ async function fetchGallery(params, options) {
|
|
|
2048
2038
|
*/
|
|
2049
2039
|
async function fetchByUuid(uuid, options) {
|
|
2050
2040
|
try {
|
|
2051
|
-
const version = options?.version ??
|
|
2041
|
+
const version = options?.version ?? 2;
|
|
2052
2042
|
const parsedUuid = uuidSchema.parse(uuid);
|
|
2053
2043
|
const response = await (options?.fetch ?? fetch)(version === 2 ? `https://ochre.lib.uchicago.edu/ochre/v2/ochre.php?uuid=${parsedUuid}&format=json&lang="*"` : `https://ochre.lib.uchicago.edu/ochre?uuid=${parsedUuid}&format=json&lang="*"`);
|
|
2054
2044
|
if (!response.ok) throw new Error("Failed to fetch OCHRE data");
|
|
@@ -2059,7 +2049,6 @@ async function fetchByUuid(uuid, options) {
|
|
|
2059
2049
|
return [error instanceof Error ? error.message : "Unknown error", null];
|
|
2060
2050
|
}
|
|
2061
2051
|
}
|
|
2062
|
-
|
|
2063
2052
|
//#endregion
|
|
2064
2053
|
//#region src/utils/fetchers/item.ts
|
|
2065
2054
|
/**
|
|
@@ -2070,7 +2059,7 @@ async function fetchByUuid(uuid, options) {
|
|
|
2070
2059
|
*/
|
|
2071
2060
|
async function fetchItem(uuid, category, itemCategories, options) {
|
|
2072
2061
|
try {
|
|
2073
|
-
const version = options?.version ??
|
|
2062
|
+
const version = options?.version ?? 2;
|
|
2074
2063
|
const [error, data] = await fetchByUuid(uuid, {
|
|
2075
2064
|
fetch,
|
|
2076
2065
|
version
|
|
@@ -2144,26 +2133,49 @@ async function fetchItem(uuid, category, itemCategories, options) {
|
|
|
2144
2133
|
};
|
|
2145
2134
|
}
|
|
2146
2135
|
}
|
|
2147
|
-
|
|
2148
2136
|
//#endregion
|
|
2149
2137
|
//#region src/utils/fetchers/set/query-helpers.ts
|
|
2150
2138
|
const CTS_INCLUDES_STOP_WORDS = [
|
|
2151
|
-
"of",
|
|
2152
|
-
"the",
|
|
2153
2139
|
"and",
|
|
2140
|
+
"at",
|
|
2154
2141
|
"in",
|
|
2155
|
-
"it"
|
|
2142
|
+
"it",
|
|
2143
|
+
"of",
|
|
2144
|
+
"the",
|
|
2145
|
+
"to"
|
|
2156
2146
|
];
|
|
2157
2147
|
const CTS_INCLUDES_STOP_WORDS_VAR = "$ctsIncludesStopWords";
|
|
2148
|
+
function buildFlattenedContentValuesExpression(contentNodesExpression) {
|
|
2149
|
+
return `for $content in ${contentNodesExpression}
|
|
2150
|
+
return string-join($content//text(), "")`;
|
|
2151
|
+
}
|
|
2158
2152
|
/**
|
|
2159
2153
|
* Build a string match predicate for an XQuery string.
|
|
2160
2154
|
*/
|
|
2161
2155
|
function buildRawStringMatchPredicate(params) {
|
|
2162
|
-
const {
|
|
2163
|
-
const comparedPath = isCaseSensitive ? path : `lower-case(${path})`;
|
|
2156
|
+
const { valueExpression, value, matchMode, isCaseSensitive } = params;
|
|
2164
2157
|
const comparedValueLiteral = stringLiteral(isCaseSensitive ? value : value.toLowerCase());
|
|
2165
|
-
|
|
2166
|
-
|
|
2158
|
+
const candidateVar = "$candidate";
|
|
2159
|
+
const comparedCandidate = isCaseSensitive ? candidateVar : `lower-case(${candidateVar})`;
|
|
2160
|
+
if (matchMode === "includes") return `some ${candidateVar} in (${valueExpression})
|
|
2161
|
+
satisfies contains(${comparedCandidate}, ${comparedValueLiteral})`;
|
|
2162
|
+
return `some ${candidateVar} in (${valueExpression})
|
|
2163
|
+
satisfies ${comparedCandidate} = ${comparedValueLiteral}`;
|
|
2164
|
+
}
|
|
2165
|
+
/**
|
|
2166
|
+
* Build a combined raw string match predicate for multiple paths.
|
|
2167
|
+
*/
|
|
2168
|
+
function buildCombinedRawStringMatchPredicate(params) {
|
|
2169
|
+
const { valueExpressions, value, matchMode, isCaseSensitive } = params;
|
|
2170
|
+
const predicates = [];
|
|
2171
|
+
for (const valueExpression of valueExpressions) predicates.push(buildRawStringMatchPredicate({
|
|
2172
|
+
valueExpression,
|
|
2173
|
+
value,
|
|
2174
|
+
matchMode,
|
|
2175
|
+
isCaseSensitive
|
|
2176
|
+
}));
|
|
2177
|
+
if (predicates.length === 1) return predicates[0] ?? "false()";
|
|
2178
|
+
return `(${predicates.join(" or ")})`;
|
|
2167
2179
|
}
|
|
2168
2180
|
/**
|
|
2169
2181
|
* Build CTS word-query options for API v2 includes search.
|
|
@@ -2172,70 +2184,204 @@ function buildCtsQueryOptionsExpression(isCaseSensitive) {
|
|
|
2172
2184
|
return `(${[
|
|
2173
2185
|
isCaseSensitive ? "case-sensitive" : "case-insensitive",
|
|
2174
2186
|
"diacritic-insensitive",
|
|
2175
|
-
"punctuation-insensitive"
|
|
2176
|
-
"whitespace-insensitive",
|
|
2177
|
-
"stemmed"
|
|
2187
|
+
"punctuation-insensitive"
|
|
2178
2188
|
].map((option) => stringLiteral(option)).join(", ")})`;
|
|
2179
2189
|
}
|
|
2180
2190
|
/**
|
|
2181
|
-
* Build a CTS-
|
|
2191
|
+
* Build a CTS word-query expression for an XQuery term.
|
|
2182
2192
|
*/
|
|
2183
|
-
function
|
|
2184
|
-
const {
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
})
|
|
2193
|
+
function buildCtsWordQueryExpression(params) {
|
|
2194
|
+
const { termExpression, isCaseSensitive } = params;
|
|
2195
|
+
return `cts:word-query(${termExpression}, ${buildCtsQueryOptionsExpression(isCaseSensitive)})`;
|
|
2196
|
+
}
|
|
2197
|
+
/**
|
|
2198
|
+
* Build tokenized search declarations for CTS-backed queries.
|
|
2199
|
+
*/
|
|
2200
|
+
function buildTokenizedSearchDeclarations(params) {
|
|
2201
|
+
const { value, isCaseSensitive, queryKey } = params;
|
|
2202
|
+
const searchStringVar = `$query${queryKey}SearchString`;
|
|
2203
|
+
const rawTermsVar = `$query${queryKey}RawTerms`;
|
|
2204
|
+
const termsVar = `$query${queryKey}Terms`;
|
|
2205
|
+
const tokenSourceExpression = isCaseSensitive ? searchStringVar : `fn:lower-case(${searchStringVar})`;
|
|
2196
2206
|
return {
|
|
2197
2207
|
declarations: [
|
|
2198
2208
|
`let ${searchStringVar} := ${stringLiteral(value)}`,
|
|
2199
|
-
String.raw`let ${rawTermsVar} := fn:tokenize(${
|
|
2209
|
+
String.raw`let ${rawTermsVar} := fn:tokenize(${tokenSourceExpression}, "\W+")`,
|
|
2200
2210
|
`let ${termsVar} :=
|
|
2201
2211
|
for $term in ${rawTermsVar}
|
|
2202
2212
|
let $normalizedTerm := fn:lower-case($term)
|
|
2203
2213
|
where $normalizedTerm ne "" and not($normalizedTerm = ${CTS_INCLUDES_STOP_WORDS_VAR})
|
|
2204
|
-
return $
|
|
2214
|
+
return $term`
|
|
2215
|
+
],
|
|
2216
|
+
termsVar
|
|
2217
|
+
};
|
|
2218
|
+
}
|
|
2219
|
+
/**
|
|
2220
|
+
* Build a CTS-backed field includes predicate for an XQuery string.
|
|
2221
|
+
*/
|
|
2222
|
+
function buildCtsFieldIncludesPredicate(params) {
|
|
2223
|
+
const { contentNodesExpression, valueExpression, value, isCaseSensitive, queryKey } = params;
|
|
2224
|
+
const tokenizedSearchDeclarations = buildTokenizedSearchDeclarations({
|
|
2225
|
+
value,
|
|
2226
|
+
isCaseSensitive,
|
|
2227
|
+
queryKey
|
|
2228
|
+
});
|
|
2229
|
+
const ctsQueryVar = `$query${queryKey}CtsQuery`;
|
|
2230
|
+
const contentNodeVar = `$query${queryKey}ContentNode`;
|
|
2231
|
+
const fallbackPredicate = buildRawStringMatchPredicate({
|
|
2232
|
+
valueExpression,
|
|
2233
|
+
value,
|
|
2234
|
+
matchMode: "includes",
|
|
2235
|
+
isCaseSensitive
|
|
2236
|
+
});
|
|
2237
|
+
return {
|
|
2238
|
+
declarations: [...tokenizedSearchDeclarations.declarations, `let ${ctsQueryVar} :=
|
|
2239
|
+
if (count(${tokenizedSearchDeclarations.termsVar}) = 1)
|
|
2240
|
+
then ${buildCtsWordQueryExpression({
|
|
2241
|
+
termExpression: `${tokenizedSearchDeclarations.termsVar}[1]`,
|
|
2242
|
+
isCaseSensitive
|
|
2243
|
+
})}
|
|
2244
|
+
else if (count(${tokenizedSearchDeclarations.termsVar}) gt 1)
|
|
2245
|
+
then cts:and-query((
|
|
2246
|
+
for $term in ${tokenizedSearchDeclarations.termsVar}
|
|
2247
|
+
return ${buildCtsWordQueryExpression({
|
|
2248
|
+
termExpression: "$term",
|
|
2249
|
+
isCaseSensitive
|
|
2250
|
+
})}
|
|
2251
|
+
))
|
|
2252
|
+
else ()`],
|
|
2253
|
+
predicate: `(if (exists(${ctsQueryVar}))
|
|
2254
|
+
then some ${contentNodeVar} in (${contentNodesExpression})
|
|
2255
|
+
satisfies cts:contains(${contentNodeVar}, ${ctsQueryVar})
|
|
2256
|
+
else ${fallbackPredicate})`
|
|
2257
|
+
};
|
|
2258
|
+
}
|
|
2259
|
+
/**
|
|
2260
|
+
* Build the raw search paths for item-level string search.
|
|
2261
|
+
*/
|
|
2262
|
+
function buildItemStringSearchPaths(language) {
|
|
2263
|
+
return [buildFlattenedContentValuesExpression(`identification/label/content[@xml:lang="${language}"]`), buildFlattenedContentValuesExpression(`properties//property/value[not(@inherited="true")]/content[@xml:lang="${language}"]`)];
|
|
2264
|
+
}
|
|
2265
|
+
/**
|
|
2266
|
+
* Build the identification branch for an item-level CTS string search.
|
|
2267
|
+
*/
|
|
2268
|
+
function buildItemStringIdentificationBranch(params) {
|
|
2269
|
+
const { termExpression, isCaseSensitive, language } = params;
|
|
2270
|
+
return `cts:element-query(xs:QName("identification"),
|
|
2271
|
+
cts:and-query((
|
|
2272
|
+
cts:element-attribute-value-query(xs:QName("content"), xs:QName("xml:lang"), ${stringLiteral(language)}),
|
|
2273
|
+
${buildCtsWordQueryExpression({
|
|
2274
|
+
termExpression,
|
|
2275
|
+
isCaseSensitive
|
|
2276
|
+
})}
|
|
2277
|
+
))
|
|
2278
|
+
)`;
|
|
2279
|
+
}
|
|
2280
|
+
/**
|
|
2281
|
+
* Build the property value branch for an item-level CTS string search.
|
|
2282
|
+
*/
|
|
2283
|
+
function buildItemStringPropertyValueBranch(params) {
|
|
2284
|
+
const { termExpression, isCaseSensitive, language } = params;
|
|
2285
|
+
return `cts:element-query(xs:QName("properties"),
|
|
2286
|
+
cts:element-query(xs:QName("property"),
|
|
2287
|
+
cts:element-query(xs:QName("value"),
|
|
2288
|
+
cts:and-query((
|
|
2289
|
+
cts:not-query(cts:element-attribute-value-query(xs:QName("value"), xs:QName("inherited"), "true")),
|
|
2290
|
+
cts:element-query(xs:QName("content"),
|
|
2291
|
+
cts:and-query((
|
|
2292
|
+
cts:element-attribute-value-query(xs:QName("content"), xs:QName("xml:lang"), ${stringLiteral(language)}),
|
|
2293
|
+
${buildCtsWordQueryExpression({
|
|
2294
|
+
termExpression,
|
|
2295
|
+
isCaseSensitive
|
|
2296
|
+
})}
|
|
2297
|
+
))
|
|
2298
|
+
)
|
|
2299
|
+
))
|
|
2300
|
+
)
|
|
2301
|
+
)
|
|
2302
|
+
)`;
|
|
2303
|
+
}
|
|
2304
|
+
/**
|
|
2305
|
+
* Build an item-level CTS string search predicate.
|
|
2306
|
+
*/
|
|
2307
|
+
function buildItemStringSearchPredicate(params) {
|
|
2308
|
+
const { query, version, queryKey } = params;
|
|
2309
|
+
const fallbackPredicate = buildCombinedRawStringMatchPredicate({
|
|
2310
|
+
valueExpressions: buildItemStringSearchPaths(query.language),
|
|
2311
|
+
value: query.value,
|
|
2312
|
+
matchMode: query.matchMode,
|
|
2313
|
+
isCaseSensitive: query.isCaseSensitive
|
|
2314
|
+
});
|
|
2315
|
+
if (query.matchMode !== "includes" || version !== 2) return {
|
|
2316
|
+
declarations: [],
|
|
2317
|
+
predicate: fallbackPredicate
|
|
2318
|
+
};
|
|
2319
|
+
const tokenizedSearchDeclarations = buildTokenizedSearchDeclarations({
|
|
2320
|
+
value: query.value,
|
|
2321
|
+
isCaseSensitive: query.isCaseSensitive,
|
|
2322
|
+
queryKey
|
|
2323
|
+
});
|
|
2324
|
+
const termQueriesVar = `$query${queryKey}TermQueries`;
|
|
2325
|
+
const ctsQueryVar = `$query${queryKey}CtsQuery`;
|
|
2326
|
+
return {
|
|
2327
|
+
declarations: [
|
|
2328
|
+
...tokenizedSearchDeclarations.declarations,
|
|
2329
|
+
`let ${termQueriesVar} :=
|
|
2330
|
+
for $term in ${tokenizedSearchDeclarations.termsVar}
|
|
2331
|
+
return
|
|
2332
|
+
cts:or-query((
|
|
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
|
+
))`,
|
|
2205
2344
|
`let ${ctsQueryVar} :=
|
|
2206
|
-
if (count(${termsVar}) = 1)
|
|
2207
|
-
then
|
|
2208
|
-
else if (count(${termsVar}) gt 1)
|
|
2209
|
-
then cts:
|
|
2210
|
-
for $term in ${termsVar}
|
|
2211
|
-
return cts:word-query($term, ${ctsOptionsExpression})
|
|
2212
|
-
), 5, ("unordered"))
|
|
2345
|
+
if (count(${tokenizedSearchDeclarations.termsVar}) = 1)
|
|
2346
|
+
then ${termQueriesVar}[1]
|
|
2347
|
+
else if (count(${tokenizedSearchDeclarations.termsVar}) gt 1)
|
|
2348
|
+
then cts:and-query(${termQueriesVar})
|
|
2213
2349
|
else ()`
|
|
2214
2350
|
],
|
|
2215
|
-
predicate: `(if (exists(${ctsQueryVar})) then cts:contains(
|
|
2351
|
+
predicate: `(if (exists(${ctsQueryVar})) then cts:contains(., ${ctsQueryVar}) else ${fallbackPredicate})`
|
|
2216
2352
|
};
|
|
2217
2353
|
}
|
|
2218
2354
|
/**
|
|
2219
2355
|
* Build a string match predicate for an XQuery string.
|
|
2220
2356
|
*/
|
|
2221
2357
|
function buildStringMatchPredicate(params) {
|
|
2222
|
-
const {
|
|
2223
|
-
|
|
2224
|
-
|
|
2358
|
+
const { contentNodesExpression, value, matchMode, isCaseSensitive, version, queryKey } = params;
|
|
2359
|
+
const valueExpression = buildFlattenedContentValuesExpression(contentNodesExpression);
|
|
2360
|
+
if (matchMode === "includes" && version === 2) return buildCtsFieldIncludesPredicate({
|
|
2361
|
+
contentNodesExpression,
|
|
2362
|
+
valueExpression,
|
|
2225
2363
|
value,
|
|
2226
2364
|
isCaseSensitive,
|
|
2227
|
-
|
|
2365
|
+
queryKey
|
|
2228
2366
|
});
|
|
2229
2367
|
return {
|
|
2230
2368
|
declarations: [],
|
|
2231
2369
|
predicate: buildRawStringMatchPredicate({
|
|
2232
|
-
|
|
2370
|
+
valueExpression,
|
|
2233
2371
|
value,
|
|
2234
2372
|
matchMode,
|
|
2235
2373
|
isCaseSensitive
|
|
2236
2374
|
})
|
|
2237
2375
|
};
|
|
2238
2376
|
}
|
|
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
|
+
}
|
|
2239
2385
|
/**
|
|
2240
2386
|
* Build a date/dateTime range predicate for an XQuery string.
|
|
2241
2387
|
*/
|
|
@@ -2247,88 +2393,139 @@ function buildDateRangePredicate(params) {
|
|
|
2247
2393
|
return conditions.join(" and ");
|
|
2248
2394
|
}
|
|
2249
2395
|
/**
|
|
2250
|
-
* Build a property
|
|
2396
|
+
* Build a property label predicate for an XQuery string.
|
|
2251
2397
|
*/
|
|
2252
|
-
function
|
|
2253
|
-
const
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
};
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
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
|
+
function buildPropertyValueAttributePredicate(params) {
|
|
2404
|
+
const { propertyValues, attributeName } = params;
|
|
2405
|
+
const valuePredicates = [];
|
|
2406
|
+
for (const propertyValue of propertyValues) valuePredicates.push(`value[@${attributeName}=${stringLiteral(propertyValue)}]`);
|
|
2407
|
+
return buildOrPredicate(valuePredicates);
|
|
2408
|
+
}
|
|
2409
|
+
function buildPropertyStringValuePredicate(params) {
|
|
2410
|
+
const { query, version, queryKey } = params;
|
|
2411
|
+
const propertyContentNodesExpression = query.matchMode === "includes" && version === 2 ? `value[not(@inherited="true")]/content[@xml:lang="${query.language}"]` : `value/content[@xml:lang="${query.language}"]`;
|
|
2412
|
+
const declarations = [];
|
|
2413
|
+
const valuePredicates = [];
|
|
2414
|
+
for (const [propertyValueIndex, propertyValue] of query.propertyValues.entries()) {
|
|
2415
|
+
const compiledStringPredicate = buildStringMatchPredicate({
|
|
2416
|
+
contentNodesExpression: propertyContentNodesExpression,
|
|
2417
|
+
value: propertyValue,
|
|
2418
|
+
matchMode: query.matchMode,
|
|
2419
|
+
isCaseSensitive: query.isCaseSensitive,
|
|
2420
|
+
version,
|
|
2421
|
+
queryKey: `${queryKey}_${propertyValueIndex + 1}`
|
|
2422
|
+
});
|
|
2423
|
+
declarations.push(...compiledStringPredicate.declarations);
|
|
2424
|
+
valuePredicates.push(compiledStringPredicate.predicate);
|
|
2425
|
+
}
|
|
2426
|
+
return {
|
|
2427
|
+
declarations,
|
|
2428
|
+
predicate: buildOrPredicate(valuePredicates)
|
|
2268
2429
|
};
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2430
|
+
}
|
|
2431
|
+
/**
|
|
2432
|
+
* Build a property predicate for an XQuery string.
|
|
2433
|
+
*/
|
|
2434
|
+
function buildPropertyPredicate(params) {
|
|
2435
|
+
const { query, version, queryKey } = params;
|
|
2436
|
+
const predicateParts = [buildPropertyLabelPredicate(query.propertyVariables)];
|
|
2437
|
+
const declarations = [];
|
|
2438
|
+
if (query.dataType === "date" || query.dataType === "dateTime") predicateParts.push(buildDateRangePredicate({
|
|
2439
|
+
from: query.from,
|
|
2440
|
+
to: query.to
|
|
2441
|
+
}));
|
|
2442
|
+
else if (query.propertyValues != null) switch (query.dataType) {
|
|
2443
|
+
case "IDREF":
|
|
2444
|
+
predicateParts.push(buildPropertyValueAttributePredicate({
|
|
2445
|
+
propertyValues: query.propertyValues,
|
|
2446
|
+
attributeName: "uuid"
|
|
2447
|
+
}));
|
|
2448
|
+
break;
|
|
2449
|
+
case "integer":
|
|
2450
|
+
case "decimal":
|
|
2451
|
+
case "time":
|
|
2452
|
+
case "boolean":
|
|
2453
|
+
predicateParts.push(buildPropertyValueAttributePredicate({
|
|
2454
|
+
propertyValues: query.propertyValues,
|
|
2455
|
+
attributeName: "rawValue"
|
|
2456
|
+
}));
|
|
2457
|
+
break;
|
|
2458
|
+
case "string": {
|
|
2459
|
+
const compiledStringPredicate = buildPropertyStringValuePredicate({
|
|
2460
|
+
query,
|
|
2461
|
+
version,
|
|
2462
|
+
queryKey
|
|
2463
|
+
});
|
|
2464
|
+
declarations.push(...compiledStringPredicate.declarations);
|
|
2465
|
+
predicateParts.push(compiledStringPredicate.predicate);
|
|
2466
|
+
break;
|
|
2467
|
+
}
|
|
2468
|
+
}
|
|
2277
2469
|
return {
|
|
2278
|
-
declarations
|
|
2279
|
-
predicate: `.//properties//property[${
|
|
2470
|
+
declarations,
|
|
2471
|
+
predicate: `.//properties//property[${buildAndPredicate(predicateParts)}]`
|
|
2280
2472
|
};
|
|
2281
2473
|
}
|
|
2282
2474
|
/**
|
|
2283
2475
|
* Build a query predicate for an XQuery string.
|
|
2284
2476
|
*/
|
|
2285
2477
|
function buildQueryPredicate(params) {
|
|
2286
|
-
const { query, version,
|
|
2478
|
+
const { query, version, queryKey } = params;
|
|
2287
2479
|
switch (query.target) {
|
|
2480
|
+
case "string": return buildItemStringSearchPredicate({
|
|
2481
|
+
query,
|
|
2482
|
+
version,
|
|
2483
|
+
queryKey
|
|
2484
|
+
});
|
|
2288
2485
|
case "title": return buildStringMatchPredicate({
|
|
2289
|
-
|
|
2486
|
+
contentNodesExpression: `identification/label/content[@xml:lang="${query.language}"]`,
|
|
2290
2487
|
value: query.value,
|
|
2291
2488
|
matchMode: query.matchMode,
|
|
2292
2489
|
isCaseSensitive: query.isCaseSensitive,
|
|
2293
2490
|
version,
|
|
2294
|
-
|
|
2491
|
+
queryKey
|
|
2295
2492
|
});
|
|
2296
2493
|
case "description": return buildStringMatchPredicate({
|
|
2297
|
-
|
|
2494
|
+
contentNodesExpression: `description/content[@xml:lang="${query.language}"]`,
|
|
2298
2495
|
value: query.value,
|
|
2299
2496
|
matchMode: query.matchMode,
|
|
2300
2497
|
isCaseSensitive: query.isCaseSensitive,
|
|
2301
2498
|
version,
|
|
2302
|
-
|
|
2499
|
+
queryKey
|
|
2303
2500
|
});
|
|
2304
2501
|
case "periods": return buildStringMatchPredicate({
|
|
2305
|
-
|
|
2502
|
+
contentNodesExpression: `periods/period/identification/label/content[@xml:lang="${query.language}"]`,
|
|
2306
2503
|
value: query.value,
|
|
2307
2504
|
matchMode: query.matchMode,
|
|
2308
2505
|
isCaseSensitive: query.isCaseSensitive,
|
|
2309
2506
|
version,
|
|
2310
|
-
|
|
2507
|
+
queryKey
|
|
2311
2508
|
});
|
|
2312
2509
|
case "bibliography": return buildStringMatchPredicate({
|
|
2313
|
-
|
|
2510
|
+
contentNodesExpression: `bibliographies/bibliography/identification/label/content[@xml:lang="${query.language}"]`,
|
|
2314
2511
|
value: query.value,
|
|
2315
2512
|
matchMode: query.matchMode,
|
|
2316
2513
|
isCaseSensitive: query.isCaseSensitive,
|
|
2317
2514
|
version,
|
|
2318
|
-
|
|
2515
|
+
queryKey
|
|
2319
2516
|
});
|
|
2320
2517
|
case "image": return buildStringMatchPredicate({
|
|
2321
|
-
|
|
2518
|
+
contentNodesExpression: `image/identification/label/content[@xml:lang="${query.language}"]`,
|
|
2322
2519
|
value: query.value,
|
|
2323
2520
|
matchMode: query.matchMode,
|
|
2324
2521
|
isCaseSensitive: query.isCaseSensitive,
|
|
2325
2522
|
version,
|
|
2326
|
-
|
|
2523
|
+
queryKey
|
|
2327
2524
|
});
|
|
2328
|
-
case "
|
|
2525
|
+
case "property": return buildPropertyPredicate({
|
|
2329
2526
|
query,
|
|
2330
2527
|
version,
|
|
2331
|
-
|
|
2528
|
+
queryKey
|
|
2332
2529
|
});
|
|
2333
2530
|
}
|
|
2334
2531
|
}
|
|
@@ -2336,11 +2533,11 @@ function buildQueryPredicate(params) {
|
|
|
2336
2533
|
* Build a boolean query clause for an XQuery string.
|
|
2337
2534
|
*/
|
|
2338
2535
|
function buildBooleanQueryClause(params) {
|
|
2339
|
-
const { query, version,
|
|
2536
|
+
const { query, version, queryKey } = params;
|
|
2340
2537
|
const compiledQueryPredicate = buildQueryPredicate({
|
|
2341
2538
|
query,
|
|
2342
2539
|
version,
|
|
2343
|
-
|
|
2540
|
+
queryKey
|
|
2344
2541
|
});
|
|
2345
2542
|
const baseClause = `(${compiledQueryPredicate.predicate})`;
|
|
2346
2543
|
return {
|
|
@@ -2360,7 +2557,7 @@ function buildQueryFilters(params) {
|
|
|
2360
2557
|
const compiledClause = buildBooleanQueryClause({
|
|
2361
2558
|
query,
|
|
2362
2559
|
version,
|
|
2363
|
-
|
|
2560
|
+
queryKey: `${index + 1}`
|
|
2364
2561
|
});
|
|
2365
2562
|
if (compiledClause.declarations.length > 0) {
|
|
2366
2563
|
hasCtsIncludesClauses = true;
|
|
@@ -2378,7 +2575,6 @@ function buildQueryFilters(params) {
|
|
|
2378
2575
|
predicate: predicateParts.join(" ")
|
|
2379
2576
|
};
|
|
2380
2577
|
}
|
|
2381
|
-
|
|
2382
2578
|
//#endregion
|
|
2383
2579
|
//#region src/utils/fetchers/set/items.ts
|
|
2384
2580
|
function mapSortDirectionToXQuery(direction) {
|
|
@@ -2456,7 +2652,6 @@ function buildOrderedItemsClause(sort) {
|
|
|
2456
2652
|
* @param params - The parameters for the fetch
|
|
2457
2653
|
* @param params.setScopeUuids - An array of Set scope UUIDs to filter by
|
|
2458
2654
|
* @param params.belongsToCollectionScopeUuids - An array of collection scope UUIDs to filter by
|
|
2459
|
-
* @param params.propertyVariableUuids - An array of property variable UUIDs to filter by
|
|
2460
2655
|
* @param params.queries - Ordered queries to combine with AND/OR and optional NOT via negation
|
|
2461
2656
|
* @param params.sort - Optional sorting configuration applied before pagination.
|
|
2462
2657
|
* For propertyValue sorting, dataType is required and the sort key uses the first valid leaf value (value[not(@i)]).
|
|
@@ -2467,8 +2662,8 @@ function buildOrderedItemsClause(sort) {
|
|
|
2467
2662
|
* @returns An XQuery string
|
|
2468
2663
|
*/
|
|
2469
2664
|
function buildXQuery$1(params, options) {
|
|
2470
|
-
const version = options?.version ??
|
|
2471
|
-
const {
|
|
2665
|
+
const version = options?.version ?? 2;
|
|
2666
|
+
const { queries, sort, setScopeUuids, belongsToCollectionScopeUuids, page, pageSize } = params;
|
|
2472
2667
|
const startPosition = (page - 1) * pageSize + 1;
|
|
2473
2668
|
const endPosition = page * pageSize;
|
|
2474
2669
|
const setScopeFilter = `/set[(${setScopeUuids.map((uuid) => `@uuid="${uuid}"`).join(" or ")})]/items/*`;
|
|
@@ -2482,10 +2677,6 @@ function buildXQuery$1(params, options) {
|
|
|
2482
2677
|
const belongsToCollectionScopeValues = belongsToCollectionScopeUuids.map((uuid) => `@uuid="${uuid}"`).join(" or ");
|
|
2483
2678
|
filterPredicates.push(`.//properties[property[label/@uuid="${BELONGS_TO_COLLECTION_UUID}" and value/(${belongsToCollectionScopeValues})]]`);
|
|
2484
2679
|
}
|
|
2485
|
-
if (propertyVariableUuids.length > 0) {
|
|
2486
|
-
const propertyVariables = propertyVariableUuids.map((uuid) => `@uuid="${uuid}"`).join(" or ");
|
|
2487
|
-
filterPredicates.push(`.//properties//property[label[${propertyVariables}]]`);
|
|
2488
|
-
}
|
|
2489
2680
|
if (compiledQueryFilters.predicate.length > 0) filterPredicates.push(`(${compiledQueryFilters.predicate})`);
|
|
2490
2681
|
const itemFilters = filterPredicates.length > 0 ? `[${filterPredicates.join(" and ")}]` : "";
|
|
2491
2682
|
const orderedItemsClause = buildOrderedItemsClause(sort);
|
|
@@ -2508,7 +2699,6 @@ function buildXQuery$1(params, options) {
|
|
|
2508
2699
|
*
|
|
2509
2700
|
* @param params - The parameters for the fetch
|
|
2510
2701
|
* @param params.setScopeUuids - The Set scope UUIDs to filter by
|
|
2511
|
-
* @param params.propertyVariableUuids - The property variable UUIDs to filter by
|
|
2512
2702
|
* @param params.queries - Ordered queries to combine with AND/OR and optional NOT via negation
|
|
2513
2703
|
* @param params.sort - Optional sorting configuration applied before pagination.
|
|
2514
2704
|
* For propertyValue sorting, dataType is required and the sort key uses the first valid leaf value (value[not(@i)]).
|
|
@@ -2522,12 +2712,11 @@ function buildXQuery$1(params, options) {
|
|
|
2522
2712
|
*/
|
|
2523
2713
|
async function fetchSetItems(params, itemCategories, options) {
|
|
2524
2714
|
try {
|
|
2525
|
-
const version = options?.version ??
|
|
2526
|
-
const { setScopeUuids, belongsToCollectionScopeUuids,
|
|
2715
|
+
const version = options?.version ?? 2;
|
|
2716
|
+
const { setScopeUuids, belongsToCollectionScopeUuids, queries, sort, page, pageSize } = setItemsParamsSchema.parse(params);
|
|
2527
2717
|
const xquery = buildXQuery$1({
|
|
2528
2718
|
setScopeUuids,
|
|
2529
2719
|
belongsToCollectionScopeUuids,
|
|
2530
|
-
propertyVariableUuids,
|
|
2531
2720
|
queries,
|
|
2532
2721
|
sort,
|
|
2533
2722
|
page,
|
|
@@ -2617,7 +2806,6 @@ async function fetchSetItems(params, itemCategories, options) {
|
|
|
2617
2806
|
};
|
|
2618
2807
|
}
|
|
2619
2808
|
}
|
|
2620
|
-
|
|
2621
2809
|
//#endregion
|
|
2622
2810
|
//#region src/utils/fetchers/set/property-values-by-property-variables.ts
|
|
2623
2811
|
function parsePropertyValueLabel(content) {
|
|
@@ -2691,6 +2879,14 @@ function aggregateAttributeValues(values) {
|
|
|
2691
2879
|
return a.content.localeCompare(b.content);
|
|
2692
2880
|
});
|
|
2693
2881
|
}
|
|
2882
|
+
function getPropertyVariableUuidsFromQueries(queries) {
|
|
2883
|
+
const propertyVariableUuids = /* @__PURE__ */ new Set();
|
|
2884
|
+
for (const query of queries) {
|
|
2885
|
+
if (query.target !== "property") continue;
|
|
2886
|
+
for (const propertyVariableUuid of query.propertyVariables) propertyVariableUuids.add(propertyVariableUuid);
|
|
2887
|
+
}
|
|
2888
|
+
return [...propertyVariableUuids];
|
|
2889
|
+
}
|
|
2694
2890
|
/**
|
|
2695
2891
|
* Schema for a single property value query item in the OCHRE API response
|
|
2696
2892
|
*/
|
|
@@ -2759,7 +2955,6 @@ const responseSchema = z.object({ result: z.union([z.object({ ochre: z.object({
|
|
|
2759
2955
|
* @param params - The parameters for the fetch
|
|
2760
2956
|
* @param params.setScopeUuids - An array of set scope UUIDs to filter by
|
|
2761
2957
|
* @param params.belongsToCollectionScopeUuids - An array of collection scope UUIDs to filter by
|
|
2762
|
-
* @param params.propertyVariableUuids - An array of property variable UUIDs to fetch
|
|
2763
2958
|
* @param params.queries - Ordered queries to combine with AND/OR and optional NOT via negation
|
|
2764
2959
|
* @param params.attributes - Whether to return values for bibliographies and periods
|
|
2765
2960
|
* @param params.attributes.bibliographies - Whether to return values for bibliographies
|
|
@@ -2770,11 +2965,11 @@ const responseSchema = z.object({ result: z.union([z.object({ ochre: z.object({
|
|
|
2770
2965
|
* @returns An XQuery string
|
|
2771
2966
|
*/
|
|
2772
2967
|
function buildXQuery(params, options) {
|
|
2773
|
-
const version = options?.version ??
|
|
2774
|
-
const { setScopeUuids, belongsToCollectionScopeUuids,
|
|
2968
|
+
const version = options?.version ?? 2;
|
|
2969
|
+
const { setScopeUuids, belongsToCollectionScopeUuids, queries, attributes, isLimitedToLeafPropertyValues } = params;
|
|
2775
2970
|
let setScopeFilter = "/set/items/*";
|
|
2776
2971
|
if (setScopeUuids.length > 0) setScopeFilter = `/set[(${setScopeUuids.map((uuid) => `@uuid="${uuid}"`).join(" or ")})]/items/*`;
|
|
2777
|
-
const propertyVariableFilters =
|
|
2972
|
+
const propertyVariableFilters = getPropertyVariableUuidsFromQueries(queries).map((uuid) => `@uuid="${uuid}"`).join(" or ");
|
|
2778
2973
|
const compiledQueryFilters = buildQueryFilters({
|
|
2779
2974
|
queries,
|
|
2780
2975
|
version
|
|
@@ -2795,14 +2990,14 @@ let $property-values :=
|
|
|
2795
2990
|
let $item-uuid := $v/ancestor::*[parent::items]/@uuid
|
|
2796
2991
|
let $variable-uuid := $p/label/@uuid
|
|
2797
2992
|
return <propertyValue uuid="{$v/@uuid}" rawValue="{$v/@rawValue}" dataType="{$v/@dataType}" itemUuid="{$item-uuid}" variableUuid="{$variable-uuid}">{
|
|
2798
|
-
if ($v/content) then string-join($v/content[@xml:lang="eng"]
|
|
2993
|
+
if ($v/content) then string-join($v/content[@xml:lang="eng"]//text(), "") else $v/text()
|
|
2799
2994
|
}</propertyValue>`];
|
|
2800
2995
|
const returnedSequences = ["$property-values"];
|
|
2801
2996
|
if (attributes.bibliographies) {
|
|
2802
2997
|
queryBlocks.push(`let $bibliography-values :=
|
|
2803
2998
|
for $item in $items
|
|
2804
2999
|
for $bibliography in $item/bibliographies/bibliography
|
|
2805
|
-
let $label := string-join($bibliography/identification/label/content[@xml:lang="eng"]
|
|
3000
|
+
let $label := string-join($bibliography/identification/label/content[@xml:lang="eng"]//text(), "")
|
|
2806
3001
|
where string-length($label) gt 0
|
|
2807
3002
|
return <attributeValue attributeType="bibliographies" itemUuid="{$item/@uuid}" content="{$label}" />`);
|
|
2808
3003
|
returnedSequences.push("$bibliography-values");
|
|
@@ -2811,7 +3006,7 @@ let $property-values :=
|
|
|
2811
3006
|
queryBlocks.push(`let $period-values :=
|
|
2812
3007
|
for $item in $items
|
|
2813
3008
|
for $period in $item/periods/period
|
|
2814
|
-
let $label := string-join($period/identification/label/content[@xml:lang="eng"]
|
|
3009
|
+
let $label := string-join($period/identification/label/content[@xml:lang="eng"]//text(), "")
|
|
2815
3010
|
where string-length($label) gt 0
|
|
2816
3011
|
return <attributeValue attributeType="periods" itemUuid="{$item/@uuid}" content="{$label}" />`);
|
|
2817
3012
|
returnedSequences.push("$period-values");
|
|
@@ -2829,7 +3024,6 @@ return (${returnedSequences.join(", ")})`}}</ochre>`;
|
|
|
2829
3024
|
*
|
|
2830
3025
|
* @param params - The parameters for the fetch
|
|
2831
3026
|
* @param params.setScopeUuids - An array of set scope UUIDs to filter by
|
|
2832
|
-
* @param params.propertyVariableUuids - The property variable UUIDs to query by
|
|
2833
3027
|
* @param params.queries - Ordered queries to combine with AND/OR and optional NOT via negation
|
|
2834
3028
|
* @param params.attributes - Whether to return values for bibliographies and periods
|
|
2835
3029
|
* @param params.attributes.bibliographies - Whether to return values for bibliographies
|
|
@@ -2843,12 +3037,11 @@ return (${returnedSequences.join(", ")})`}}</ochre>`;
|
|
|
2843
3037
|
*/
|
|
2844
3038
|
async function fetchSetPropertyValuesByPropertyVariables(params, options) {
|
|
2845
3039
|
try {
|
|
2846
|
-
const version = options?.version ??
|
|
2847
|
-
const { setScopeUuids, belongsToCollectionScopeUuids,
|
|
3040
|
+
const version = options?.version ?? 2;
|
|
3041
|
+
const { setScopeUuids, belongsToCollectionScopeUuids, queries, attributes, isLimitedToLeafPropertyValues } = setPropertyValuesByPropertyVariablesParamsSchema.parse(params);
|
|
2848
3042
|
const xquery = buildXQuery({
|
|
2849
3043
|
setScopeUuids,
|
|
2850
3044
|
belongsToCollectionScopeUuids,
|
|
2851
|
-
propertyVariableUuids,
|
|
2852
3045
|
queries,
|
|
2853
3046
|
attributes,
|
|
2854
3047
|
isLimitedToLeafPropertyValues
|
|
@@ -2911,7 +3104,6 @@ async function fetchSetPropertyValuesByPropertyVariables(params, options) {
|
|
|
2911
3104
|
};
|
|
2912
3105
|
}
|
|
2913
3106
|
}
|
|
2914
|
-
|
|
2915
3107
|
//#endregion
|
|
2916
3108
|
//#region src/utils/getters.ts
|
|
2917
3109
|
const DEFAULT_OPTIONS = { includeNestedProperties: false };
|
|
@@ -3165,7 +3357,6 @@ function filterProperties(property, filter, options = DEFAULT_OPTIONS) {
|
|
|
3165
3357
|
}
|
|
3166
3358
|
return false;
|
|
3167
3359
|
}
|
|
3168
|
-
|
|
3169
3360
|
//#endregion
|
|
3170
3361
|
//#region src/utils/parse/website.ts
|
|
3171
3362
|
const SEGMENT_UNIQUE_SLUG_PREFIX_REGEX = /^\$[^-]*-/;
|
|
@@ -4416,7 +4607,7 @@ function parseContexts(contexts) {
|
|
|
4416
4607
|
}
|
|
4417
4608
|
return contextsParsed;
|
|
4418
4609
|
}
|
|
4419
|
-
function parseWebsite(websiteTree, metadata, belongsTo, { version =
|
|
4610
|
+
function parseWebsite(websiteTree, metadata, belongsTo, { version = 2 } = {}) {
|
|
4420
4611
|
if (!websiteTree.properties) throw new Error("Website properties not found");
|
|
4421
4612
|
if (typeof websiteTree.items === "string" || !("resource" in websiteTree.items)) throw new Error("Website pages not found");
|
|
4422
4613
|
const resources = ensureArray(websiteTree.items.resource);
|
|
@@ -4436,7 +4627,6 @@ function parseWebsite(websiteTree, metadata, belongsTo, { version = DEFAULT_API_
|
|
|
4436
4627
|
properties
|
|
4437
4628
|
};
|
|
4438
4629
|
}
|
|
4439
|
-
|
|
4440
4630
|
//#endregion
|
|
4441
4631
|
//#region src/utils/fetchers/website.ts
|
|
4442
4632
|
const API_VERSION_SUFFIX_REGEX = /-v\d+$/;
|
|
@@ -4449,7 +4639,7 @@ const API_VERSION_SUFFIX_REGEX = /-v\d+$/;
|
|
|
4449
4639
|
function parseApiVersionSuffix(abbreviation) {
|
|
4450
4640
|
if (!API_VERSION_SUFFIX_REGEX.test(abbreviation)) return {
|
|
4451
4641
|
abbreviation,
|
|
4452
|
-
version:
|
|
4642
|
+
version: 2
|
|
4453
4643
|
};
|
|
4454
4644
|
const result = apiVersionSuffixSchema.safeParse(abbreviation.slice(-3));
|
|
4455
4645
|
if (!result.success) throw new Error("Invalid API version suffix");
|
|
@@ -4509,6 +4699,5 @@ async function fetchWebsite(abbreviation, options) {
|
|
|
4509
4699
|
};
|
|
4510
4700
|
}
|
|
4511
4701
|
}
|
|
4512
|
-
|
|
4513
4702
|
//#endregion
|
|
4514
|
-
export { DEFAULT_API_VERSION, DEFAULT_PAGE_SIZE, fetchGallery, fetchItem, fetchSetItems, fetchSetPropertyValuesByPropertyVariables, fetchWebsite, filterProperties, flattenItemProperties, getLeafPropertyValues, getPropertyByLabel, getPropertyByLabelAndValue, getPropertyByLabelAndValues, getPropertyByUuid, getPropertyValueByLabel, getPropertyValueByUuid, getPropertyValuesByLabel, getPropertyValuesByUuid, getUniqueProperties, getUniquePropertyLabels };
|
|
4703
|
+
export { DEFAULT_API_VERSION, DEFAULT_PAGE_SIZE, fetchGallery, fetchItem, fetchSetItems, fetchSetPropertyValuesByPropertyVariables, fetchWebsite, filterProperties, flattenItemProperties, getLeafPropertyValues, getPropertyByLabel, getPropertyByLabelAndValue, getPropertyByLabelAndValues, getPropertyByUuid, getPropertyValueByLabel, getPropertyValueByUuid, getPropertyValuesByLabel, getPropertyValuesByUuid, getUniqueProperties, getUniquePropertyLabels };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ochre-sdk",
|
|
3
|
-
"version": "0.20.
|
|
3
|
+
"version": "0.20.24",
|
|
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",
|
|
@@ -46,12 +46,12 @@
|
|
|
46
46
|
"zod": "^4.3.6"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
|
-
"@antfu/eslint-config": "^7.7.
|
|
49
|
+
"@antfu/eslint-config": "^7.7.3",
|
|
50
50
|
"@types/node": "^24.12.0",
|
|
51
|
-
"bumpp": "^
|
|
52
|
-
"eslint": "^10.0
|
|
51
|
+
"bumpp": "^11.0.1",
|
|
52
|
+
"eslint": "^10.1.0",
|
|
53
53
|
"prettier": "^3.8.1",
|
|
54
|
-
"tsdown": "^0.
|
|
54
|
+
"tsdown": "^0.21.4",
|
|
55
55
|
"typescript": "^5.9.3"
|
|
56
56
|
},
|
|
57
57
|
"scripts": {
|