ochre-sdk 1.0.71 → 1.0.73
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/README.md +22 -0
- package/dist/constants.mjs +0 -5
- package/dist/fetchers/gallery.mjs +11 -5
- package/dist/fetchers/item-children.mjs +4 -2
- package/dist/fetchers/item-links.mjs +10 -5
- package/dist/fetchers/item-ocr-data.d.mts +37 -0
- package/dist/fetchers/item-ocr-data.mjs +166 -0
- package/dist/fetchers/item.mjs +43 -58
- package/dist/fetchers/set/items.mjs +26 -12
- package/dist/fetchers/set/property-values.mjs +24 -14
- package/dist/fetchers/website-metadata.mjs +7 -3
- package/dist/fetchers/website.mjs +20 -3
- package/dist/helpers.d.mts +1 -5
- package/dist/helpers.mjs +1 -5
- package/dist/index.d.mts +4 -4
- package/dist/index.mjs +3 -3
- package/dist/parsers/index.d.mts +3 -10
- package/dist/parsers/index.mjs +1 -75
- package/dist/parsers/string.mjs +5 -1
- package/dist/query.d.mts +19 -28
- package/dist/query.mjs +104 -86
- package/dist/schemas.d.mts +7 -8
- package/dist/schemas.mjs +8 -10
- package/dist/types/index.d.mts +27 -57
- package/dist/utilities.d.mts +25 -1
- package/dist/utilities.mjs +41 -1
- package/dist/xml/schemas.d.mts +2 -7
- package/dist/xml/schemas.mjs +9 -43
- package/dist/xml/types.d.mts +13 -49
- package/package.json +3 -3
- package/dist/fetchers/ocr-matches.d.mts +0 -44
- package/dist/fetchers/ocr-matches.mjs +0 -134
package/dist/xml/schemas.mjs
CHANGED
|
@@ -42,19 +42,23 @@ const ITEM_CATEGORIES = [
|
|
|
42
42
|
"set"
|
|
43
43
|
];
|
|
44
44
|
const XMLItemCategory = v.picklist(ITEM_CATEGORIES);
|
|
45
|
+
const XMLRichTextEnvelope = {
|
|
46
|
+
links: v.optional(v.lazy(() => XMLLink)),
|
|
47
|
+
properties: v.optional(v.object({ property: v.array(v.lazy(() => XMLProperty), "XMLRichTextEnvelope: properties is array of XMLProperty") })),
|
|
48
|
+
annotation: v.optional(v.string("XMLRichTextEnvelope: annotation is string and optional"))
|
|
49
|
+
};
|
|
45
50
|
const XMLString = v.lazy(() => v.object({
|
|
46
51
|
payload: v.optional(v.string("XMLString: payload is string and optional")),
|
|
47
52
|
rend: v.optional(v.string("XMLString: rend is string and optional")),
|
|
48
53
|
whitespace: v.optional(v.string("XMLString: whitespace is string and optional")),
|
|
49
|
-
|
|
50
|
-
properties: v.optional(v.object({ property: v.array(v.lazy(() => XMLProperty), "XMLString: properties is array of XMLProperty") })),
|
|
51
|
-
annotation: v.optional(v.string("XMLString: annotation is string and optional")),
|
|
54
|
+
...XMLRichTextEnvelope,
|
|
52
55
|
string: v.optional(v.array(XMLString, "XMLString: string is array of XMLString"))
|
|
53
56
|
}, "XMLString: Shape error"));
|
|
54
57
|
const XMLContent = v.object({ content: v.array(v.object({
|
|
55
58
|
string: v.array(XMLString),
|
|
56
59
|
title: v.optional(v.string("XMLContent: title is string and optional")),
|
|
57
|
-
lang: v.string("XMLContent: lang is string and required")
|
|
60
|
+
lang: v.string("XMLContent: lang is string and required"),
|
|
61
|
+
...XMLRichTextEnvelope
|
|
58
62
|
}), "XMLContent: content is array of object with string, title, and lang") }, "XMLContent: Shape error");
|
|
59
63
|
const XMLNumber = v.pipe(v.union([v.string("XMLNumber: string is string and required"), XMLString]), v.check(isXMLNumber, "XMLNumber: string is not a number"), v.transform(parseXMLNumber));
|
|
60
64
|
const XMLOptionalNumber = v.optional(v.pipe(v.union([v.string("XMLNumber: string is string and required"), XMLString]), v.check(isOptionalXMLNumber, "XMLNumber: string is not a number"), v.transform(parseOptionalXMLNumber)));
|
|
@@ -220,42 +224,6 @@ const XMLImageMap = v.object({
|
|
|
220
224
|
width: XMLNumber,
|
|
221
225
|
height: XMLNumber
|
|
222
226
|
}, "XMLImageMap: Shape error");
|
|
223
|
-
const XMLOcrString = v.object({
|
|
224
|
-
HPOS: XMLNumber,
|
|
225
|
-
VPOS: XMLNumber,
|
|
226
|
-
WIDTH: XMLNumber,
|
|
227
|
-
HEIGHT: XMLNumber,
|
|
228
|
-
CONTENT: v.string("XMLOcrString: CONTENT is string and required"),
|
|
229
|
-
VERTICES: v.optional(v.string("XMLOcrString: VERTICES is string and optional"))
|
|
230
|
-
}, "XMLOcrString: Shape error");
|
|
231
|
-
const XMLOcrTextLine = v.object({ string: v.optional(v.array(XMLOcrString, "XMLOcrTextLine: string is array of XMLOcrString")) }, "XMLOcrTextLine: Shape error");
|
|
232
|
-
const XMLOcrTextBlock = v.object({ TextLine: v.optional(v.array(XMLOcrTextLine, "XMLOcrTextBlock: TextLine is array of XMLOcrTextLine")) }, "XMLOcrTextBlock: Shape error");
|
|
233
|
-
const XMLOcrPage = v.object({
|
|
234
|
-
n: XMLOptionalNumber,
|
|
235
|
-
fileName: v.optional(v.string("XMLOcrPage: fileName is string and optional")),
|
|
236
|
-
WIDTH: XMLOptionalNumber,
|
|
237
|
-
HEIGHT: XMLOptionalNumber,
|
|
238
|
-
TextBlock: v.optional(v.array(XMLOcrTextBlock, "XMLOcrPage: TextBlock is array of XMLOcrTextBlock"))
|
|
239
|
-
}, "XMLOcrPage: Shape error");
|
|
240
|
-
const XMLOcr = v.object({ Page: v.optional(v.array(XMLOcrPage, "XMLOcr: Page is array of XMLOcrPage")) }, "XMLOcr: Shape error");
|
|
241
|
-
const XMLOcrMatch = v.object({
|
|
242
|
-
resourceUuid: v.optional(v.string("XMLOcrMatch: resourceUuid is string and optional")),
|
|
243
|
-
n: XMLOptionalNumber,
|
|
244
|
-
fileName: v.optional(v.string("XMLOcrMatch: fileName is string and optional")),
|
|
245
|
-
WIDTH: XMLOptionalNumber,
|
|
246
|
-
HEIGHT: XMLOptionalNumber,
|
|
247
|
-
string: v.optional(v.array(XMLOcrString, "XMLOcrMatch: string is array of XMLOcrString"))
|
|
248
|
-
}, "XMLOcrMatch: Shape error");
|
|
249
|
-
const XMLOcrMatchItem = v.object({
|
|
250
|
-
uuid: v.pipe(v.string("XMLOcrMatchItem: uuid is string and required"), v.check(isPseudoUuid, "XMLOcrMatchItem: uuid is not a valid pseudo-UUID")),
|
|
251
|
-
matchCount: XMLNumber,
|
|
252
|
-
ocrMatch: v.optional(v.array(XMLOcrMatch, "XMLOcrMatchItem: ocrMatch is array of XMLOcrMatch"))
|
|
253
|
-
}, "XMLOcrMatchItem: Shape error");
|
|
254
|
-
/**
|
|
255
|
-
* Schema for validating OCR matches fetched from the OCHRE API
|
|
256
|
-
* @internal
|
|
257
|
-
*/
|
|
258
|
-
const XMLOcrMatchesData = v.object({ result: v.object({ ochre: v.object({ ocrMatches: v.optional(v.object({ ocrItem: v.optional(v.array(XMLOcrMatchItem, "XMLOcrMatchesData: ocrItem is array of XMLOcrMatchItem")) })) }) }) }, "XMLOcrMatchesData: Shape error");
|
|
259
227
|
const XMLNote = v.object({
|
|
260
228
|
content: v.optional(XMLContent.entries.content),
|
|
261
229
|
payload: v.optional(v.string("XMLNote: payload is string and optional")),
|
|
@@ -676,7 +644,6 @@ const XMLResource = v.object({
|
|
|
676
644
|
width: XMLOptionalNumber,
|
|
677
645
|
image: v.optional(XMLImage),
|
|
678
646
|
imagemap: v.optional(XMLImageMap),
|
|
679
|
-
ocr: v.optional(XMLOcr),
|
|
680
647
|
document: v.optional(XMLContent),
|
|
681
648
|
coordinates: v.optional(XMLCoordinates),
|
|
682
649
|
periods: v.optional(v.object({ period: v.array(XMLPeriod) })),
|
|
@@ -828,7 +795,6 @@ const XMLWebsiteResource = v.lazy(() => v.object({
|
|
|
828
795
|
width: XMLOptionalNumber,
|
|
829
796
|
image: v.optional(XMLImage),
|
|
830
797
|
imagemap: v.optional(XMLImageMap),
|
|
831
|
-
ocr: v.optional(XMLOcr),
|
|
832
798
|
document: v.optional(XMLContent),
|
|
833
799
|
coordinates: v.optional(XMLCoordinates),
|
|
834
800
|
periods: v.optional(v.object({ period: v.array(XMLPeriod) })),
|
|
@@ -962,4 +928,4 @@ const XMLWebsiteData = v.object({ result: v.object({ ochre: v.object({
|
|
|
962
928
|
tree: v.array(XMLWebsiteTree)
|
|
963
929
|
}, "XMLWebsiteData: ochre is object with website tree") }, "XMLWebsiteData: result is object with ochre") }, "XMLWebsiteData: Shape error");
|
|
964
930
|
//#endregion
|
|
965
|
-
export { XMLData, XMLDataItem, XMLGalleryData, XMLItemLinksData, XMLLink,
|
|
931
|
+
export { XMLData, XMLDataItem, XMLGalleryData, XMLItemLinksData, XMLLink, XMLSetItemsData, XMLWebsiteData };
|
package/dist/xml/types.d.mts
CHANGED
|
@@ -2,19 +2,26 @@
|
|
|
2
2
|
type XMLItemCategory = "tree" | "bibliography" | "spatialUnit" | "concept" | "period" | "person" | "propertyVariable" | "variable" | "propertyValue" | "value" | "text" | "resource" | "set";
|
|
3
3
|
type XMLHeadingItemCategory = Exclude<XMLItemCategory, "tree" | "bibliography" | "spatialUnit" | "concept" | "period">;
|
|
4
4
|
type XMLRecursiveItemCategory = Exclude<XMLItemCategory, "tree" | "person" | "propertyVariable" | "propertyValue" | "set">;
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
/**
|
|
6
|
+
* Rich text envelope shared by `<string>` and `<content>` nodes, carrying the
|
|
7
|
+
* inline links, presentation properties and annotation identifier that turn a
|
|
8
|
+
* span of text into an annotation.
|
|
9
|
+
*/
|
|
10
|
+
type XMLRichTextEnvelope = {
|
|
9
11
|
links?: XMLLink;
|
|
10
12
|
properties?: {
|
|
11
13
|
property: Array<XMLProperty>;
|
|
12
14
|
};
|
|
13
15
|
annotation?: string;
|
|
16
|
+
};
|
|
17
|
+
type XMLString = XMLRichTextEnvelope & {
|
|
18
|
+
payload?: string;
|
|
19
|
+
rend?: string;
|
|
20
|
+
whitespace?: string;
|
|
14
21
|
string?: Array<XMLString>;
|
|
15
22
|
};
|
|
16
23
|
type XMLContent = {
|
|
17
|
-
content: Array<{
|
|
24
|
+
content: Array<XMLRichTextEnvelope & {
|
|
18
25
|
string: Array<XMLString>;
|
|
19
26
|
lang: string;
|
|
20
27
|
title?: string;
|
|
@@ -175,48 +182,6 @@ type XMLImageMap = {
|
|
|
175
182
|
width: XMLNumber;
|
|
176
183
|
height: XMLNumber;
|
|
177
184
|
};
|
|
178
|
-
type XMLOcrString = {
|
|
179
|
-
HPOS: XMLNumber;
|
|
180
|
-
VPOS: XMLNumber;
|
|
181
|
-
WIDTH: XMLNumber;
|
|
182
|
-
HEIGHT: XMLNumber;
|
|
183
|
-
CONTENT: string;
|
|
184
|
-
VERTICES?: string;
|
|
185
|
-
};
|
|
186
|
-
type XMLOcrTextLine = {
|
|
187
|
-
string?: Array<XMLOcrString>;
|
|
188
|
-
};
|
|
189
|
-
type XMLOcrTextBlock = {
|
|
190
|
-
TextLine?: Array<XMLOcrTextLine>;
|
|
191
|
-
};
|
|
192
|
-
type XMLOcrPage = {
|
|
193
|
-
n?: XMLNumber;
|
|
194
|
-
fileName?: string;
|
|
195
|
-
WIDTH?: XMLNumber;
|
|
196
|
-
HEIGHT?: XMLNumber;
|
|
197
|
-
TextBlock?: Array<XMLOcrTextBlock>;
|
|
198
|
-
};
|
|
199
|
-
type XMLOcr = {
|
|
200
|
-
Page?: Array<XMLOcrPage>;
|
|
201
|
-
};
|
|
202
|
-
type XMLOcrMatch = Omit<XMLOcrPage, "TextBlock"> & {
|
|
203
|
-
resourceUuid?: string;
|
|
204
|
-
string?: Array<XMLOcrString>;
|
|
205
|
-
};
|
|
206
|
-
type XMLOcrMatchItem = {
|
|
207
|
-
uuid: string;
|
|
208
|
-
matchCount: XMLNumber;
|
|
209
|
-
ocrMatch?: Array<XMLOcrMatch>;
|
|
210
|
-
};
|
|
211
|
-
type XMLOcrMatchesData = {
|
|
212
|
-
result: {
|
|
213
|
-
ochre: {
|
|
214
|
-
ocrMatches?: {
|
|
215
|
-
ocrItem?: Array<XMLOcrMatchItem>;
|
|
216
|
-
};
|
|
217
|
-
};
|
|
218
|
-
};
|
|
219
|
-
};
|
|
220
185
|
type XMLNote = Partial<XMLContent> & XMLString & {
|
|
221
186
|
noteNo?: XMLNumber;
|
|
222
187
|
title?: string;
|
|
@@ -598,7 +563,6 @@ type XMLResource = XMLBaseItem & {
|
|
|
598
563
|
width?: XMLNumber;
|
|
599
564
|
image?: XMLImage;
|
|
600
565
|
imagemap?: XMLImageMap;
|
|
601
|
-
ocr?: XMLOcr;
|
|
602
566
|
document?: XMLContent;
|
|
603
567
|
coordinates?: XMLCoordinates;
|
|
604
568
|
periods?: {
|
|
@@ -956,4 +920,4 @@ type XMLWebsiteData = {
|
|
|
956
920
|
};
|
|
957
921
|
};
|
|
958
922
|
//#endregion
|
|
959
|
-
export { XMLBaseItem, XMLBibliography, XMLBoolean, XMLConcept, XMLContent, XMLContext, XMLContextGroup, XMLContextItem, XMLContextValue, XMLCoordinate, XMLCoordinates, XMLCoordinatesSource, XMLData, XMLDataItem, XMLDictionaryUnit, XMLEmptyContext, XMLEvent, XMLGallery, XMLGalleryData, XMLHeading, XMLHeadingItemCategory, XMLIdentification, XMLImage, XMLImageMap, XMLImageMapArea, XMLInterpretation, XMLItemCategory, XMLItemLinks, XMLItemLinksData, XMLLicense, XMLLink, XMLLinkedBaseItem, XMLLinkedBibliography, XMLLinkedConcept, XMLLinkedPeriod, XMLLinkedPerson, XMLLinkedPropertyValue, XMLLinkedPropertyVariable, XMLLinkedResource, XMLLinkedSet, XMLLinkedSpatialUnit, XMLLinkedText, XMLLinkedTree, XMLMetadata, XMLNote, XMLNumber, XMLObservation,
|
|
923
|
+
export { XMLBaseItem, XMLBibliography, XMLBoolean, XMLConcept, XMLContent, XMLContext, XMLContextGroup, XMLContextItem, XMLContextValue, XMLCoordinate, XMLCoordinates, XMLCoordinatesSource, XMLData, XMLDataItem, XMLDictionaryUnit, XMLEmptyContext, XMLEvent, XMLGallery, XMLGalleryData, XMLHeading, XMLHeadingItemCategory, XMLIdentification, XMLImage, XMLImageMap, XMLImageMapArea, XMLInterpretation, XMLItemCategory, XMLItemLinks, XMLItemLinksData, XMLLicense, XMLLink, XMLLinkedBaseItem, XMLLinkedBibliography, XMLLinkedConcept, XMLLinkedPeriod, XMLLinkedPerson, XMLLinkedPropertyValue, XMLLinkedPropertyVariable, XMLLinkedResource, XMLLinkedSet, XMLLinkedSpatialUnit, XMLLinkedText, XMLLinkedTree, XMLMetadata, XMLNote, XMLNumber, XMLObservation, XMLPeriod, XMLPerson, XMLProperty, XMLPropertyRelation, XMLPropertyValue, XMLPropertyVariable, XMLRecursiveItemCategory, XMLResource, XMLRichTextEnvelope, XMLSection, XMLSet, XMLSetItems, XMLSetItemsData, XMLSimplifiedProperty, XMLSpatialUnit, XMLString, XMLText, XMLTree, XMLWebsiteContext, XMLWebsiteContextItem, XMLWebsiteContextLevel, XMLWebsiteData, XMLWebsiteFilterContext, XMLWebsiteFilterContextItem, XMLWebsiteOptions, XMLWebsiteProperties, XMLWebsiteResource, XMLWebsiteResourceGroup, XMLWebsiteResourceItem, XMLWebsiteScope, XMLWebsiteSegment, XMLWebsiteStyle, XMLWebsiteTree };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ochre-sdk",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.73",
|
|
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",
|
|
@@ -53,10 +53,10 @@
|
|
|
53
53
|
"devDependencies": {
|
|
54
54
|
"@antfu/eslint-config": "^9.2.0",
|
|
55
55
|
"@types/node": "^24.13.3",
|
|
56
|
-
"bumpp": "^12.
|
|
56
|
+
"bumpp": "^12.2.0",
|
|
57
57
|
"eslint": "^10.8.0",
|
|
58
58
|
"knip": "^6.31.0",
|
|
59
|
-
"oxfmt": "^0.
|
|
59
|
+
"oxfmt": "^0.62.0",
|
|
60
60
|
"tsdown": "^0.22.14",
|
|
61
61
|
"typescript": "^6.0.3",
|
|
62
62
|
"vitest": "^4.1.10"
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import { OcrMatch } from "../types/index.mjs";
|
|
2
|
-
import { FetchFunction } from "../parsers/helpers.mjs";
|
|
3
|
-
//#region src/fetchers/ocr-matches.d.ts
|
|
4
|
-
/**
|
|
5
|
-
* Fetches the locations of OCR text matches within OCHRE resources
|
|
6
|
-
*
|
|
7
|
-
* Matching mirrors the `ocr` Set item query target, so the same value and match
|
|
8
|
-
* mode that selected an item will locate its hits. `matchCountsByUuid` reports
|
|
9
|
-
* the untruncated count, which can exceed the returned matches when
|
|
10
|
-
* `maxMatchesPerItem` caps them.
|
|
11
|
-
*
|
|
12
|
-
* @param parameters - The parameters for the fetch
|
|
13
|
-
* @param parameters.uuids - The resource UUIDs to search, typically from a filtered Set item fetch
|
|
14
|
-
* @param parameters.value - The search value
|
|
15
|
-
* @param parameters.matchMode - Whether to match loosely (stemming and wildcards) or on whole OCR words, defaults to "includes"
|
|
16
|
-
* @param parameters.isCaseSensitive - Whether matching is case sensitive, defaults to false
|
|
17
|
-
* @param parameters.maxMatchesPerItem - The cap on returned matches per requested UUID, defaults to 50
|
|
18
|
-
* @param options - Options for the fetch
|
|
19
|
-
* @param options.fetch - The fetch function to use
|
|
20
|
-
* @returns The OCR matches, or null if the fetch/parse fails
|
|
21
|
-
*/
|
|
22
|
-
declare function fetchOcrMatches(parameters: {
|
|
23
|
-
uuids: Array<string>;
|
|
24
|
-
value: string;
|
|
25
|
-
matchMode?: "includes" | "exact";
|
|
26
|
-
isCaseSensitive?: boolean;
|
|
27
|
-
maxMatchesPerItem?: number;
|
|
28
|
-
}, options?: {
|
|
29
|
-
fetch?: FetchFunction;
|
|
30
|
-
}): Promise<{
|
|
31
|
-
matches: Array<OcrMatch>;
|
|
32
|
-
matchesByUuid: Record<string, Array<OcrMatch>>;
|
|
33
|
-
matchCountsByUuid: Record<string, number>;
|
|
34
|
-
error: null;
|
|
35
|
-
detailedError: null;
|
|
36
|
-
} | {
|
|
37
|
-
matches: null;
|
|
38
|
-
matchesByUuid: null;
|
|
39
|
-
matchCountsByUuid: null;
|
|
40
|
-
error: string;
|
|
41
|
-
detailedError: string;
|
|
42
|
-
}>;
|
|
43
|
-
//#endregion
|
|
44
|
-
export { fetchOcrMatches };
|
|
@@ -1,134 +0,0 @@
|
|
|
1
|
-
import { XML_PARSER_OPTIONS } from "../constants.mjs";
|
|
2
|
-
import { createSchemaValidationError, getErrorOutput, stringLiteral } from "../utilities.mjs";
|
|
3
|
-
import { buildOcrTermQueryExpressions } from "../query.mjs";
|
|
4
|
-
import { ocrMatchesParametersSchema } from "../schemas.mjs";
|
|
5
|
-
import { restoreXMLMetadata } from "../xml/metadata.mjs";
|
|
6
|
-
import { parseOcrMatches } from "../parsers/index.mjs";
|
|
7
|
-
import { XMLOcrMatchesData } from "../xml/schemas.mjs";
|
|
8
|
-
import * as v from "valibot";
|
|
9
|
-
import { XMLParser } from "fast-xml-parser";
|
|
10
|
-
//#region src/fetchers/ocr-matches.ts
|
|
11
|
-
/**
|
|
12
|
-
* Build an XQuery string to fetch OCR match locations from the OCHRE API
|
|
13
|
-
*
|
|
14
|
-
* Each OCR word is matched with `cts:contains` against the same per-term CTS
|
|
15
|
-
* queries the Set item filter compiles, so hit locations always agree with what
|
|
16
|
-
* the filter matched — including stemming and wildcards, which cannot be
|
|
17
|
-
* reproduced outside MarkLogic.
|
|
18
|
-
*
|
|
19
|
-
* @param parameters - The parameters for the fetch
|
|
20
|
-
* @param parameters.uuids - The resource UUIDs to search
|
|
21
|
-
* @param parameters.termQueryExpressions - One CTS query expression per search term, in word order
|
|
22
|
-
* @param parameters.maxMatchesPerItem - The cap on returned matches per requested UUID
|
|
23
|
-
* @returns An XQuery string
|
|
24
|
-
*/
|
|
25
|
-
function buildXQuery(parameters) {
|
|
26
|
-
const { uuids, termQueryExpressions, maxMatchesPerItem } = parameters;
|
|
27
|
-
return `xquery version "1.0-ml";
|
|
28
|
-
|
|
29
|
-
declare variable $uuids := (${Array.from(uuids, (uuid) => stringLiteral(uuid)).join(", ")});
|
|
30
|
-
|
|
31
|
-
declare variable $termQueries := (
|
|
32
|
-
${termQueryExpressions.join(",\n ")}
|
|
33
|
-
);
|
|
34
|
-
|
|
35
|
-
declare variable $termCount := ${termQueryExpressions.length};
|
|
36
|
-
|
|
37
|
-
<ochre>{
|
|
38
|
-
<ocrMatches>{
|
|
39
|
-
for $uuid in $uuids
|
|
40
|
-
let $matches :=
|
|
41
|
-
for $page in doc($uuid)//ocr/Page
|
|
42
|
-
let $words := $page//TextLine/string
|
|
43
|
-
let $wordCount := count($words)
|
|
44
|
-
let $resourceUuid := string($page/ancestor::resource[1]/@uuid)
|
|
45
|
-
for $word at $index in $words
|
|
46
|
-
where $index + $termCount - 1 le $wordCount
|
|
47
|
-
and (every $offset in (1 to $termCount)
|
|
48
|
-
satisfies cts:contains($words[$index + $offset - 1], $termQueries[$offset]))
|
|
49
|
-
return <ocrMatch resourceUuid="{$resourceUuid}">{
|
|
50
|
-
$page/@n, $page/@fileName, $page/@WIDTH, $page/@HEIGHT,
|
|
51
|
-
subsequence($words, $index, $termCount)
|
|
52
|
-
}</ocrMatch>
|
|
53
|
-
return <ocrItem uuid="{$uuid}" matchCount="{count($matches)}">{
|
|
54
|
-
subsequence($matches, 1, ${maxMatchesPerItem})
|
|
55
|
-
}</ocrItem>
|
|
56
|
-
}</ocrMatches>
|
|
57
|
-
}</ochre>`;
|
|
58
|
-
}
|
|
59
|
-
/**
|
|
60
|
-
* Fetches the locations of OCR text matches within OCHRE resources
|
|
61
|
-
*
|
|
62
|
-
* Matching mirrors the `ocr` Set item query target, so the same value and match
|
|
63
|
-
* mode that selected an item will locate its hits. `matchCountsByUuid` reports
|
|
64
|
-
* the untruncated count, which can exceed the returned matches when
|
|
65
|
-
* `maxMatchesPerItem` caps them.
|
|
66
|
-
*
|
|
67
|
-
* @param parameters - The parameters for the fetch
|
|
68
|
-
* @param parameters.uuids - The resource UUIDs to search, typically from a filtered Set item fetch
|
|
69
|
-
* @param parameters.value - The search value
|
|
70
|
-
* @param parameters.matchMode - Whether to match loosely (stemming and wildcards) or on whole OCR words, defaults to "includes"
|
|
71
|
-
* @param parameters.isCaseSensitive - Whether matching is case sensitive, defaults to false
|
|
72
|
-
* @param parameters.maxMatchesPerItem - The cap on returned matches per requested UUID, defaults to 50
|
|
73
|
-
* @param options - Options for the fetch
|
|
74
|
-
* @param options.fetch - The fetch function to use
|
|
75
|
-
* @returns The OCR matches, or null if the fetch/parse fails
|
|
76
|
-
*/
|
|
77
|
-
async function fetchOcrMatches(parameters, options) {
|
|
78
|
-
try {
|
|
79
|
-
const { uuids, value, matchMode, isCaseSensitive, maxMatchesPerItem } = v.parse(ocrMatchesParametersSchema, parameters);
|
|
80
|
-
const termQueryExpressions = buildOcrTermQueryExpressions({
|
|
81
|
-
value,
|
|
82
|
-
matchMode,
|
|
83
|
-
isCaseSensitive
|
|
84
|
-
});
|
|
85
|
-
if (termQueryExpressions.length === 0) return {
|
|
86
|
-
matches: [],
|
|
87
|
-
matchesByUuid: {},
|
|
88
|
-
matchCountsByUuid: {},
|
|
89
|
-
error: null,
|
|
90
|
-
detailedError: null
|
|
91
|
-
};
|
|
92
|
-
const response = await (options?.fetch ?? fetch)("https://ochre.lib.uchicago.edu/ochre/v2/ochre.php?xquery&xsl=none&lang=\"*\"", {
|
|
93
|
-
method: "POST",
|
|
94
|
-
body: buildXQuery({
|
|
95
|
-
uuids,
|
|
96
|
-
termQueryExpressions,
|
|
97
|
-
maxMatchesPerItem
|
|
98
|
-
}),
|
|
99
|
-
headers: { "Content-Type": "application/xquery" }
|
|
100
|
-
});
|
|
101
|
-
if (!response.ok) throw new Error(`OCHRE API responded with status: ${response.status}`, { cause: response.statusText });
|
|
102
|
-
const dataRaw = await response.text();
|
|
103
|
-
const data = new XMLParser(XML_PARSER_OPTIONS).parse(dataRaw);
|
|
104
|
-
const { success, issues, output } = v.safeParse(XMLOcrMatchesData, data);
|
|
105
|
-
if (!success) throw createSchemaValidationError("Failed to parse OCHRE OCR matches", issues);
|
|
106
|
-
restoreXMLMetadata(output, data);
|
|
107
|
-
const rawOcrItems = output.result.ochre.ocrMatches?.ocrItem ?? [];
|
|
108
|
-
const matches = parseOcrMatches(rawOcrItems);
|
|
109
|
-
const matchesByUuid = {};
|
|
110
|
-
const matchCountsByUuid = {};
|
|
111
|
-
for (const uuid of uuids) {
|
|
112
|
-
matchesByUuid[uuid] = [];
|
|
113
|
-
matchCountsByUuid[uuid] = 0;
|
|
114
|
-
}
|
|
115
|
-
for (const rawOcrItem of rawOcrItems) matchCountsByUuid[rawOcrItem.uuid] = rawOcrItem.matchCount;
|
|
116
|
-
for (const match of matches) matchesByUuid[match.uuid]?.push(match);
|
|
117
|
-
return {
|
|
118
|
-
matches,
|
|
119
|
-
matchesByUuid,
|
|
120
|
-
matchCountsByUuid,
|
|
121
|
-
error: null,
|
|
122
|
-
detailedError: null
|
|
123
|
-
};
|
|
124
|
-
} catch (error) {
|
|
125
|
-
return {
|
|
126
|
-
matches: null,
|
|
127
|
-
matchesByUuid: null,
|
|
128
|
-
matchCountsByUuid: null,
|
|
129
|
-
...getErrorOutput(error, "Unknown error")
|
|
130
|
-
};
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
//#endregion
|
|
134
|
-
export { fetchOcrMatches };
|