ochre-sdk 1.0.74 → 1.0.76
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 +3 -3
- package/dist/fetchers/item-ocr-data.d.mts +9 -6
- package/dist/fetchers/item-ocr-data.mjs +19 -11
- package/dist/index.d.mts +2 -2
- package/dist/parsers/website/index.mjs +3 -1
- package/dist/query.mjs +57 -17
- package/dist/types/index.d.mts +9 -5
- package/dist/types/website.d.mts +22 -22
- package/dist/xml/schemas.mjs +8 -1
- package/dist/xml/types.d.mts +1 -0
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -145,13 +145,13 @@ const queries: Query = {
|
|
|
145
145
|
};
|
|
146
146
|
```
|
|
147
147
|
|
|
148
|
-
Every
|
|
148
|
+
Every word node in that layer holds a single OCR word in its `CONTENT` attribute, so `includes` matches each search term as its own word anywhere in the layer, in any order, with `*` and `?` wildcards supported. `exact` instead matches the terms as a run of adjacent whole words, so `"THE COLLEGE"` matches a page carrying that phrase but not one where the two words merely appear apart. An item matches when the OCR layer of the item itself or of any of its child Resources matches.
|
|
149
149
|
|
|
150
150
|
Set item projections do not carry the OCR layer, so an `ocr` leaf is resolved by an extra index-only search over the Resource documents whose matching UUIDs are then joined back onto the Set items. It still composes with `and`, `or`, and `isNegated` like any other leaf, and repeating the same OCR search inside one tree only costs one search.
|
|
151
151
|
|
|
152
152
|
## OCR Data
|
|
153
153
|
|
|
154
|
-
A Resource may carry an `<ocr>` layer holding the positioned output of an OCR run. The node hierarchy inside that layer is irregular and is not parsed, but any
|
|
154
|
+
A Resource may carry an `<ocr>` layer holding the positioned output of an OCR run. The node hierarchy inside that layer is irregular and is not parsed, but any word node within it, at any depth, is read as one positioned OCR string. A word node is any element named `string` in any casing and any namespace, and its text comes from the `CONTENT` attribute rather than from the element's text content.
|
|
155
155
|
|
|
156
156
|
```ts
|
|
157
157
|
import { fetchItemOcrData } from "ochre-sdk";
|
|
@@ -165,7 +165,7 @@ for (const ocrString of result.ocrStrings ?? []) {
|
|
|
165
165
|
}
|
|
166
166
|
```
|
|
167
167
|
|
|
168
|
-
`x` and `y` come from `HPOS` and `VPOS` and give the top-left corner of the box, `width` and `height` its size, and `vertices` its full bounding polygon, which is not always rectangular. Each geometry field is null when the source attribute is absent or unparseable. `resourceUuid` names the Resource that owns the OCR layer, which differs from the requested item when the OCR lives on a child Resource.
|
|
168
|
+
`x` and `y` come from `HPOS` and `VPOS` and give the top-left corner of the box, `width` and `height` its size, and `vertices` comes from `VERTICES` and is its full bounding polygon, which is not always rectangular. Each geometry field is null when the source attribute is absent or unparseable, and `vertices` is then empty. `resourceUuid` names the Resource that owns the OCR layer, which differs from the requested item when the OCR lives on a child Resource.
|
|
169
169
|
|
|
170
170
|
Matching defaults to case-insensitive `includes` and runs against each string's `CONTENT`. Because a `<string>` holds a single OCR word, a multi-word search value is split on whitespace and a string is returned when it matches any one term. Requesting an item that does not exist is an error; an item with no OCR layer, or no matches, returns an empty array.
|
|
171
171
|
|
|
@@ -4,12 +4,15 @@ import { OcrString } from "../types/index.mjs";
|
|
|
4
4
|
* Fetches and parses the OCR strings of an OCHRE item that match a search value
|
|
5
5
|
*
|
|
6
6
|
* Resources may carry an `<ocr>` layer whose internal hierarchy is irregular
|
|
7
|
-
* and therefore not parsed. Only its
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
7
|
+
* and therefore not parsed. Only its word nodes are returned, wherever they
|
|
8
|
+
* occur in that subtree, in document order. A word node is any element whose
|
|
9
|
+
* name is `string` in any casing and any namespace, and matching reads its
|
|
10
|
+
* `CONTENT` attribute rather than its text content.
|
|
11
|
+
*
|
|
12
|
+
* Each word node holds a single OCR word, so a multi-word search value is split
|
|
13
|
+
* on whitespace and a node is returned when it matches any one term. Nested
|
|
14
|
+
* child Resources are searched too, with `resourceUuid` naming the Resource
|
|
15
|
+
* each match belongs to.
|
|
13
16
|
*
|
|
14
17
|
* @param uuid - The UUID of the OCHRE item to read the OCR layer of
|
|
15
18
|
* @param value - The search value to match against each OCR string's content
|
|
@@ -52,9 +52,14 @@ function parseOcrStringVertices(value) {
|
|
|
52
52
|
* Build an XQuery string to fetch matching OCR strings from the OCHRE API
|
|
53
53
|
*
|
|
54
54
|
* The `<ocr>` layer is marked supplemental, so it is deliberately read without
|
|
55
|
-
* the supplemental stripping the other fetchers apply.
|
|
56
|
-
*
|
|
57
|
-
*
|
|
55
|
+
* the supplemental stripping the other fetchers apply. Word nodes are projected
|
|
56
|
+
* at any depth, because OCHRE does not guarantee the shape of the surrounding
|
|
57
|
+
* hierarchy.
|
|
58
|
+
*
|
|
59
|
+
* Both the container and the word nodes are matched on a case-folded
|
|
60
|
+
* `local-name()` rather than a name test, because OCHRE varies the casing of
|
|
61
|
+
* these elements and may serve them in a namespace. A plain `//ocr//string`
|
|
62
|
+
* name test silently matches nothing in either of those cases.
|
|
58
63
|
*
|
|
59
64
|
* The matches are wrapped in an `<ocrStrings>` element rather than returned
|
|
60
65
|
* directly under `<ochre>`: the API collapses an `<ochre>` element that has no
|
|
@@ -78,10 +83,10 @@ declare variable $terms := (${termValues.join(", ")});
|
|
|
78
83
|
|
|
79
84
|
let $ochre := doc(${stringLiteral(uuid)})/ochre
|
|
80
85
|
let $ocrStrings :=
|
|
81
|
-
for $string in $ochre
|
|
86
|
+
for $string in $ochre//*[lower-case(local-name(.)) = "ocr"]//*[lower-case(local-name(.)) = "string"][@CONTENT]
|
|
82
87
|
where (some $term in $terms satisfies ${matchExpression})
|
|
83
88
|
return <ocrString
|
|
84
|
-
resourceUuid="{string($string/ancestor
|
|
89
|
+
resourceUuid="{string($string/ancestor::*[local-name(.) = "resource"][1]/@uuid)}"
|
|
85
90
|
content="{string($string/@CONTENT)}"
|
|
86
91
|
x="{string($string/@HPOS)}"
|
|
87
92
|
y="{string($string/@VPOS)}"
|
|
@@ -95,12 +100,15 @@ return <ochre><ocrStrings found="{exists($ochre)}">{$ocrStrings}</ocrStrings></o
|
|
|
95
100
|
* Fetches and parses the OCR strings of an OCHRE item that match a search value
|
|
96
101
|
*
|
|
97
102
|
* Resources may carry an `<ocr>` layer whose internal hierarchy is irregular
|
|
98
|
-
* and therefore not parsed. Only its
|
|
99
|
-
*
|
|
100
|
-
*
|
|
101
|
-
*
|
|
102
|
-
*
|
|
103
|
-
*
|
|
103
|
+
* and therefore not parsed. Only its word nodes are returned, wherever they
|
|
104
|
+
* occur in that subtree, in document order. A word node is any element whose
|
|
105
|
+
* name is `string` in any casing and any namespace, and matching reads its
|
|
106
|
+
* `CONTENT` attribute rather than its text content.
|
|
107
|
+
*
|
|
108
|
+
* Each word node holds a single OCR word, so a multi-word search value is split
|
|
109
|
+
* on whitespace and a node is returned when it matches any one term. Nested
|
|
110
|
+
* child Resources are searched too, with `resourceUuid` naming the Resource
|
|
111
|
+
* each match belongs to.
|
|
104
112
|
*
|
|
105
113
|
* @param uuid - The UUID of the OCHRE item to read the OCR layer of
|
|
106
114
|
* @param value - The search value to match against each OCR string's content
|
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { MultilingualOptions, MultilingualString, MultilingualStringEntries, MultilingualStringEntry, MultilingualStringInput, MultilingualStringJSON, MultilingualStringObject, MultilingualStringText } from "./parsers/multilingual.mjs";
|
|
2
|
-
import { AccordionWebBlock, ContextTree, ContextTreeFilterLevel, ContextTreeLevel, ContextTreeLevelItem, ProtectedWebsite, Scope, Style, StylesheetCategory, StylesheetItem, WebAccordionItem, WebBlock, WebBlockByLayout, WebBlockItem, WebBlockLayout, WebElement, WebElementComponent, WebElementComponentName, WebElementComponentOf, WebElementOf, WebImage, WebSidebar, WebTitle, Webpage, Website, WebsiteMetadata, WebsitePropertyQuery, WebsitePropertyQueryNode, WebsiteSegment, WebsiteType } from "./types/website.mjs";
|
|
2
|
+
import { AccordionWebBlock, ContextTree, ContextTreeFilterLevel, ContextTreeFilterVariant, ContextTreeLevel, ContextTreeLevelItem, ProtectedWebsite, Scope, Style, StylesheetCategory, StylesheetItem, WebAccordionItem, WebBlock, WebBlockByLayout, WebBlockItem, WebBlockLayout, WebElement, WebElementComponent, WebElementComponentName, WebElementComponentOf, WebElementOf, WebImage, WebOptions, WebSidebar, WebTitle, Webpage, Website, WebsiteMetadata, WebsitePropertyQuery, WebsitePropertyQueryNode, WebsiteSegment, WebsiteType } from "./types/website.mjs";
|
|
3
3
|
import { AnyBibliography, AnyConcept, AnyItem, AnyPeriod, AnyPerson, AnyPropertyValue, AnyPropertyVariable, AnyResource, AnySet, AnySpatialUnit, AnyText, AnyTree, BaseItem, BaseItemLink, BelongsTo, Bibliography, BibliographyEntryInfo, BibliographyItemLink, BibliographySourceDocument, Concept, ConceptItemLink, ContainedItemCategory, ContainedItemCategoryFromOption, ContainedItemCategoryOption, Context, ContextItem, ContextItemCategory, ContextNode, Coordinates, CoordinatesSource, DictionaryUnitItemLink, EmbeddedBibliography, EmbeddedConcept, EmbeddedItem, EmbeddedPeriod, EmbeddedPerson, EmbeddedPropertyValue, EmbeddedPropertyVariable, EmbeddedResource, EmbeddedSet, EmbeddedSpatialUnit, EmbeddedText, EmbeddedTree, Event, Gallery, Heading, HeadingItemCategory, Identification, Image, ImageMap, ImageMapArea, Interpretation, Item, ItemCategory, ItemCategoryFromOption, ItemCategoryOption, ItemCategoryWithEmbeddedItems, ItemContainerCategory, ItemLink, ItemLinkCategory, ItemLinks, ItemPayloadKind, ItemProperty, ItemWithoutEmbeddedItems, LanguageCodes, License, Metadata, Note, Observation, OcrString, Period, PeriodItemLink, Person, PersonItemLink, Prettify, Property, PropertyLike, PropertyRelation, PropertyValue, PropertyValueContent, PropertyValueDataType, PropertyValueItemLink, PropertyValueQueryItem, PropertyVariable, PropertyVariableItemLink, Query, QueryGroup, QueryLeaf, QueryablePropertyValueDataType, RecursiveItemCategory, Resource, ResourceItemLink, Section, Set, SetAttributeValueQueryItem, SetBibliography, SetConcept, SetItem, SetItemCategory, SetItemLink, SetItemProperty, SetItemSimplifiedProperty, SetItemsSort, SetItemsSortDirection, SetPeriod, SetResource, SetSpatialUnit, SetTree, SimplifiedProperty, SpatialUnit, SpatialUnitItemLink, Text, TextItemLink, TopLevelItem, Tree, TreeItemCategory, TreeItemLink } from "./types/index.mjs";
|
|
4
4
|
import { fetchGallery } from "./fetchers/gallery.mjs";
|
|
5
5
|
import { fetchItemChildren } from "./fetchers/item-children.mjs";
|
|
@@ -12,4 +12,4 @@ import { fetchWebsiteMetadata } from "./fetchers/website-metadata.mjs";
|
|
|
12
12
|
import { fetchWebsite } from "./fetchers/website.mjs";
|
|
13
13
|
import { PropertyOptions, filterProperties, getLeafPropertyValues, getPropertyByVariableLabel, getPropertyByVariableLabelAndValue, getPropertyByVariableLabelAndValueContent, getPropertyByVariableLabelAndValueContents, getPropertyByVariableLabelAndValues, getPropertyByVariableUuid, getPropertyValueByVariableLabel, getPropertyValueByVariableUuid, getPropertyValueContentByVariableLabel, getPropertyValueContentByVariableUuid, getPropertyValueContentsByVariableUuid, getPropertyValuesByVariableLabel, getPropertyValuesByVariableUuid, getUniqueProperties, getUniquePropertyVariableLabels } from "./getters.mjs";
|
|
14
14
|
import { DEFAULT_PAGE_SIZE, flattenItemProperties } from "./helpers.mjs";
|
|
15
|
-
export { type AccordionWebBlock, type AnyBibliography, type AnyConcept, type AnyItem, type AnyPeriod, type AnyPerson, type AnyPropertyValue, type AnyPropertyVariable, type AnyResource, type AnySet, type AnySpatialUnit, type AnyText, type AnyTree, type BaseItem, type BaseItemLink, type BelongsTo, type Bibliography, type BibliographyEntryInfo, type BibliographyItemLink, type BibliographySourceDocument, type Concept, type ConceptItemLink, type ContainedItemCategory, type ContainedItemCategoryFromOption, type ContainedItemCategoryOption, type Context, type ContextItem, type ContextItemCategory, type ContextNode, type ContextTree, type ContextTreeFilterLevel, type ContextTreeLevel, type ContextTreeLevelItem, type Coordinates, type CoordinatesSource, DEFAULT_PAGE_SIZE, type DictionaryUnitItemLink, type EmbeddedBibliography, type EmbeddedConcept, type EmbeddedItem, type EmbeddedPeriod, type EmbeddedPerson, type EmbeddedPropertyValue, type EmbeddedPropertyVariable, type EmbeddedResource, type EmbeddedSet, type EmbeddedSpatialUnit, type EmbeddedText, type EmbeddedTree, type Event, type Gallery, type Heading, type HeadingItemCategory, type Identification, type Image, type ImageMap, type ImageMapArea, type Interpretation, type Item, type ItemCategory, type ItemCategoryFromOption, type ItemCategoryOption, type ItemCategoryWithEmbeddedItems, type ItemContainerCategory, type ItemLink, type ItemLinkCategory, type ItemLinks, type ItemPayloadKind, type ItemProperty, type ItemWithoutEmbeddedItems, type LanguageCodes, type License, type Metadata, type MultilingualOptions, MultilingualString, type MultilingualStringEntries, type MultilingualStringEntry, type MultilingualStringInput, type MultilingualStringJSON, type MultilingualStringObject, type MultilingualStringText, type Note, type Observation, type OcrString, type Period, type PeriodItemLink, type Person, type PersonItemLink, type Prettify, type Property, type PropertyLike, PropertyOptions, type PropertyRelation, type PropertyValue, type PropertyValueContent, type PropertyValueDataType, type PropertyValueItemLink, type PropertyValueQueryItem, type PropertyVariable, type PropertyVariableItemLink, type ProtectedWebsite, type Query, type QueryGroup, type QueryLeaf, type QueryablePropertyValueDataType, type RecursiveItemCategory, type Resource, type ResourceItemLink, type Scope, type Section, type Set, type SetAttributeValueQueryItem, type SetBibliography, type SetConcept, type SetItem, type SetItemCategory, type SetItemLink, type SetItemProperty, type SetItemSimplifiedProperty, type SetItemsSort, type SetItemsSortDirection, type SetPeriod, type SetResource, type SetSpatialUnit, type SetTree, type SimplifiedProperty, type SpatialUnit, type SpatialUnitItemLink, type Style, type StylesheetCategory, type StylesheetItem, type Text, type TextItemLink, type TopLevelItem, type Tree, type TreeItemCategory, type TreeItemLink, type WebAccordionItem, type WebBlock, type WebBlockByLayout, type WebBlockItem, type WebBlockLayout, type WebElement, type WebElementComponent, type WebElementComponentName, type WebElementComponentOf, type WebElementOf, type WebImage, type WebSidebar, type WebTitle, type Webpage, type Website, type WebsiteMetadata, type WebsitePropertyQuery, type WebsitePropertyQueryNode, type WebsiteSegment, type WebsiteType, defineLanguages, fetchGallery, fetchItem, fetchItemChildren, fetchItemLinks, fetchItemOcrData, fetchSetItems, fetchSetPropertyValues, fetchWebsite, fetchWebsiteMetadata, filterProperties, flattenItemProperties, getLeafPropertyValues, getPropertyByVariableLabel, getPropertyByVariableLabelAndValue, getPropertyByVariableLabelAndValueContent, getPropertyByVariableLabelAndValueContents, getPropertyByVariableLabelAndValues, getPropertyByVariableUuid, getPropertyValueByVariableLabel, getPropertyValueByVariableUuid, getPropertyValueContentByVariableLabel, getPropertyValueContentByVariableUuid, getPropertyValueContentsByVariableUuid, getPropertyValuesByVariableLabel, getPropertyValuesByVariableUuid, getUniqueProperties, getUniquePropertyVariableLabels, withLanguages };
|
|
15
|
+
export { type AccordionWebBlock, type AnyBibliography, type AnyConcept, type AnyItem, type AnyPeriod, type AnyPerson, type AnyPropertyValue, type AnyPropertyVariable, type AnyResource, type AnySet, type AnySpatialUnit, type AnyText, type AnyTree, type BaseItem, type BaseItemLink, type BelongsTo, type Bibliography, type BibliographyEntryInfo, type BibliographyItemLink, type BibliographySourceDocument, type Concept, type ConceptItemLink, type ContainedItemCategory, type ContainedItemCategoryFromOption, type ContainedItemCategoryOption, type Context, type ContextItem, type ContextItemCategory, type ContextNode, type ContextTree, type ContextTreeFilterLevel, type ContextTreeFilterVariant, type ContextTreeLevel, type ContextTreeLevelItem, type Coordinates, type CoordinatesSource, DEFAULT_PAGE_SIZE, type DictionaryUnitItemLink, type EmbeddedBibliography, type EmbeddedConcept, type EmbeddedItem, type EmbeddedPeriod, type EmbeddedPerson, type EmbeddedPropertyValue, type EmbeddedPropertyVariable, type EmbeddedResource, type EmbeddedSet, type EmbeddedSpatialUnit, type EmbeddedText, type EmbeddedTree, type Event, type Gallery, type Heading, type HeadingItemCategory, type Identification, type Image, type ImageMap, type ImageMapArea, type Interpretation, type Item, type ItemCategory, type ItemCategoryFromOption, type ItemCategoryOption, type ItemCategoryWithEmbeddedItems, type ItemContainerCategory, type ItemLink, type ItemLinkCategory, type ItemLinks, type ItemPayloadKind, type ItemProperty, type ItemWithoutEmbeddedItems, type LanguageCodes, type License, type Metadata, type MultilingualOptions, MultilingualString, type MultilingualStringEntries, type MultilingualStringEntry, type MultilingualStringInput, type MultilingualStringJSON, type MultilingualStringObject, type MultilingualStringText, type Note, type Observation, type OcrString, type Period, type PeriodItemLink, type Person, type PersonItemLink, type Prettify, type Property, type PropertyLike, PropertyOptions, type PropertyRelation, type PropertyValue, type PropertyValueContent, type PropertyValueDataType, type PropertyValueItemLink, type PropertyValueQueryItem, type PropertyVariable, type PropertyVariableItemLink, type ProtectedWebsite, type Query, type QueryGroup, type QueryLeaf, type QueryablePropertyValueDataType, type RecursiveItemCategory, type Resource, type ResourceItemLink, type Scope, type Section, type Set, type SetAttributeValueQueryItem, type SetBibliography, type SetConcept, type SetItem, type SetItemCategory, type SetItemLink, type SetItemProperty, type SetItemSimplifiedProperty, type SetItemsSort, type SetItemsSortDirection, type SetPeriod, type SetResource, type SetSpatialUnit, type SetTree, type SimplifiedProperty, type SpatialUnit, type SpatialUnitItemLink, type Style, type StylesheetCategory, type StylesheetItem, type Text, type TextItemLink, type TopLevelItem, type Tree, type TreeItemCategory, type TreeItemLink, type WebAccordionItem, type WebBlock, type WebBlockByLayout, type WebBlockItem, type WebBlockLayout, type WebElement, type WebElementComponent, type WebElementComponentName, type WebElementComponentOf, type WebElementOf, type WebImage, type WebOptions, type WebSidebar, type WebTitle, type Webpage, type Website, type WebsiteMetadata, type WebsitePropertyQuery, type WebsitePropertyQueryNode, type WebsiteSegment, type WebsiteType, defineLanguages, fetchGallery, fetchItem, fetchItemChildren, fetchItemLinks, fetchItemOcrData, fetchSetItems, fetchSetPropertyValues, fetchWebsite, fetchWebsiteMetadata, filterProperties, flattenItemProperties, getLeafPropertyValues, getPropertyByVariableLabel, getPropertyByVariableLabelAndValue, getPropertyByVariableLabelAndValueContent, getPropertyByVariableLabelAndValueContents, getPropertyByVariableLabelAndValues, getPropertyByVariableUuid, getPropertyValueByVariableLabel, getPropertyValueByVariableUuid, getPropertyValueContentByVariableLabel, getPropertyValueContentByVariableUuid, getPropertyValueContentsByVariableUuid, getPropertyValuesByVariableLabel, getPropertyValuesByVariableUuid, getUniqueProperties, getUniquePropertyVariableLabels, withLanguages };
|
|
@@ -351,7 +351,8 @@ function parseWebElementProperties(componentProperty, elementResource, options,
|
|
|
351
351
|
properties = {
|
|
352
352
|
component: "advanced-search",
|
|
353
353
|
boundElementUuid: boundElementPropertyUuid,
|
|
354
|
-
href
|
|
354
|
+
href,
|
|
355
|
+
options: parseWebsiteOptions(elementResource.options, options)
|
|
355
356
|
};
|
|
356
357
|
break;
|
|
357
358
|
}
|
|
@@ -1363,6 +1364,7 @@ function parseFilterContexts(filterContextLevels, options) {
|
|
|
1363
1364
|
for (const filterContextLevel of filterContextLevels) for (const contextItemToParse of filterContextLevel.context) filterContextTreeLevels.push({
|
|
1364
1365
|
...parseContextItem(contextItemToParse, options),
|
|
1365
1366
|
filterType: contextItemToParse.filterType ?? "property",
|
|
1367
|
+
filterVariant: contextItemToParse.filterVariant ?? null,
|
|
1366
1368
|
...parseFilterContextDisplay(contextItemToParse.filterOption)
|
|
1367
1369
|
});
|
|
1368
1370
|
return filterContextTreeLevels;
|
package/dist/query.mjs
CHANGED
|
@@ -472,16 +472,17 @@ function buildItemStringQueryExpression(parameters) {
|
|
|
472
472
|
language
|
|
473
473
|
})]);
|
|
474
474
|
}
|
|
475
|
-
|
|
475
|
+
const OCR_STRING_QNAMES = `(xs:QName("String"), fn:QName("http://www.loc.gov/standards/alto/ns-v2#", "string"))`;
|
|
476
|
+
function tokenizeOcrExactValue(value) {
|
|
476
477
|
const terms = [];
|
|
477
478
|
for (const term of value.split(/\s+/u)) if (term !== "") terms.push(term);
|
|
478
479
|
return terms;
|
|
479
480
|
}
|
|
480
481
|
/**
|
|
481
482
|
* Word queries against the OCR layer cannot carry a stemming option: the OCHRE
|
|
482
|
-
* database has unstemmed word searches turned off, and asking
|
|
483
|
-
*
|
|
484
|
-
*
|
|
483
|
+
* database has unstemmed word searches turned off, and asking a word query for
|
|
484
|
+
* `unstemmed` fails with `XDMP-WORDSEARCH`. Omitting the option altogether
|
|
485
|
+
* resolves the term against the database default instead.
|
|
485
486
|
*/
|
|
486
487
|
function buildOcrWordQueryExpression(parameters) {
|
|
487
488
|
const { value, isCaseSensitive } = parameters;
|
|
@@ -492,30 +493,39 @@ function buildOcrWordQueryExpression(parameters) {
|
|
|
492
493
|
"whitespace-insensitive"
|
|
493
494
|
];
|
|
494
495
|
if (hasWildcardCharacters(value)) options.push("wildcarded");
|
|
495
|
-
return `cts:element-word-query(xs:QName("
|
|
496
|
+
return `cts:element-attribute-word-query(${OCR_STRING_QNAMES}, xs:QName("CONTENT"), ${stringLiteral(value)}, (${options.map((option) => stringLiteral(option)).join(", ")}))`;
|
|
497
|
+
}
|
|
498
|
+
function buildOcrValueQueryExpression(parameters) {
|
|
499
|
+
const { value, isCaseSensitive } = parameters;
|
|
500
|
+
return `cts:element-attribute-value-query(${OCR_STRING_QNAMES}, xs:QName("CONTENT"), ${stringLiteral(value)}, ${buildWordQueryOptionsExpression({
|
|
501
|
+
matchMode: "exact",
|
|
502
|
+
isCaseSensitive
|
|
503
|
+
})})`;
|
|
496
504
|
}
|
|
497
505
|
/**
|
|
498
506
|
* Compile an OCR text search into a query over the `<ocr>` layer of a Resource
|
|
499
507
|
* document
|
|
500
508
|
*
|
|
501
|
-
*
|
|
502
|
-
* matches
|
|
503
|
-
*
|
|
504
|
-
*
|
|
505
|
-
*
|
|
506
|
-
*
|
|
509
|
+
* Each word node in that layer holds a single OCR word in its `CONTENT`
|
|
510
|
+
* attribute, so `includes` matches every search term as a word inside that
|
|
511
|
+
* attribute anywhere in the layer, and `exact` requires every term to equal a
|
|
512
|
+
* whole `CONTENT` value.
|
|
513
|
+
*
|
|
514
|
+
* The conjunction is only an index narrowing for `exact`. Attribute values
|
|
515
|
+
* carry no word positions, so `cts:near-query` over them silently degenerates
|
|
516
|
+
* into a conjunction and cannot express a phrase at all. Word order and
|
|
517
|
+
* adjacency are instead enforced by {@link registerOcrPhraseHelper} over the
|
|
518
|
+
* documents this narrowing returns.
|
|
507
519
|
*/
|
|
508
520
|
function buildOcrQueryExpression(query) {
|
|
509
521
|
const { value, matchMode, isCaseSensitive } = query;
|
|
510
522
|
if (matchMode === "exact") {
|
|
511
|
-
const terms =
|
|
523
|
+
const terms = tokenizeOcrExactValue(value);
|
|
512
524
|
if (terms.length === 0) return "cts:false-query()";
|
|
513
|
-
|
|
514
|
-
elementName: "string",
|
|
525
|
+
return buildNestedElementQuery(["ocr"], buildAndCtsQueryExpressionInternal(Array.from(terms, (term) => buildOcrValueQueryExpression({
|
|
515
526
|
value: term,
|
|
516
527
|
isCaseSensitive
|
|
517
|
-
}));
|
|
518
|
-
return buildNestedElementQuery(["ocr"], termQueryExpressions.length === 1 ? termQueryExpressions[0] ?? "cts:false-query()" : `cts:near-query((${termQueryExpressions.join(", ")}), ${termQueryExpressions.length - 1}, ("ordered"))`);
|
|
528
|
+
}))));
|
|
519
529
|
}
|
|
520
530
|
const terms = tokenizeIncludesSearchValue({
|
|
521
531
|
value,
|
|
@@ -528,6 +538,31 @@ function buildOcrQueryExpression(query) {
|
|
|
528
538
|
}))));
|
|
529
539
|
}
|
|
530
540
|
/**
|
|
541
|
+
* Declare the filter that holds an `exact` multi-term search to a run of
|
|
542
|
+
* adjacent OCR words, which no CTS query over the layer can express
|
|
543
|
+
*/
|
|
544
|
+
function registerOcrPhraseHelper(context) {
|
|
545
|
+
const helperName = "local:ocrHasPhrase";
|
|
546
|
+
if (context.helperNamesByKey.has(helperName)) return helperName;
|
|
547
|
+
context.helperNamesByKey.set(helperName, helperName);
|
|
548
|
+
context.helperDeclarations.push(`declare function ${helperName}($resource as node(), $terms as xs:string*, $isCaseSensitive as xs:boolean) as xs:boolean {
|
|
549
|
+
let $contents :=
|
|
550
|
+
for $word in $resource//*[lower-case(local-name(.)) = "ocr"]//*[lower-case(local-name(.)) = "string"][@CONTENT]
|
|
551
|
+
return if ($isCaseSensitive) then string($word/@CONTENT) else lower-case(string($word/@CONTENT))
|
|
552
|
+
let $needles :=
|
|
553
|
+
for $term in $terms
|
|
554
|
+
return if ($isCaseSensitive) then $term else lower-case($term)
|
|
555
|
+
let $length := count($needles)
|
|
556
|
+
return
|
|
557
|
+
some $start in (1 to (count($contents) - $length + 1))
|
|
558
|
+
satisfies (
|
|
559
|
+
every $offset in (1 to $length)
|
|
560
|
+
satisfies $contents[$start + $offset - 1] = $needles[$offset]
|
|
561
|
+
)
|
|
562
|
+
};`);
|
|
563
|
+
return helperName;
|
|
564
|
+
}
|
|
565
|
+
/**
|
|
531
566
|
* Bind the UUIDs of the Resource documents whose OCR layer matches a query,
|
|
532
567
|
* reusing the binding when the same search is requested more than once
|
|
533
568
|
*/
|
|
@@ -541,10 +576,15 @@ function registerOcrBinding(context, query) {
|
|
|
541
576
|
if (existingName != null) return existingName;
|
|
542
577
|
const name = `$ocrItemUuids${context.ocrBindings.length + 1}`;
|
|
543
578
|
const queryExpression = buildOcrQueryExpression(query);
|
|
579
|
+
const phraseTerms = query.matchMode === "exact" ? tokenizeOcrExactValue(query.value) : [];
|
|
544
580
|
context.ocrBindingNamesByKey.set(key, name);
|
|
581
|
+
const searchExpression = `cts:search(/ochre/resource, ${queryExpression})`;
|
|
582
|
+
const phraseHelperName = phraseTerms.length > 1 ? registerOcrPhraseHelper(context) : null;
|
|
545
583
|
context.ocrBindings.push({
|
|
546
584
|
name,
|
|
547
|
-
expression: queryExpression === "cts:false-query()" ? "()" :
|
|
585
|
+
expression: queryExpression === "cts:false-query()" ? "()" : phraseHelperName == null ? `${searchExpression}/@uuid/string()` : `for $ocrResource in ${searchExpression}
|
|
586
|
+
where ${phraseHelperName}($ocrResource, (${phraseTerms.map((term) => stringLiteral(term)).join(", ")}), ${query.isCaseSensitive ? "true()" : "false()"})
|
|
587
|
+
return string($ocrResource/@uuid)`
|
|
548
588
|
});
|
|
549
589
|
return name;
|
|
550
590
|
}
|
package/dist/types/index.d.mts
CHANGED
|
@@ -266,11 +266,15 @@ type ImageMap = {
|
|
|
266
266
|
* Positioned OCR string in OCHRE
|
|
267
267
|
*
|
|
268
268
|
* OCHRE gives no guarantee about the node hierarchy inside a Resource's
|
|
269
|
-
* `<ocr>` layer, so only
|
|
270
|
-
* occur.
|
|
271
|
-
*
|
|
272
|
-
*
|
|
273
|
-
*
|
|
269
|
+
* `<ocr>` layer, so only its word nodes are parsed, at whatever depth they
|
|
270
|
+
* occur. A word node is any element named `string` in any casing and any
|
|
271
|
+
* namespace, and its text is read from the `CONTENT` attribute.
|
|
272
|
+
*
|
|
273
|
+
* `x` and `y` come from `HPOS` and `VPOS` and locate the top-left corner of
|
|
274
|
+
* the word's box, while `vertices` comes from `VERTICES` and is its full
|
|
275
|
+
* bounding polygon, which is not necessarily rectangular. Every geometry
|
|
276
|
+
* attribute is optional in the source, so each one is null when absent or
|
|
277
|
+
* unparseable, and `vertices` is then empty.
|
|
274
278
|
*
|
|
275
279
|
* `resourceUuid` is the Resource that owns the OCR layer, which differs from
|
|
276
280
|
* the requested item when the OCR lives on a child Resource.
|
package/dist/types/website.d.mts
CHANGED
|
@@ -16,6 +16,10 @@ type ContextTreeLevel<T extends LanguageCodes = LanguageCodes> = {
|
|
|
16
16
|
identification: Identification<T>;
|
|
17
17
|
type: string;
|
|
18
18
|
};
|
|
19
|
+
/**
|
|
20
|
+
* Represents the input control a filter context level is rendered with
|
|
21
|
+
*/
|
|
22
|
+
type ContextTreeFilterVariant = "checkbox" | "chip" | "range" | "tile" | "toggle";
|
|
19
23
|
/**
|
|
20
24
|
* Represents a filter context tree level with a context item
|
|
21
25
|
*/
|
|
@@ -24,6 +28,7 @@ type ContextTreeFilterLevel<T extends LanguageCodes = LanguageCodes> = {
|
|
|
24
28
|
identification: Identification<T>;
|
|
25
29
|
type: string;
|
|
26
30
|
filterType: "property" | "coordinates" | "bibliography" | "period";
|
|
31
|
+
filterVariant: ContextTreeFilterVariant | null;
|
|
27
32
|
isInlineDisplayed: boolean;
|
|
28
33
|
isSidebarDisplayed: boolean;
|
|
29
34
|
isSidebarOpen: boolean;
|
|
@@ -49,6 +54,17 @@ type Scope<T extends LanguageCodes = LanguageCodes> = {
|
|
|
49
54
|
type: string;
|
|
50
55
|
identification: Identification<T>;
|
|
51
56
|
};
|
|
57
|
+
/**
|
|
58
|
+
* Represents the parsed OCHRE "options" block shared by the website itself and
|
|
59
|
+
* by every web element component that supports it
|
|
60
|
+
*/
|
|
61
|
+
type WebOptions<T extends LanguageCodes = LanguageCodes> = {
|
|
62
|
+
scopes: Array<Scope<T>> | null;
|
|
63
|
+
contextTree: ContextTree<T> | null;
|
|
64
|
+
labels: {
|
|
65
|
+
title: MultilingualString<T> | null;
|
|
66
|
+
};
|
|
67
|
+
};
|
|
52
68
|
/**
|
|
53
69
|
* Represents a stylesheet item with its UUID and category
|
|
54
70
|
*/
|
|
@@ -188,16 +204,11 @@ type Website<T extends LanguageCodes = LanguageCodes> = {
|
|
|
188
204
|
isPersistentIdentifierDisplayed: boolean;
|
|
189
205
|
iiifViewer: "universal-viewer" | "clover";
|
|
190
206
|
};
|
|
191
|
-
options: {
|
|
192
|
-
contextTree: ContextTree<T> | null;
|
|
193
|
-
scopes: Array<Scope<T>> | null;
|
|
194
|
-
labels: {
|
|
195
|
-
title: MultilingualString<T> | null;
|
|
196
|
-
};
|
|
207
|
+
options: Prettify<WebOptions<T> & {
|
|
197
208
|
stylesheets: {
|
|
198
209
|
properties: Array<StylesheetItem>;
|
|
199
210
|
};
|
|
200
|
-
}
|
|
211
|
+
}>;
|
|
201
212
|
};
|
|
202
213
|
};
|
|
203
214
|
type WebsiteSegment<T extends LanguageCodes = LanguageCodes> = Website<T> & {
|
|
@@ -285,6 +296,7 @@ type WebElementComponent<T extends LanguageCodes = LanguageCodes> = {
|
|
|
285
296
|
component: "advanced-search";
|
|
286
297
|
boundElementUuid: string | null;
|
|
287
298
|
href: string | null;
|
|
299
|
+
options: WebOptions<T>;
|
|
288
300
|
} | {
|
|
289
301
|
component: "annotated-document";
|
|
290
302
|
linkUuid: string;
|
|
@@ -353,13 +365,7 @@ type WebElementComponent<T extends LanguageCodes = LanguageCodes> = {
|
|
|
353
365
|
isLimitedToLeafPropertyValues: boolean;
|
|
354
366
|
sidebarSort: "default" | "alphabetical";
|
|
355
367
|
};
|
|
356
|
-
options:
|
|
357
|
-
scopes: Array<Scope<T>> | null;
|
|
358
|
-
contextTree: ContextTree<T> | null;
|
|
359
|
-
labels: {
|
|
360
|
-
title: MultilingualString<T> | null;
|
|
361
|
-
};
|
|
362
|
-
};
|
|
368
|
+
options: WebOptions<T>;
|
|
363
369
|
} | {
|
|
364
370
|
component: "empty-space";
|
|
365
371
|
height: string | null;
|
|
@@ -418,13 +424,7 @@ type WebElementComponent<T extends LanguageCodes = LanguageCodes> = {
|
|
|
418
424
|
startIcon: string | null;
|
|
419
425
|
endIcon: string | null;
|
|
420
426
|
}>;
|
|
421
|
-
options:
|
|
422
|
-
scopes: Array<Scope<T>> | null;
|
|
423
|
-
contextTree: ContextTree<T> | null;
|
|
424
|
-
labels: {
|
|
425
|
-
title: MultilingualString<T> | null;
|
|
426
|
-
};
|
|
427
|
-
};
|
|
427
|
+
options: WebOptions<T>;
|
|
428
428
|
collectionProperties: Prettify<Partial<Omit<Extract<WebElementComponent<T>, {
|
|
429
429
|
component: "collection";
|
|
430
430
|
}>, "component" | "linkUuids" | "options" | "image"> & {
|
|
@@ -563,4 +563,4 @@ type ProtectedWebsite<T extends LanguageCodes = LanguageCodes> = {
|
|
|
563
563
|
};
|
|
564
564
|
};
|
|
565
565
|
//#endregion
|
|
566
|
-
export { AccordionWebBlock, ContextTree, ContextTreeFilterLevel, ContextTreeLevel, ContextTreeLevelItem, ProtectedWebsite, Scope, Style, StylesheetCategory, StylesheetItem, WebAccordionItem, WebBlock, WebBlockByLayout, WebBlockItem, WebBlockLayout, WebElement, WebElementComponent, WebElementComponentName, WebElementComponentOf, WebElementOf, WebImage, WebSidebar, WebTitle, Webpage, Website, WebsiteMetadata, WebsitePropertyQuery, WebsitePropertyQueryNode, WebsiteSegment, WebsiteType };
|
|
566
|
+
export { AccordionWebBlock, ContextTree, ContextTreeFilterLevel, ContextTreeFilterVariant, ContextTreeLevel, ContextTreeLevelItem, ProtectedWebsite, Scope, Style, StylesheetCategory, StylesheetItem, WebAccordionItem, WebBlock, WebBlockByLayout, WebBlockItem, WebBlockLayout, WebElement, WebElementComponent, WebElementComponentName, WebElementComponentOf, WebElementOf, WebImage, WebOptions, WebSidebar, WebTitle, Webpage, Website, WebsiteMetadata, WebsitePropertyQuery, WebsitePropertyQueryNode, WebsiteSegment, WebsiteType };
|
package/dist/xml/schemas.mjs
CHANGED
|
@@ -737,7 +737,14 @@ const XMLWebsiteFilterContextItem = v.object({
|
|
|
737
737
|
"sidebar-displayed-closed",
|
|
738
738
|
"sidebar-displayed-open",
|
|
739
739
|
"inline-sidebar-hidden"
|
|
740
|
-
], "XMLWebsiteFilterContextItem: filterOption is invalid"))
|
|
740
|
+
], "XMLWebsiteFilterContextItem: filterOption is invalid")),
|
|
741
|
+
filterVariant: v.optional(v.picklist([
|
|
742
|
+
"checkbox",
|
|
743
|
+
"chip",
|
|
744
|
+
"range",
|
|
745
|
+
"tile",
|
|
746
|
+
"toggle"
|
|
747
|
+
], "XMLWebsiteFilterContextItem: filterVariant is invalid"))
|
|
741
748
|
}, "XMLWebsiteFilterContextItem: Shape error");
|
|
742
749
|
const XMLWebsiteContext = v.object({ context: v.array(XMLWebsiteContextItem) }, "XMLWebsiteContext: Shape error");
|
|
743
750
|
const XMLWebsiteFilterContext = v.object({ context: v.array(XMLWebsiteFilterContextItem) }, "XMLWebsiteFilterContext: Shape error");
|
package/dist/xml/types.d.mts
CHANGED
|
@@ -690,6 +690,7 @@ type XMLWebsiteContextItem = {
|
|
|
690
690
|
type XMLWebsiteFilterContextItem = XMLWebsiteContextItem & {
|
|
691
691
|
filterType?: "property" | "coordinates" | "bibliography" | "period";
|
|
692
692
|
filterOption?: "inline-displayed" | "inline-sidebar-displayed-closed" | "inline-sidebar-displayed-open" | "sidebar-displayed-closed" | "sidebar-displayed-open" | "inline-sidebar-hidden";
|
|
693
|
+
filterVariant?: "checkbox" | "chip" | "range" | "tile" | "toggle";
|
|
693
694
|
};
|
|
694
695
|
type XMLWebsiteContext = {
|
|
695
696
|
context: Array<XMLWebsiteContextItem>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ochre-sdk",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.76",
|
|
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",
|
|
@@ -47,17 +47,17 @@
|
|
|
47
47
|
"dependencies": {
|
|
48
48
|
"date-fns": "^4.4.0",
|
|
49
49
|
"fast-equals": "^6.0.2",
|
|
50
|
-
"fast-xml-parser": "^5.
|
|
50
|
+
"fast-xml-parser": "^5.11.0",
|
|
51
51
|
"valibot": "^1.4.2"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
54
|
"@antfu/eslint-config": "^9.3.0",
|
|
55
55
|
"@types/node": "^24.13.3",
|
|
56
|
-
"bumpp": "^12.2.
|
|
57
|
-
"eslint": "^10.8.
|
|
56
|
+
"bumpp": "^12.2.1",
|
|
57
|
+
"eslint": "^10.8.1",
|
|
58
58
|
"eslint-plugin-erasable-syntax-only": "^0.4.2",
|
|
59
|
-
"knip": "^6.32.
|
|
60
|
-
"oxfmt": "^0.
|
|
59
|
+
"knip": "^6.32.2",
|
|
60
|
+
"oxfmt": "^0.63.0",
|
|
61
61
|
"tsdown": "^0.22.14",
|
|
62
62
|
"typescript": "^6.0.3",
|
|
63
63
|
"vitest": "^4.1.10"
|