lookup relationship by type, allow entity lookup by meta

This commit is contained in:
tysonlt
2021-07-14 13:01:03 +10:00
parent 4972ee3f98
commit e53ed6151a
2 changed files with 194 additions and 45 deletions

View File

@@ -54,6 +54,7 @@ import org.apache.logging.log4j.Logger;
import org.apache.xpath.XPathAPI;
import org.dspace.app.itemimport.service.ItemImportService;
import org.dspace.app.util.LocalSchemaFilenameFilter;
import org.dspace.app.util.RelationshipUtils;
import org.dspace.authorize.AuthorizeException;
import org.dspace.authorize.ResourcePolicy;
import org.dspace.authorize.service.AuthorizeService;
@@ -67,6 +68,7 @@ import org.dspace.content.Item;
import org.dspace.content.MetadataField;
import org.dspace.content.MetadataSchema;
import org.dspace.content.MetadataSchemaEnum;
import org.dspace.content.MetadataValue;
import org.dspace.content.Relationship;
import org.dspace.content.RelationshipType;
import org.dspace.content.WorkspaceItem;
@@ -78,6 +80,7 @@ import org.dspace.content.service.InstallItemService;
import org.dspace.content.service.ItemService;
import org.dspace.content.service.MetadataFieldService;
import org.dspace.content.service.MetadataSchemaService;
import org.dspace.content.service.MetadataValueService;
import org.dspace.content.service.RelationshipService;
import org.dspace.content.service.RelationshipTypeService;
import org.dspace.content.service.WorkspaceItemService;
@@ -158,6 +161,8 @@ public class ItemImportServiceImpl implements ItemImportService, InitializingBea
protected RelationshipService relationshipService;
@Autowired(required = true)
protected RelationshipTypeService relationshipTypeService;
@Autowired(required = true)
protected MetadataValueService metadataValueService;
protected String tempWorkDir;
@@ -168,6 +173,9 @@ public class ItemImportServiceImpl implements ItemImportService, InitializingBea
protected boolean isQuiet = false;
protected boolean processRelationships = false;
//remember which folder item was imported from
Map<String, Item> itemFolderMap = null;
@Override
public void afterPropertiesSet() throws Exception {
tempWorkDir = configurationService.getProperty("org.dspace.app.batchitemimport.work.dir");
@@ -224,7 +232,7 @@ public class ItemImportServiceImpl implements ItemImportService, InitializingBea
Map<String, String> skipItems = new HashMap<>(); // set of items to skip if in 'resume'
// mode
Map<String, Item> itemMap = new HashMap<>(); //remember which folder item was imported from
itemFolderMap = new HashMap<>();
System.out.println("Adding items from directory: " + sourceDir);
log.debug("Adding items from directory: " + sourceDir);
@@ -271,7 +279,7 @@ public class ItemImportServiceImpl implements ItemImportService, InitializingBea
//we still need the item in the map for relationship linking
String skippedHandle = skipItems.get(dircontents[i]);
Item skippedItem = (Item) handleService.resolveToObject(c, skippedHandle);
itemMap.put(dircontents[i], skippedItem);
itemFolderMap.put(dircontents[i], skippedItem);
}
} else {
@@ -297,7 +305,7 @@ public class ItemImportServiceImpl implements ItemImportService, InitializingBea
Item item = addItem(c, clist, sourceDir, dircontents[i], mapOut, template);
if (processRelationships) {
itemMap.put(dircontents[i], item);
itemFolderMap.put(dircontents[i], item);
}
c.uncacheEntity(item);
@@ -307,7 +315,7 @@ public class ItemImportServiceImpl implements ItemImportService, InitializingBea
if (processRelationships) {
//now that all items are imported, iterate again to link relationships
addRelationships(c, sourceDir, itemMap);
addRelationships(c, sourceDir);
}
} finally {
@@ -323,47 +331,75 @@ public class ItemImportServiceImpl implements ItemImportService, InitializingBea
*
* @param c Context
* @param sourceDir The parent import source directory
* @param itemMap Item imported in this batch, keyed by their import subfolder
* @throws Exception
*/
protected void addRelationships(Context c, String sourceDir, Map<String, Item> itemMap) throws Exception {
protected void addRelationships(Context c, String sourceDir) throws Exception {
System.out.println("Linking relationships");
for (Map.Entry<String, Item> itemEntry : itemMap.entrySet()) {
for (Map.Entry<String, Item> itemEntry : itemFolderMap.entrySet()) {
String folderName = itemEntry.getKey();
String path = sourceDir + File.separatorChar + folderName;
Item leftItem = itemEntry.getValue();
Item item = itemEntry.getValue();
System.out.println("Adding relationships from directory "+ folderName);
//look for a 'relationship' manifest
Map<Integer, List<String>> relationships = processRelationshipFile(path, "relationships");
Map<String, List<String>> relationships = processRelationshipFile(path, "relationships");
if (!relationships.isEmpty()) {
for (Map.Entry<Integer, List<String>> relEntry : relationships.entrySet()) {
for (Map.Entry<String, List<String>> relEntry : relationships.entrySet()) {
Integer relationshipTypeId = relEntry.getKey();
String relationshipType = relEntry.getKey();
List<String> identifierList = relEntry.getValue();
RelationshipType relationshipType = null;
try {
relationshipType = relationshipTypeService.find(c, relationshipTypeId.intValue());
} catch (Exception e) {
System.out.println("\tERROR: relationship type "+ relationshipTypeId +" not found.");
throw e;
}
for (String itemIdentifier : identifierList) {
Item rightItem = resolveRelatedItem(c, itemMap, itemIdentifier);
if (null == rightItem) {
throw new Exception("\tERROR: could not find item for "+ itemIdentifier);
//find referenced item
Item relationItem = resolveRelatedItem(c, itemIdentifier);
if (null == relationItem) {
throw new Exception("Could not find item for "+ itemIdentifier);
}
Relationship relationship = relationshipService.create(c, leftItem, rightItem, relationshipType, -1, -1);
System.out.println("\tAdded relationship (type: "+ relationshipTypeId +") to "+ rightItem.getHandle());
//get entity type of entity and item
String itemEntityType = getEntityType(item);
String relatedEntityType = getEntityType(relationItem);
//find matching relationship type
List<RelationshipType> relTypes = relationshipTypeService.findByLeftwardOrRightwardTypeName(c, relationshipType);
RelationshipType foundRelationshipType = RelationshipUtils.matchRelationshipType(relTypes, relatedEntityType, itemEntityType, relationshipType);
if (foundRelationshipType == null) {
throw new Exception("No Relationship type found for:\n" +
"Target type: " + relatedEntityType + "\n" +
"Origin referer type: " + itemEntityType + "\n" +
"with typeName: " + relationshipType
);
}
boolean left = false;
if (foundRelationshipType.getLeftwardType().equalsIgnoreCase(relationshipType)) {
left = true;
}
// Placeholder items for relation placing
Item leftItem = null;
Item rightItem = null;
if (left) {
leftItem = item;
rightItem = relationItem;
} else {
leftItem = relationItem;
rightItem = item;
}
// Create the relationship
int leftPlace = relationshipService.findNextLeftPlaceByLeftItem(c, leftItem);
int rightPlace = relationshipService.findNextRightPlaceByRightItem(c, rightItem);
Relationship persistedRelationship = relationshipService.create(c, leftItem, rightItem, foundRelationshipType, leftPlace, rightPlace);
relationshipService.update(c, persistedRelationship);
System.out.println("\tAdded relationship (type: "+ foundRelationshipType.getID() +") from "+ leftItem.getHandle() +" to "+ rightItem.getHandle());
}
@@ -375,12 +411,22 @@ public class ItemImportServiceImpl implements ItemImportService, InitializingBea
}
/**
* Get the item's entity type from meta.
*
* @param item
* @return
*/
protected String getEntityType(Item item) throws Exception {
return itemService.getMetadata(item, "dspace", "entity", "type", Item.ANY).get(0).getValue();
}
/**
* Read the relationship manifest file.
*
* Each line in the file contains a relationship type id and an item identifier in the following format:
*
* <relationship_type_id> <handle|uuid|import_item_folder>
* relation.<relation_key> <handle|uuid|folderName:import_item_folder|schema.element[.qualifier]:value>
*
* The input_item_folder should refer the folder name of another item in this import batch.
*
@@ -389,10 +435,10 @@ public class ItemImportServiceImpl implements ItemImportService, InitializingBea
* @return Map of found relationships
* @throws Exception
*/
protected Map<Integer, List<String>> processRelationshipFile(String path, String filename) throws Exception {
protected Map<String, List<String>> processRelationshipFile(String path, String filename) throws Exception {
File file = new File(path + File.separatorChar + filename);
Map<Integer, List<String>> result = new HashMap<>();
Map<String, List<String>> result = new HashMap<>();
if (file.exists()) {
@@ -408,33 +454,31 @@ public class ItemImportServiceImpl implements ItemImportService, InitializingBea
continue;
}
int relationshipTypeId;
String relationshipType = null;
String itemIdentifier = null;
//format: <relationship_id> <related_folder_name>
StringTokenizer st = new StringTokenizer(line);
if (st.hasMoreTokens()) {
try {
relationshipTypeId = Integer.valueOf(st.nextToken());
} catch (NumberFormatException e) {
throw new Exception("Bad mapfile line:\n" + line);
relationshipType= st.nextToken();
if (relationshipType.split("\\.").length > 1) {
relationshipType = relationshipType.split("\\.")[1];
}
} else {
throw new Exception("Bad mapfile line:\n" + line);
}
if (st.hasMoreTokens()) {
itemIdentifier = st.nextToken();
itemIdentifier = st.nextToken("").trim();
} else {
throw new Exception("Bad mapfile line:\n" + line);
}
if (!result.containsKey(relationshipTypeId)) {
result.put(relationshipTypeId, new ArrayList<>());
if (!result.containsKey(relationshipType)) {
result.put(relationshipType, new ArrayList<>());
}
result.get(relationshipTypeId).add(itemIdentifier);
result.get(relationshipType).add(itemIdentifier);
}
@@ -464,26 +508,81 @@ public class ItemImportServiceImpl implements ItemImportService, InitializingBea
* that was just imported. Next it will try to find the item by handle or UUID.
*
* @param c Context
* @param itemMap Item imported in this batch, keyed by their import subfolder
* @param itemIdentifier The identifier string found in the import manifest (handle, uuid, or another import subfolder)
* @return Item if found, or null.
* @throws Exception
*/
protected Item resolveRelatedItem(Context c, Map<String, Item> itemMap, String itemIdentifier) throws Exception {
protected Item resolveRelatedItem(Context c, String itemIdentifier) throws Exception {
Item item = null;
if (itemIdentifier.contains(":")) {
if (itemMap.containsKey(itemIdentifier)) {
//identifier refers to a folder name in this import batch
item = itemMap.get(itemIdentifier);
if (itemIdentifier.startsWith("folderName:") || itemIdentifier.startsWith("rowName:")) {
//identifier refers to a folder name in this import
int i = itemIdentifier.indexOf(":");
String folderName = itemIdentifier.substring(i + 1);
if (itemFolderMap.containsKey(folderName)) {
return itemFolderMap.get(folderName);
}
} else {
//lookup by meta value
int i = itemIdentifier.indexOf(":");
String metaKey = itemIdentifier.substring(0, i);
String metaValue = itemIdentifier.substring(i + 1);
return findItemByMetaValue(c, metaKey, metaValue);
}
} else if (itemIdentifier.indexOf('/') != -1) {
//resolve by handle
item = (Item) handleService.resolveToObject(c, itemIdentifier);
return (Item) handleService.resolveToObject(c, itemIdentifier);
} else {
//try to resolve by UUID
item = itemService.findByIdOrLegacyId(c, itemIdentifier);
return itemService.findByIdOrLegacyId(c, itemIdentifier);
}
return null;
}
/**
* Lookup an item by a (unique) meta value.
*
* @param metaKey
* @param metaValue
* @return Item
* @throws Exception if single item not found.
*/
protected Item findItemByMetaValue(Context c, String metaKey, String metaValue) throws Exception {
Item item = null;
String mf[] = metaKey.split("\\.");
if (mf.length < 2) {
throw new Exception("Bad metadata field in reference: '" + metaKey + "' (expected syntax is schema.element[.qualifier])");
}
String schema = mf[0];
String element = mf[1];
String qualifier = mf.length == 2 ? null : mf[2];
try {
MetadataField mfo = metadataFieldService.findByElement(c, schema, element, qualifier);
Iterator<MetadataValue> mdv = metadataValueService.findByFieldAndValue(c, mfo, metaValue);
if (mdv.hasNext()) {
MetadataValue mdvVal = mdv.next();
UUID uuid = mdvVal.getDSpaceObject().getID();
if (mdv.hasNext()) {
throw new Exception("Ambiguous reference; multiple matches in db: " + metaKey);
}
item = itemService.find(c, uuid);
}
} catch (SQLException e) {
throw new Exception("Error looking up item by metadata reference: " + metaKey, e);
}
if (item == null) {
throw new Exception("Item not found by metadata reference: " + metaKey);
}
return item;

View File

@@ -0,0 +1,50 @@
package org.dspace.app.util;
import java.util.List;
import org.dspace.content.RelationshipType;
public class RelationshipUtils {
/**
* Matches two Entity types to a Relationship Type from a set of Relationship Types.
*
* @param relTypes set of Relationship Types.
* @param targetType entity type of target.
* @param originType entity type of origin referer.
* @return null or matched Relationship Type.
*/
public static RelationshipType matchRelationshipType(List<RelationshipType> relTypes, String targetType, String originType, String originTypeName) {
RelationshipType foundRelationshipType = null;
if (originTypeName.split("\\.").length > 1) {
originTypeName = originTypeName.split("\\.")[1];
}
for (RelationshipType relationshipType : relTypes) {
// Is origin type leftward or righward
boolean isLeft = false;
if (relationshipType.getLeftType().getLabel().equalsIgnoreCase(originType)) {
isLeft = true;
}
if (isLeft) {
// Validate typeName reference
if (!relationshipType.getLeftwardType().equalsIgnoreCase(originTypeName)) {
continue;
}
if (relationshipType.getLeftType().getLabel().equalsIgnoreCase(originType) &&
relationshipType.getRightType().getLabel().equalsIgnoreCase(targetType)) {
foundRelationshipType = relationshipType;
}
} else {
if (!relationshipType.getRightwardType().equalsIgnoreCase(originTypeName)) {
continue;
}
if (relationshipType.getLeftType().getLabel().equalsIgnoreCase(targetType) &&
relationshipType.getRightType().getLabel().equalsIgnoreCase(originType)) {
foundRelationshipType = relationshipType;
}
}
}
return foundRelationshipType;
}
}