Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make possible the use of @id in hasComponent-entities (exemplar) #1450

Open
wants to merge 3 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 12 additions & 6 deletions rest/src/main/groovy/whelk/rest/api/Crud.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -167,11 +167,17 @@ class Crud extends HttpServlet {
HttpServletResponse response) {
Tuple2<Document, String> docAndLocation

if (request.getId() in cachedFetches) {
docAndLocation = cachedFetches[request.getId()]
// If a component-IRI is requested - we quietly and internally redirect the request to fetch the
// containing record instead.
// If there isn't one, fall back to whatever was expressly asked for (necessary for special iris like vocab).
String id = whelk.getStorage().getSystemIdByIri(Document.BASE_URI.toString() + request.getId())
if (id == null)
id = request.getId()

if (id in cachedFetches) {
docAndLocation = cachedFetches[id]
} else {
docAndLocation = getDocumentFromStorage(
request.getId(), request.getVersion().orElse(null))
docAndLocation = getDocumentFromStorage(id, request.getVersion().orElse(null))
}

Document doc = docAndLocation.v1
Expand All @@ -190,7 +196,7 @@ class Crud extends HttpServlet {
History history = new History(whelk.storage.loadDocumentHistory(doc.getShortId()), jsonld)
ETag eTag = ETag.plain(doc.getChecksum(jsonld))
def body = history.m_changeSetsMap
sendGetResponse(response, body, eTag, request.getPath(), request.getContentType(), request.getId())
sendGetResponse(response, body, eTag, request.getPath(), request.getContentType(), id)
} else {
ETag eTag
if (request.shouldEmbellish()) {
Expand Down Expand Up @@ -219,7 +225,7 @@ class Crud extends HttpServlet {
String location = loc ?: doc.id
addProposal25Headers(response, location, getDataURI(location, request))

sendGetResponse(response, body, eTag, request.getPath(), request.getContentType(), request.getId())
sendGetResponse(response, body, eTag, request.getPath(), request.getContentType(), id)
}
}

Expand Down
22 changes: 22 additions & 0 deletions whelk-core/src/main/groovy/whelk/Document.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ class Document {
static final List thingIdPath2 = ["@graph", 1, "@id"]
static final List thingTypePath = ["@graph", 1, "@type"]
static final List thingSameAsPath = ["@graph", 1, "sameAs"]
static final List thingComponentPath = ["@graph", 1, "hasComponent"]
static final List thingTypedIDsPath = ["@graph", 1, "identifiedBy"]
static final List thingIndirectTypedIDsPath = ["@graph", 1, "indirectlyIdentifiedBy"]
static final List thingCarrierTypesPath = ["@graph", 1, "carrierType"]
Expand Down Expand Up @@ -441,6 +442,18 @@ class Document {
return ret
}

List<String> getThingComponentIdentifiers() {
List<String> ret = []

List componentObjects = get(thingComponentPath)
for (Map object : componentObjects) {
if (object.get("@id") != null)
ret.add(object.get("@id"))
}

return ret
}

void addThingIdentifier(String identifier) {
if (get(thingIdPath) == null) {
set(thingIdPath2, identifier)
Expand Down Expand Up @@ -869,6 +882,15 @@ class Document {
}
}

void mintComponentIDs() {
List componentObjects = get(thingComponentPath)
for (Map object : componentObjects) {
Object existingId = object.get("@id")
if (existingId != null && existingId.contains("TEMPID"))
object.put("@id", BASE_URI.toString() + IdGenerator.generate())
}
}

void applyInverses(JsonLd jsonld) {
Map thing = get(thingPath)
jsonld.applyInverses(thing)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1008,6 +1008,7 @@ class PostgreSQLComponent {
}
}

doc.mintComponentIDs()
if (doVerifyDocumentIdRetention) {
verifyDocumentIdRetention(preUpdateDoc, doc, connection)
}
Expand Down Expand Up @@ -1098,11 +1099,13 @@ class PostgreSQLComponent {
HashSet<String> oldIDs = new HashSet<>()
oldIDs.addAll( preUpdateDoc.getRecordIdentifiers() )
oldIDs.addAll( preUpdateDoc.getThingIdentifiers() )
oldIDs.addAll( preUpdateDoc.getThingComponentIdentifiers() )

// Compile list of all new IDs
HashSet<String> newIDs = new HashSet<>()
newIDs.addAll( postUpdateDoc.getRecordIdentifiers() )
newIDs.addAll( postUpdateDoc.getThingIdentifiers() )
newIDs.addAll( postUpdateDoc.getThingComponentIdentifiers() )

// (#work identifiers integrity is not enforced, because doing so would disable breaking out works.)

Expand Down Expand Up @@ -1550,6 +1553,16 @@ class PostgreSQLComponent {
}
}
}
for (altThingId in doc.getThingComponentIdentifiers()) {
// don't re-add thing identifiers if doc is deleted
if (!deleted) {
altIdInsert.setString(1, doc.getShortId())
altIdInsert.setString(2, altThingId)
altIdInsert.setInt(3, 1) // thing id -> graphIndex = 1
altIdInsert.setBoolean(4, false) // alternative ID
altIdInsert.addBatch()
}
}
try {
altIdInsert.executeBatch()
} catch (BatchUpdateException bue) {
Expand Down
22 changes: 14 additions & 8 deletions whelk-core/src/main/groovy/whelk/filter/LinkFinder.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -134,9 +134,12 @@ class LinkFinder {
// Keep looking for more links
for (Object key : data.keySet()) {

// sameAs objects are not links per se, and must not be replaced
// sameAs objects are not links per se, and must not be replaced.
// itemUsed is used to target specific components of holding records. If we let those
// be upgraded, links to those specific components would be lost (replaced with links
// to the record as a whole). So we don't upgrade them. This is a cheat/hack. :(
String keyString = (String) key
if (keyString == "sameAs")
if (keyString == "sameAs" || keyString == "itemUsed")
continue

Object value = data.get(key)
Expand Down Expand Up @@ -174,7 +177,7 @@ class LinkFinder {
/**
* A heavy-handed last line of defense against confusing embedded entities with references.
* After running this, 'document' can no longer have both @id and other data in any same
* embedded entity (root entities are exempt).
* embedded entity (root entities and root entites under hasComponent are exempt).
*/
private void clearReferenceAmbiguities(Document document) {
List graphList = document.data.get(JsonLd.GRAPH_KEY)
Expand Down Expand Up @@ -217,19 +220,22 @@ class LinkFinder {
for (Object key : data.keySet()) {
Object value = data.get(key)

// Components must themselves be considered "root" entities, as they must be allowed both @id and data.
boolean viewNextAsRoot = key.equals("hasComponent")

if (value instanceof List)
clearReferenceAmbiguities_internal( (List) value )
clearReferenceAmbiguities_internal( (List) value, viewNextAsRoot )
if (value instanceof Map)
clearReferenceAmbiguities_internal( (Map) value, false )
clearReferenceAmbiguities_internal( (Map) value, viewNextAsRoot )
}
}

private void clearReferenceAmbiguities_internal(List data) {
private void clearReferenceAmbiguities_internal(List data, boolean isRootEntry) {
for (Object element : data){
if (element instanceof List)
clearReferenceAmbiguities_internal( (List) element )
clearReferenceAmbiguities_internal( (List) element, false )
else if (element instanceof Map)
clearReferenceAmbiguities_internal( (Map) element, false )
clearReferenceAmbiguities_internal( (Map) element, isRootEntry )
}
}
}
Loading