ochre-sdk 0.22.18 → 0.22.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -300,6 +300,7 @@ type Event = {
300
300
  type Interpretation = {
301
301
  date: string | null;
302
302
  number: number;
303
+ notes: Array<Note>;
303
304
  links: Array<Link>;
304
305
  properties: Array<Property>;
305
306
  bibliographies: Array<Bibliography>;
package/dist/index.mjs CHANGED
@@ -1292,7 +1292,7 @@ function parseObservation(observation) {
1292
1292
  * @returns Array of parsed Observation objects
1293
1293
  */
1294
1294
  function parseObservations(observations) {
1295
- return observations.map((obs) => parseObservation(obs));
1295
+ return observations.map((observation) => parseObservation(observation));
1296
1296
  }
1297
1297
  /**
1298
1298
  * Parses an array of raw events into standardized Event objects
@@ -1435,12 +1435,13 @@ function parseProperties(properties, language = "eng") {
1435
1435
  * @returns Array of parsed Interpretation objects
1436
1436
  */
1437
1437
  function parseInterpretations(interpretations) {
1438
- return interpretations.map((interp) => ({
1439
- date: interp.date ?? null,
1440
- number: interp.interpretationNo,
1441
- links: interp.links ? parseLinks(ensureArray(interp.links)) : [],
1442
- properties: interp.properties ? parseProperties(ensureArray(interp.properties.property)) : [],
1443
- bibliographies: interp.bibliographies ? parseBibliographies(ensureArray(interp.bibliographies.bibliography)) : []
1438
+ return interpretations.map((interpretation) => ({
1439
+ date: interpretation.date ?? null,
1440
+ number: interpretation.interpretationNo,
1441
+ notes: interpretation.notes ? parseNotes(ensureArray(interpretation.notes.note)) : [],
1442
+ links: interpretation.links ? parseLinks(ensureArray(interpretation.links)) : [],
1443
+ properties: interpretation.properties ? parseProperties(ensureArray(interpretation.properties.property)) : [],
1444
+ bibliographies: interpretation.bibliographies ? parseBibliographies(ensureArray(interpretation.bibliographies.bibliography)) : []
1444
1445
  }));
1445
1446
  }
1446
1447
  /**
@@ -2275,13 +2276,8 @@ function buildRichTextPhraseQueryExpression(params) {
2275
2276
  const { value, isCaseSensitive } = params;
2276
2277
  return `cts:word-query(${stringLiteral(value)}, ${buildRichTextPhraseOptionsExpression({ isCaseSensitive })})`;
2277
2278
  }
2278
- function buildCtsNearQueryExpression(params) {
2279
- const { queryExpressions, distance, isOrdered = false } = params;
2280
- const options = isOrdered ? `, (${stringLiteral("ordered")})` : "";
2281
- return `cts:near-query((${queryExpressions.join(", ")}), ${distance}${options})`;
2282
- }
2283
2279
  function buildRichTextExactQueryExpression(params) {
2284
- const { value, isCaseSensitive, language } = params;
2280
+ const { value, isCaseSensitive } = params;
2285
2281
  const phraseQuery = buildRichTextPhraseQueryExpression({
2286
2282
  value,
2287
2283
  isCaseSensitive
@@ -2291,17 +2287,10 @@ function buildRichTextExactQueryExpression(params) {
2291
2287
  isCaseSensitive
2292
2288
  });
2293
2289
  if (terms.length <= 1) return phraseQuery;
2294
- return buildOrCtsQueryExpressionInternal([phraseQuery, buildCtsNearQueryExpression({
2295
- queryExpressions: terms.map((term) => buildCtsWordQueryExpression({
2296
- value: term,
2297
- matchMode: "exact",
2298
- isCaseSensitive,
2299
- queryFamily: "text",
2300
- language
2301
- })),
2302
- distance: 2,
2303
- isOrdered: true
2304
- })]);
2290
+ return buildOrCtsQueryExpressionInternal([phraseQuery, buildAndCtsQueryExpressionInternal(terms.map((term) => buildRichTextPhraseQueryExpression({
2291
+ value: term,
2292
+ isCaseSensitive
2293
+ })))]);
2305
2294
  }
2306
2295
  function buildCtsElementWordQueryExpression(params) {
2307
2296
  const { elementName, value, matchMode, isCaseSensitive, queryFamily, language } = params;
@@ -2522,6 +2511,16 @@ function buildPropertyStringQueryExpression(params) {
2522
2511
  value,
2523
2512
  matchMode,
2524
2513
  isCaseSensitive
2514
+ }),
2515
+ rawValueQueryExpression: buildValueRawValueInnerQuery({
2516
+ value,
2517
+ matchMode,
2518
+ isCaseSensitive
2519
+ }),
2520
+ bareValueQueryExpression: buildValueDirectTextInnerQuery({
2521
+ value,
2522
+ matchMode,
2523
+ isCaseSensitive
2525
2524
  })
2526
2525
  });
2527
2526
  }
@@ -3041,6 +3040,47 @@ function buildOrderedItemsClause(sort) {
3041
3040
  })}
3042
3041
  return $item`;
3043
3042
  }
3043
+ function isExactStringPropertyQuery(query) {
3044
+ return "target" in query && query.target === "property" && query.dataType === "string" && query.value != null && query.matchMode === "exact" && query.isNegated !== true;
3045
+ }
3046
+ function getCtsQueriesWithoutExactStringPropertyQueries(queries) {
3047
+ if (queries == null) return null;
3048
+ if ("target" in queries) return isExactStringPropertyQuery(queries) ? null : queries;
3049
+ if ("or" in queries) return queries;
3050
+ const filteredChildren = [];
3051
+ for (const childQuery of queries.and) {
3052
+ const filteredChildQuery = getCtsQueriesWithoutExactStringPropertyQueries(childQuery);
3053
+ if (filteredChildQuery != null) filteredChildren.push(filteredChildQuery);
3054
+ }
3055
+ if (filteredChildren.length === 0) return null;
3056
+ return filteredChildren.length === 1 ? filteredChildren[0] ?? null : { and: filteredChildren };
3057
+ }
3058
+ function buildExactStringPropertyPredicate(query) {
3059
+ const propertyPredicates = [];
3060
+ const value = stringLiteral(query.value);
3061
+ if (query.propertyVariable != null) propertyPredicates.push(`label/@uuid = ${stringLiteral(query.propertyVariable)}`);
3062
+ propertyPredicates.push(`value[
3063
+ not(@inherited = "true")
3064
+ and (
3065
+ content[@xml:lang = ${stringLiteral(query.language)}]/string = ${value}
3066
+ or @rawValue = ${value}
3067
+ or (not(content) and text() = ${value})
3068
+ )
3069
+ ]`);
3070
+ return `.//properties/property[${propertyPredicates.join(" and ")}]`;
3071
+ }
3072
+ function buildExactStringPropertyXPathFilterExpression(queries) {
3073
+ if (queries == null) return null;
3074
+ if ("target" in queries) return isExactStringPropertyQuery(queries) ? buildExactStringPropertyPredicate(queries) : null;
3075
+ if ("or" in queries) return null;
3076
+ const childExpressions = [];
3077
+ for (const childQuery of queries.and) {
3078
+ const childExpression = buildExactStringPropertyXPathFilterExpression(childQuery);
3079
+ if (childExpression != null) childExpressions.push(childExpression);
3080
+ }
3081
+ if (childExpressions.length === 0) return null;
3082
+ return childExpressions.join(" and ");
3083
+ }
3044
3084
  /**
3045
3085
  * Build an XQuery string to fetch Set items from the OCHRE API
3046
3086
  * @param params - The parameters for the fetch
@@ -3058,7 +3098,9 @@ function buildXQuery$1(params) {
3058
3098
  const startPosition = (page - 1) * pageSize + 1;
3059
3099
  const setScopeDeclaration = `declare variable $setScopeUuids := (${setScopeUuids.map((uuid) => stringLiteral(uuid)).join(", ")});`;
3060
3100
  const baseItemsExpression = "doc()/ochre/set[@uuid = $setScopeUuids]/items/*";
3061
- const compiledQueryPlan = buildQueryPlan({ queries });
3101
+ const ctsQueries = getCtsQueriesWithoutExactStringPropertyQueries(queries);
3102
+ const exactStringPropertyXPathFilterExpression = buildExactStringPropertyXPathFilterExpression(queries);
3103
+ const compiledQueryPlan = buildQueryPlan({ queries: ctsQueries });
3062
3104
  const itemsQueryExpressions = [];
3063
3105
  const belongsToCollectionQueryExpression = buildBelongsToCollectionQueryExpression(belongsToCollectionScopeUuids, BELONGS_TO_COLLECTION_UUID);
3064
3106
  if (compiledQueryPlan.queryExpression != null) itemsQueryExpressions.push(compiledQueryPlan.queryExpression);
@@ -3067,8 +3109,11 @@ function buildXQuery$1(params) {
3067
3109
  const orderedItemsClause = buildOrderedItemsClause(sort);
3068
3110
  const xqueryDeclarations = ["xquery version \"1.0-ml\";", setScopeDeclaration];
3069
3111
  if (compiledQueryPlan.prolog !== "") xqueryDeclarations.push(compiledQueryPlan.prolog);
3070
- const itemsClause = itemsQueryExpression == null ? `let $items := ${baseItemsExpression}` : `let $query := ${itemsQueryExpression}
3071
- let $items := cts:search(${baseItemsExpression}, $query)`;
3112
+ const searchedItemsClause = itemsQueryExpression == null ? `let $searchedItems := ${baseItemsExpression}` : `let $query := ${itemsQueryExpression}
3113
+ let $searchedItems := cts:search(${baseItemsExpression}, $query)`;
3114
+ const itemsClause = exactStringPropertyXPathFilterExpression == null ? `${searchedItemsClause}
3115
+ let $items := $searchedItems` : `${searchedItemsClause}
3116
+ let $items := $searchedItems[${exactStringPropertyXPathFilterExpression}]`;
3072
3117
  return `${xqueryDeclarations.join("\n\n")}
3073
3118
 
3074
3119
  <ochre>{
@@ -3201,67 +3246,27 @@ function parsePropertyValueBooleanContent(rawValue) {
3201
3246
  if (typeof rawValue === "boolean") return rawValue;
3202
3247
  return rawValue.toString().toLocaleLowerCase("en-US") === "true";
3203
3248
  }
3204
- function getPropertyValueGroupKey(value) {
3205
- const contentKey = value.content == null ? "null" : `${typeof value.content}:${value.content.toLocaleString("en-US")}`;
3206
- return `${value.dataType}|${contentKey}`;
3207
- }
3208
- function aggregatePropertyValues(values) {
3209
- const groupedPropertyValuesMap = /* @__PURE__ */ new Map();
3210
- for (const value of values) {
3211
- const key = getPropertyValueGroupKey(value);
3212
- const existing = groupedPropertyValuesMap.get(key);
3213
- if (existing == null) {
3214
- groupedPropertyValuesMap.set(key, {
3215
- dataType: value.dataType,
3216
- content: value.content,
3217
- label: value.label,
3218
- itemUuids: new Set([value.itemUuid])
3219
- });
3220
- continue;
3221
- }
3222
- existing.itemUuids.add(value.itemUuid);
3223
- if (existing.label == null && value.label != null) existing.label = value.label;
3224
- }
3225
- const groupedPropertyValues = [];
3226
- for (const group of groupedPropertyValuesMap.values()) {
3227
- if (group.content == null) continue;
3228
- groupedPropertyValues.push({
3229
- count: group.itemUuids.size,
3230
- dataType: group.dataType,
3231
- content: group.content,
3232
- label: group.label
3233
- });
3234
- }
3235
- return groupedPropertyValues.toSorted((a, b) => {
3249
+ function sortPropertyValues(values) {
3250
+ return values.toSorted((a, b) => {
3236
3251
  if (a.count !== b.count) return b.count - a.count;
3237
3252
  if (a.label !== b.label) return a.label?.localeCompare(b.label ?? "") ?? 0;
3238
3253
  return a.content?.toString().localeCompare(b.content?.toString() ?? "") ?? 0;
3239
3254
  });
3240
3255
  }
3241
- function aggregateAttributeValues(values) {
3242
- const groupedAttributeValuesMap = /* @__PURE__ */ new Map();
3243
- for (const value of values) {
3244
- if (value.content == null || value.content === "") continue;
3245
- const existing = groupedAttributeValuesMap.get(value.content);
3246
- if (existing == null) {
3247
- groupedAttributeValuesMap.set(value.content, {
3248
- content: value.content,
3249
- itemUuids: new Set([value.itemUuid])
3250
- });
3251
- continue;
3252
- }
3253
- existing.itemUuids.add(value.itemUuid);
3254
- }
3255
- const groupedAttributeValues = [];
3256
- for (const group of groupedAttributeValuesMap.values()) groupedAttributeValues.push({
3257
- count: group.itemUuids.size,
3258
- content: group.content
3259
- });
3260
- return groupedAttributeValues.toSorted((a, b) => {
3256
+ function getPropertyValueKey(value) {
3257
+ return `${value.dataType}|${typeof value.content}:${value.content.toLocaleString("en-US")}`;
3258
+ }
3259
+ function sortAttributeValues(values) {
3260
+ return values.toSorted((a, b) => {
3261
3261
  if (a.count !== b.count) return b.count - a.count;
3262
3262
  return a.content.localeCompare(b.content);
3263
3263
  });
3264
3264
  }
3265
+ const countSchema = z.union([z.number(), z.string()]).optional().transform((val) => {
3266
+ if (val == null || val === "") return 1;
3267
+ const count = Number(val);
3268
+ return Number.isFinite(count) ? count : 1;
3269
+ });
3265
3270
  function getPropertyVariableUuidsFromQueries(queries) {
3266
3271
  const propertyVariableUuids = /* @__PURE__ */ new Set();
3267
3272
  if (queries == null) return [];
@@ -3298,8 +3303,10 @@ function getItemFilterQueriesFromPropertyValueQueries(queries) {
3298
3303
  */
3299
3304
  const propertyValueQueryItemSchema = z.object({
3300
3305
  uuid: z.string(),
3306
+ scope: z.enum(["global", "variable"]).default("global"),
3301
3307
  variableUuid: z.string().optional(),
3302
- itemUuid: z.string().optional(),
3308
+ count: countSchema,
3309
+ globalCount: countSchema.nullish(),
3303
3310
  dataType: z.string(),
3304
3311
  rawValue: fakeStringSchema.optional(),
3305
3312
  content: z.union([
@@ -3309,8 +3316,10 @@ const propertyValueQueryItemSchema = z.object({
3309
3316
  ]).optional()
3310
3317
  }).transform((val) => {
3311
3318
  const returnValue = {
3319
+ scope: val.scope,
3312
3320
  variableUuid: val.variableUuid != null && val.variableUuid !== "" ? val.variableUuid : null,
3313
- itemUuid: val.itemUuid != null && val.itemUuid !== "" ? val.itemUuid : null,
3321
+ count: val.count,
3322
+ globalCount: val.globalCount ?? null,
3314
3323
  dataType: val.dataType,
3315
3324
  content: null,
3316
3325
  label: null
@@ -3342,11 +3351,11 @@ const propertyValueQueryItemSchema = z.object({
3342
3351
  });
3343
3352
  const attributeValueQueryItemSchema = z.object({
3344
3353
  attributeType: z.enum(["bibliographies", "periods"]),
3345
- itemUuid: z.string().optional(),
3354
+ count: countSchema,
3346
3355
  content: z.string().optional()
3347
3356
  }).transform((val) => ({
3348
3357
  attributeType: val.attributeType,
3349
- itemUuid: val.itemUuid != null && val.itemUuid !== "" ? val.itemUuid : null,
3358
+ count: val.count,
3350
3359
  content: val.content != null && val.content !== "" ? val.content : null
3351
3360
  }));
3352
3361
  /**
@@ -3371,9 +3380,8 @@ const responseSchema = z.object({ result: z.union([z.object({ ochre: z.object({
3371
3380
  */
3372
3381
  function buildXQuery(params) {
3373
3382
  const { setScopeUuids, belongsToCollectionScopeUuids, queries, propertyVariableUuids, attributes, isLimitedToLeafPropertyValues } = params;
3374
- const setScopeValues = setScopeUuids.map((uuid) => stringLiteral(uuid));
3375
- const setScopeDeclaration = setScopeValues.length > 0 ? `declare variable $setScopeUuids := (${setScopeValues.join(", ")});` : "";
3376
- const baseItemsExpression = setScopeValues.length > 0 ? "doc()/ochre/set[@uuid = $setScopeUuids]/items/*" : "doc()/ochre/set/items/*";
3383
+ const setScopeDeclaration = `declare variable $setScopeUuids := (${setScopeUuids.map((uuid) => stringLiteral(uuid)).join(", ")});`;
3384
+ const baseItemsExpression = "doc()/ochre/set[@uuid = $setScopeUuids]/items/*";
3377
3385
  const compiledQueryPlan = buildQueryPlan({ queries: getItemFilterQueriesFromPropertyValueQueries(queries) });
3378
3386
  const itemsQueryExpressions = [];
3379
3387
  const belongsToCollectionQueryExpression = buildBelongsToCollectionQueryExpression(belongsToCollectionScopeUuids, BELONGS_TO_COLLECTION_UUID);
@@ -3383,39 +3391,186 @@ function buildXQuery(params) {
3383
3391
  const valueFilter = isLimitedToLeafPropertyValues ? "[not(@i)]" : "";
3384
3392
  const queryBlocks = [];
3385
3393
  const returnedSequences = [];
3386
- const xqueryDeclarations = ["xquery version \"1.0-ml\";"];
3387
- if (setScopeDeclaration !== "") xqueryDeclarations.push(setScopeDeclaration);
3394
+ const xqueryDeclarations = [
3395
+ "xquery version \"1.0-ml\";",
3396
+ "declare namespace map = \"http://marklogic.com/xdmp/map\";",
3397
+ setScopeDeclaration,
3398
+ `declare function local:increment-count($counts, $key) {
3399
+ let $current := map:get($counts, $key)
3400
+ return map:put(
3401
+ $counts,
3402
+ $key,
3403
+ if (empty($current)) then 1 else xs:integer($current) + 1
3404
+ )
3405
+ };
3406
+
3407
+ declare function local:value-display($v) {
3408
+ if ($v/content)
3409
+ then string-join($v/content[@xml:lang="eng"]//text(), "")
3410
+ else string($v)
3411
+ };
3412
+
3413
+ declare function local:value-content($data-type, $raw-value, $value-uuid, $display) {
3414
+ if ($data-type = "IDREF") then $value-uuid
3415
+ else if ($data-type = ("integer", "decimal", "time")) then
3416
+ if ($raw-value castable as xs:double)
3417
+ then string(xs:double($raw-value))
3418
+ else ""
3419
+ else if ($data-type = "boolean") then
3420
+ if ($raw-value = "") then ""
3421
+ else if (lower-case($raw-value) = "true") then "true"
3422
+ else "false"
3423
+ else if ($raw-value != "") then $raw-value
3424
+ else if ($display != "" and $display != "<unassigned>") then $display
3425
+ else ""
3426
+ };
3427
+
3428
+ declare function local:value-kind($data-type) {
3429
+ if ($data-type = ("integer", "decimal", "time")) then "number"
3430
+ else if ($data-type = "boolean") then "boolean"
3431
+ else "string"
3432
+ };
3433
+
3434
+ declare function local:property-output-raw-value($data-type, $raw-value, $content) {
3435
+ if ($data-type = ("integer", "decimal", "time", "boolean")) then $content
3436
+ else $raw-value
3437
+ };
3438
+
3439
+ declare function local:put-property-detail(
3440
+ $details,
3441
+ $key,
3442
+ $scope,
3443
+ $variable-uuid,
3444
+ $value-uuid,
3445
+ $raw-value,
3446
+ $data-type,
3447
+ $display
3448
+ ) {
3449
+ let $existing := map:get($details, $key)
3450
+ return
3451
+ if (
3452
+ empty($existing)
3453
+ or (string-length(string($existing)) = 0 and string-length($display) gt 0)
3454
+ ) then
3455
+ map:put(
3456
+ $details,
3457
+ $key,
3458
+ <propertyValue scope="{$scope}" variableUuid="{$variable-uuid}" uuid="{$value-uuid}" rawValue="{$raw-value}" dataType="{$data-type}">{$display}</propertyValue>
3459
+ )
3460
+ else ()
3461
+ };
3462
+
3463
+ declare function local:add-property-facet(
3464
+ $counts,
3465
+ $details,
3466
+ $seen,
3467
+ $key,
3468
+ $scope,
3469
+ $variable-uuid,
3470
+ $value-uuid,
3471
+ $raw-value,
3472
+ $data-type,
3473
+ $display
3474
+ ) {
3475
+ if (exists(map:get($seen, $key))) then ()
3476
+ else (
3477
+ map:put($seen, $key, true()),
3478
+ local:increment-count($counts, $key),
3479
+ local:put-property-detail($details, $key, $scope, $variable-uuid, $value-uuid, $raw-value, $data-type, $display)
3480
+ )
3481
+ };
3482
+
3483
+ declare function local:add-attribute-facet($counts, $seen, $key) {
3484
+ if (exists(map:get($seen, $key))) then ()
3485
+ else (
3486
+ map:put($seen, $key, true()),
3487
+ local:increment-count($counts, $key)
3488
+ )
3489
+ };`
3490
+ ];
3388
3491
  if (compiledQueryPlan.prolog !== "") xqueryDeclarations.push(compiledQueryPlan.prolog);
3389
3492
  if (propertyVariableUuids.length > 0) {
3390
- const propertyVariableFilters = propertyVariableUuids.map((uuid) => `@uuid="${uuid}"`).join(" or ");
3391
- queryBlocks.push(`let $matching-props := $items//property[label[${propertyVariableFilters}]]
3493
+ const propertyVariableValues = propertyVariableUuids.map((uuid) => stringLiteral(uuid));
3494
+ xqueryDeclarations.push(`declare variable $facetLabelUuids := (${propertyVariableValues.join(", ")});`);
3495
+ queryBlocks.push(`let $global-property-counts := map:map()
3496
+ let $variable-property-counts := map:map()
3497
+ let $variable-property-details := map:map()
3498
+ let $variable-property-global-keys := map:map()
3499
+ let $_property-aggregation := xdmp:eager(
3500
+ for $item in $items
3501
+ let $global-seen := map:map()
3502
+ let $variable-seen := map:map()
3503
+ return
3504
+ for $p in $item/properties/property[label/@uuid = $facetLabelUuids]
3505
+ let $variable-uuid := string($p/label/@uuid)
3506
+ for $v in $p/value${valueFilter}
3507
+ let $value-uuid := string($v/@uuid)
3508
+ let $raw-value := string($v/@rawValue)
3509
+ let $data-type := string($v/@dataType)
3510
+ let $display := local:value-display($v)
3511
+ let $content := local:value-content($data-type, $raw-value, $value-uuid, $display)
3512
+ let $value-kind := local:value-kind($data-type)
3513
+ let $output-raw-value := local:property-output-raw-value($data-type, $raw-value, $content)
3514
+ let $global-key := string-join(($data-type, $value-kind, $content), "||")
3515
+ let $variable-key := string-join(($variable-uuid, $data-type, $value-kind, $content), "||")
3516
+ where $content != ""
3517
+ return (
3518
+ local:add-attribute-facet($global-property-counts, $global-seen, $global-key),
3519
+ local:add-property-facet($variable-property-counts, $variable-property-details, $variable-seen, $variable-key, "variable", $variable-uuid, $value-uuid, $output-raw-value, $data-type, $display),
3520
+ map:put($variable-property-global-keys, $variable-key, $global-key)
3521
+ )
3522
+ )
3392
3523
 
3393
3524
  let $property-values :=
3394
- for $p in $matching-props
3395
- for $v in $p/value${valueFilter}
3396
- let $item-uuid := $v/ancestor::*[parent::items]/@uuid
3397
- let $variable-uuid := $p/label/@uuid
3398
- return <propertyValue uuid="{$v/@uuid}" rawValue="{$v/@rawValue}" dataType="{$v/@dataType}" itemUuid="{$item-uuid}" variableUuid="{$variable-uuid}">{
3399
- if ($v/content) then string-join($v/content[@xml:lang="eng"]//text(), "") else $v/text()
3400
- }</propertyValue>`);
3525
+ (
3526
+ $_property-aggregation,
3527
+ for $key in map:keys($variable-property-counts)
3528
+ let $detail := map:get($variable-property-details, $key)
3529
+ let $global-key := map:get($variable-property-global-keys, $key)
3530
+ return <propertyValue scope="variable" variableUuid="{string($detail/@variableUuid)}" uuid="{string($detail/@uuid)}" rawValue="{string($detail/@rawValue)}" dataType="{string($detail/@dataType)}" count="{map:get($variable-property-counts, $key)}" globalCount="{map:get($global-property-counts, $global-key)}">{
3531
+ string($detail)
3532
+ }</propertyValue>
3533
+ )`);
3401
3534
  returnedSequences.push("$property-values");
3402
3535
  }
3403
3536
  if (attributes.bibliographies) {
3404
- queryBlocks.push(`let $bibliography-values :=
3537
+ queryBlocks.push(`let $bibliography-counts := map:map()
3538
+ let $_bibliography-aggregation := xdmp:eager(
3405
3539
  for $item in $items
3406
- for $bibliography in $item/bibliographies/bibliography
3540
+ let $seen := map:map()
3541
+ return
3542
+ for $bibliography in $item/bibliographies/bibliography
3407
3543
  let $label := string-join($bibliography/identification/label/content[@xml:lang="eng"]//text(), "")
3408
3544
  where string-length($label) gt 0
3409
- return <attributeValue attributeType="bibliographies" itemUuid="{$item/@uuid}" content="{$label}" />`);
3545
+ return local:add-attribute-facet($bibliography-counts, $seen, $label)
3546
+ )
3547
+
3548
+ let $bibliography-values :=
3549
+ (
3550
+ $_bibliography-aggregation,
3551
+ for $label in map:keys($bibliography-counts)
3552
+ return <attributeValue attributeType="bibliographies" count="{map:get($bibliography-counts, $label)}" content="{$label}" />
3553
+ )`);
3410
3554
  returnedSequences.push("$bibliography-values");
3411
3555
  }
3412
3556
  if (attributes.periods) {
3413
- queryBlocks.push(`let $period-values :=
3557
+ queryBlocks.push(`let $period-counts := map:map()
3558
+ let $_period-aggregation := xdmp:eager(
3414
3559
  for $item in $items
3415
- for $period in $item/periods/period
3560
+ let $seen := map:map()
3561
+ return
3562
+ for $period in $item/periods/period
3416
3563
  let $label := string-join($period/identification/label/content[@xml:lang="eng"]//text(), "")
3417
3564
  where string-length($label) gt 0
3418
- return <attributeValue attributeType="periods" itemUuid="{$item/@uuid}" content="{$label}" />`);
3565
+ return local:add-attribute-facet($period-counts, $seen, $label)
3566
+ )
3567
+
3568
+ let $period-values :=
3569
+ (
3570
+ $_period-aggregation,
3571
+ for $label in map:keys($period-counts)
3572
+ return <attributeValue attributeType="periods" count="{map:get($period-counts, $label)}" content="{$label}" />
3573
+ )`);
3419
3574
  returnedSequences.push("$period-values");
3420
3575
  }
3421
3576
  const itemsClause = itemsQueryExpression == null ? `let $items := ${baseItemsExpression}` : `let $query := ${itemsQueryExpression}
@@ -3481,35 +3636,50 @@ async function fetchSetPropertyValues(params, options) {
3481
3636
  if (parsedResultRaw.result.ochre.propertyValue != null) parsedPropertyValues.push(...Array.isArray(parsedResultRaw.result.ochre.propertyValue) ? parsedResultRaw.result.ochre.propertyValue : [parsedResultRaw.result.ochre.propertyValue]);
3482
3637
  if (parsedResultRaw.result.ochre.attributeValue != null) parsedAttributeValues.push(...Array.isArray(parsedResultRaw.result.ochre.attributeValue) ? parsedResultRaw.result.ochre.attributeValue : [parsedResultRaw.result.ochre.attributeValue]);
3483
3638
  }
3484
- const propertyValuesByPropertyVariableUuidRaw = {};
3485
- const flattenedPropertyValues = [];
3639
+ const propertyValuesByPropertyVariableUuid = {};
3640
+ const flattenedPropertyValuesByKey = /* @__PURE__ */ new Map();
3486
3641
  for (const propertyValue of parsedPropertyValues) {
3487
- const aggregatePropertyValueItem = {
3488
- itemUuid: propertyValue.itemUuid,
3642
+ if (propertyValue.content == null) continue;
3643
+ const propertyValueItem = {
3644
+ count: propertyValue.count,
3489
3645
  dataType: propertyValue.dataType,
3490
3646
  content: propertyValue.content,
3491
3647
  label: propertyValue.label
3492
3648
  };
3493
- flattenedPropertyValues.push(aggregatePropertyValueItem);
3494
- if (propertyValue.variableUuid == null) continue;
3495
- (propertyValuesByPropertyVariableUuidRaw[propertyValue.variableUuid] ??= []).push(aggregatePropertyValueItem);
3496
- }
3497
- const propertyValuesByPropertyVariableUuid = {};
3498
- for (const [propertyVariableUuid, values] of Object.entries(propertyValuesByPropertyVariableUuidRaw)) {
3499
- const aggregatedValues = aggregatePropertyValues(values);
3500
- if (aggregatedValues.length > 0) propertyValuesByPropertyVariableUuid[propertyVariableUuid] = aggregatedValues;
3649
+ const globalPropertyValueItem = {
3650
+ count: propertyValue.globalCount ?? propertyValue.count,
3651
+ dataType: propertyValue.dataType,
3652
+ content: propertyValue.content,
3653
+ label: propertyValue.label
3654
+ };
3655
+ const globalPropertyValueKey = getPropertyValueKey({
3656
+ dataType: globalPropertyValueItem.dataType,
3657
+ content: propertyValue.content
3658
+ });
3659
+ const existingGlobalPropertyValue = flattenedPropertyValuesByKey.get(globalPropertyValueKey);
3660
+ if (existingGlobalPropertyValue == null) flattenedPropertyValuesByKey.set(globalPropertyValueKey, globalPropertyValueItem);
3661
+ else if (existingGlobalPropertyValue.label == null && globalPropertyValueItem.label != null) existingGlobalPropertyValue.label = globalPropertyValueItem.label;
3662
+ if (propertyValue.scope === "global") continue;
3663
+ if (propertyValue.variableUuid != null) (propertyValuesByPropertyVariableUuid[propertyValue.variableUuid] ??= []).push(propertyValueItem);
3501
3664
  }
3502
- const attributeValuesByTypeRaw = {
3665
+ for (const [propertyVariableUuid, values] of Object.entries(propertyValuesByPropertyVariableUuid)) propertyValuesByPropertyVariableUuid[propertyVariableUuid] = sortPropertyValues(values);
3666
+ const attributeValuesByType = {
3503
3667
  bibliographies: [],
3504
3668
  periods: []
3505
3669
  };
3506
- for (const attributeValue of parsedAttributeValues) attributeValuesByTypeRaw[attributeValue.attributeType].push(attributeValue);
3670
+ for (const attributeValue of parsedAttributeValues) {
3671
+ if (attributeValue.content == null || attributeValue.content === "") continue;
3672
+ attributeValuesByType[attributeValue.attributeType].push({
3673
+ count: attributeValue.count,
3674
+ content: attributeValue.content
3675
+ });
3676
+ }
3507
3677
  return {
3508
- propertyValues: aggregatePropertyValues(flattenedPropertyValues),
3678
+ propertyValues: sortPropertyValues([...flattenedPropertyValuesByKey.values()]),
3509
3679
  propertyValuesByPropertyVariableUuid,
3510
3680
  attributeValues: {
3511
- bibliographies: attributes.bibliographies ? aggregateAttributeValues(attributeValuesByTypeRaw.bibliographies) : null,
3512
- periods: attributes.periods ? aggregateAttributeValues(attributeValuesByTypeRaw.periods) : null
3681
+ bibliographies: attributes.bibliographies ? sortAttributeValues(attributeValuesByType.bibliographies) : null,
3682
+ periods: attributes.periods ? sortAttributeValues(attributeValuesByType.periods) : null
3513
3683
  },
3514
3684
  error: null
3515
3685
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ochre-sdk",
3
- "version": "0.22.18",
3
+ "version": "0.22.20",
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,7 +46,7 @@
46
46
  "@date-fns/utc": "^2.1.1",
47
47
  "date-fns": "^4.1.0",
48
48
  "fast-equals": "^6.0.0",
49
- "zod": "^4.3.6"
49
+ "zod": "^4.4.1"
50
50
  },
51
51
  "devDependencies": {
52
52
  "@antfu/eslint-config": "^8.2.0",
@@ -54,9 +54,9 @@
54
54
  "bumpp": "^11.0.1",
55
55
  "eslint": "^10.2.1",
56
56
  "prettier": "^3.8.3",
57
- "tsdown": "^0.21.9",
57
+ "tsdown": "^0.21.10",
58
58
  "typescript": "^6.0.3",
59
- "vitest": "^4.1.4"
59
+ "vitest": "^4.1.5"
60
60
  },
61
61
  "scripts": {
62
62
  "dev": "tsdown src/index.ts --watch",