Skip to content

Commit e10f95e

Browse files
authored
Create tool for retrieving object children. (#88)
- Includes indexing adjustments for more accurate hierarchical representation.
1 parent 3756945 commit e10f95e

21 files changed

+7781
-6192
lines changed

api/src/routes/edit.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,32 @@ import express = require("express");
22
import Config from "../models/Config";
33
const edit = express.Router();
44
import { requireToken } from "./auth";
5+
import { pidSanitizer } from "./sanitize";
6+
import Solr from "../services/Solr";
57

68
edit.get("/models", requireToken, function (req, res) {
79
res.json({ CollectionModels: Config.getInstance().collectionModels, DataModels: Config.getInstance().dataModels });
810
});
911

12+
async function getChildren(req, res) {
13+
const query =
14+
(req.params.pid ?? "").length > 0
15+
? `fedora_parent_id_str_mv:"${req.params.pid.replace('"', "")}"`
16+
: "-fedora_parent_id_str_mv:*";
17+
const config = Config.getInstance();
18+
const solr = Solr.getInstance();
19+
const rows = parseInt(req.query.rows ?? "100000").toString();
20+
const start = parseInt(req.query.start ?? "0").toString();
21+
const result = await solr.query(config.solrCore, query, { fl: "id,title", rows, start });
22+
if (result.statusCode !== 200) {
23+
res.status(result.statusCode ?? 500).send("Unexpected Solr response code.");
24+
return;
25+
}
26+
const response = result?.body?.response ?? { numFound: 0, start: 0, docs: [] };
27+
res.json(response);
28+
}
29+
30+
edit.get("/object/children", requireToken, getChildren);
31+
edit.get("/object/children/:pid", requireToken, pidSanitizer, getChildren);
32+
1033
export default edit;

api/src/routes/messenger.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,9 @@ import express = require("express");
44
import Config from "../models/Config";
55
import { Queue } from "bullmq";
66
import { requireToken } from "./auth";
7-
import { sanitizeParameters } from "./sanitize";
7+
import { pidSanitizer } from "./sanitize";
88
const messenger = express.Router();
99

10-
const pidSanitizer = sanitizeParameters({ pid: /^[a-zA-Z]+:[0-9]+/ }, /^$/);
11-
1210
messenger.post("/pdfgenerator/:pid", pidSanitizer, requireToken, async function (req, res) {
1311
const q = new Queue("vudl");
1412
await q.add("generatepdf", { pid: req.params.pid });

api/src/routes/sanitize.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,5 @@ export function sanitizeParameters(customRules = {}, defaultRule = /^[-.a-zA-Z0-
1414
next();
1515
};
1616
}
17+
18+
export const pidSanitizer = sanitizeParameters({ pid: /^[a-zA-Z]+:[0-9]+/ }, /^$/);

api/src/services/HierarchyCollector.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -81,11 +81,9 @@ class HierarchyCollector {
8181
// Create promises to retrieve parents asynchronously...
8282
const promises = (result.relations.isMemberOf ?? []).map(async (resource) => {
8383
const parentPid = resource.substr("info:fedora/".length);
84-
if (!this.config.topLevelPids.includes(parentPid)) {
85-
// The "false" here skips RDF retrieval:
86-
const parent = await this.getHierarchy(parentPid, false);
87-
result.addParent(parent);
88-
}
84+
// The "false" here skips RDF retrieval:
85+
const parent = await this.getHierarchy(parentPid, false);
86+
result.addParent(parent);
8987
});
9088
// Now wait for the promises to complete before we return results, so
9189
// nothing happens out of order.

api/src/services/Solr.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,18 @@ class Solr {
3232
return http(method, url, data, options);
3333
}
3434

35+
public async query(
36+
core: string,
37+
solrQuery: string,
38+
queryParams: Record<string, string> = {}
39+
): Promise<NeedleResponse> {
40+
const formatter = ([key, val]) => {
41+
return "&" + encodeURIComponent(key) + "=" + encodeURIComponent(val);
42+
};
43+
const extras = Object.entries(queryParams).map(formatter).join("");
44+
return this._request("get", core + "/select?q=" + encodeURIComponent(solrQuery) + extras);
45+
}
46+
3547
public async deleteRecord(core: string, pid: string): Promise<NeedleResponse> {
3648
// Strip double quotes from PID -- they should never be present, and it protects
3749
// against malicious query manipulation.

api/src/services/SolrIndexer.ts

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -106,19 +106,26 @@ class SolrIndexer {
106106
fields[dynamic_sequence_field_name] = this.padNumber(seqNum);
107107
}
108108

109-
// Process parent data:
109+
// Process parent data (note that hierarchyParents makes some special exceptions for VuFind;
110+
// fedoraParents exactly maintains the hierarchy as represented in Fedora):
110111
const hierarchyParents: Array<FedoraData> = [];
112+
const fedoraParents: Array<FedoraData> = [];
111113
const hierarchySequences: Array<string> = [];
112114
for (const parent of fedoraData.parents) {
113-
// If the object is a Data, the parentPID is the Resource it belongs
115+
// Fedora parents should directly reflect the repository without any
116+
// VuFind-specific filtering or adjustments:
117+
fedoraParents.push(parent);
118+
119+
// If the object is a Data, the VuFind parentPID is the Resource it belongs
114120
// to (skip the List object):
115121
if (fedoraData.models.includes("vudl-system:DataModel")) {
116122
for (const grandParent of parent.parents) {
117123
hierarchyParents.push(grandParent);
118124
hierarchySequences.push(this.padNumber(sequenceIndex[grandParent.pid] ?? 0));
119125
}
120-
} else {
121-
// ...else it is the immediate parent (Folder most likely):
126+
} else if (!this.config.topLevelPids.includes(pid)) {
127+
// ...for non-Data objects, store the immediate parent (Folder most likely)
128+
// as long as the current pid is not marked as a top-level one:
122129
hierarchyParents.push(parent);
123130
hierarchySequences.push(this.padNumber(sequenceIndex[parent.pid] ?? 0));
124131
}
@@ -134,6 +141,7 @@ class SolrIndexer {
134141
}
135142
}
136143
}
144+
fields.fedora_parent_id_str_mv = fedoraParents.map((parent) => parent.pid);
137145
if (hierarchyParents.length > 0) {
138146
// This is what we are collapsing on:
139147
fields.hierarchy_first_parent_id_str = fedoraData.models.includes("vudl-system:DataModel")

0 commit comments

Comments
 (0)