|
2 | 2 | /* Copyright Contributors to the ODPi Egeria project. */
|
3 | 3 | package org.odpi.egeria.connectors.juxt.crux.repositoryconnector;
|
4 | 4 |
|
| 5 | +import clojure.lang.IPersistentMap; |
5 | 6 | import crux.api.tx.Transaction;
|
6 | 7 | import org.odpi.egeria.connectors.juxt.crux.auditlog.CruxOMRSAuditCode;
|
7 | 8 | import org.odpi.egeria.connectors.juxt.crux.auditlog.CruxOMRSErrorCode;
|
8 |
| -import org.odpi.egeria.connectors.juxt.crux.mapping.Constants; |
| 9 | +import org.odpi.egeria.connectors.juxt.crux.mapping.EntitySummaryMapping; |
| 10 | +import org.odpi.egeria.connectors.juxt.crux.mapping.RelationshipMapping; |
| 11 | +import org.odpi.egeria.connectors.juxt.crux.model.search.CruxQuery; |
9 | 12 | import org.odpi.openmetadata.frameworks.auditlog.AuditLog;
|
10 | 13 | import org.odpi.openmetadata.repositoryservices.connectors.stores.metadatacollectionstore.OMRSDynamicTypeMetadataCollectionBase;
|
11 | 14 | import org.odpi.openmetadata.repositoryservices.connectors.stores.metadatacollectionstore.properties.HistorySequencingOrder;
|
@@ -149,8 +152,7 @@ public List<EntityDetail> getEntityDetailHistory(String userId,
|
149 | 152 | HistorySequencingOrder sequencingOrder) throws
|
150 | 153 | InvalidParameterException,
|
151 | 154 | RepositoryErrorException,
|
152 |
| - EntityNotKnownException, |
153 |
| - EntityProxyOnlyException { |
| 155 | + EntityNotKnownException { |
154 | 156 | final String methodName = "getEntityDetailHistory";
|
155 | 157 | super.getInstanceHistoryParameterValidation(userId, guid, fromTime, toTime, methodName);
|
156 | 158 | return cruxRepositoryConnector.getPreviousVersionsOfEntity(guid, fromTime, toTime, startFromElement, pageSize, sequencingOrder);
|
@@ -259,11 +261,6 @@ public List<EntityDetail> findEntities(String userId,
|
259 | 261 | PagingErrorException {
|
260 | 262 |
|
261 | 263 | this.findEntitiesParameterValidation(userId, entityTypeGUID, entitySubtypeGUIDs, matchProperties, fromEntityElement, limitResultsByStatus, matchClassifications, asOfTime, sequencingProperty, sequencingOrder, pageSize);
|
262 |
| - |
263 |
| - // TODO: rework this a bit, so that we retrieve the list of GUIDs and then loop through this |
264 |
| - // list making calls to getEntityDetail here in the MetadataCollection (?) (Then all of its additional |
265 |
| - // validations will be applied to each entity as well...) |
266 |
| - |
267 | 264 | return cruxRepositoryConnector.findEntities(entityTypeGUID,
|
268 | 265 | entitySubtypeGUIDs,
|
269 | 266 | matchProperties,
|
@@ -351,9 +348,7 @@ public List<EntityDetail> findEntitiesByPropertyValue(String userId,
|
351 | 348 | UserNotAuthorizedException {
|
352 | 349 |
|
353 | 350 | super.findEntitiesByPropertyValueParameterValidation(userId, entityTypeGUID, searchCriteria, fromEntityElement, limitResultsByStatus, limitResultsByClassification, asOfTime, sequencingProperty, sequencingOrder, pageSize);
|
354 |
| - |
355 | 351 | SearchClassifications searchClassifications = repositoryHelper.getSearchClassificationsFromList(limitResultsByClassification);
|
356 |
| - |
357 | 352 | return cruxRepositoryConnector.findEntitiesByText(entityTypeGUID,
|
358 | 353 | searchCriteria,
|
359 | 354 | fromEntityElement,
|
@@ -460,11 +455,6 @@ public List<Relationship> findRelationships(String userId,
|
460 | 455 | PagingErrorException {
|
461 | 456 |
|
462 | 457 | super.findRelationshipsParameterValidation(userId, relationshipTypeGUID, relationshipSubtypeGUIDs, matchProperties, fromRelationshipElement, limitResultsByStatus, asOfTime, sequencingProperty, sequencingOrder, pageSize);
|
463 |
| - |
464 |
| - // TODO: rework this a bit, so that we retrieve the list of GUIDs and then loop through this |
465 |
| - // list making calls to getEntityDetail here in the MetadataCollection (?) (Then all of its additional |
466 |
| - // validations will be applied to each entity as well...) |
467 |
| - |
468 | 458 | return cruxRepositoryConnector.findRelationships(relationshipTypeGUID,
|
469 | 459 | relationshipSubtypeGUIDs,
|
470 | 460 | matchProperties,
|
@@ -500,9 +490,7 @@ public List<Relationship> findRelationshipsByProperty(String userId,
|
500 | 490 | UserNotAuthorizedException {
|
501 | 491 |
|
502 | 492 | super.findRelationshipsByPropertyParameterValidation(userId, relationshipTypeGUID, matchProperties, matchCriteria, fromRelationshipElement, limitResultsByStatus, asOfTime, sequencingProperty, sequencingOrder, pageSize);
|
503 |
| - |
504 | 493 | SearchProperties searchProperties = repositoryHelper.getSearchPropertiesFromInstanceProperties(repositoryName, matchProperties, matchCriteria);
|
505 |
| - |
506 | 494 | return findRelationships(userId,
|
507 | 495 | relationshipTypeGUID,
|
508 | 496 | null,
|
@@ -537,7 +525,6 @@ public List<Relationship> findRelationshipsByPropertyValue(String userId,
|
537 | 525 | UserNotAuthorizedException {
|
538 | 526 |
|
539 | 527 | super.findRelationshipsByPropertyValueParameterValidation(userId, relationshipTypeGUID, searchCriteria, fromRelationshipElement, limitResultsByStatus, asOfTime, sequencingProperty, sequencingOrder, pageSize);
|
540 |
| - |
541 | 528 | return cruxRepositoryConnector.findRelationshipsByText(relationshipTypeGUID,
|
542 | 529 | searchCriteria,
|
543 | 530 | fromRelationshipElement,
|
@@ -747,15 +734,13 @@ public EntityDetail addEntity(String userId,
|
747 | 734 | // (If not, then why not, and where should this validation be done instead?)
|
748 | 735 |
|
749 | 736 | TypeDef typeDef = super.addEntityParameterValidation(userId, entityTypeGUID, initialProperties, initialClassifications, initialStatus, methodName);
|
750 |
| - |
751 | 737 | EntityDetail newEntity = repositoryHelper.getNewEntity(repositoryName,
|
752 | 738 | metadataCollectionId,
|
753 | 739 | InstanceProvenanceType.LOCAL_COHORT,
|
754 | 740 | userId,
|
755 | 741 | typeDef.getName(),
|
756 | 742 | initialProperties,
|
757 | 743 | initialClassifications);
|
758 |
| - |
759 | 744 | newEntity.setMetadataCollectionName(metadataCollectionName);
|
760 | 745 | // TODO: should we not add the calling user to the 'maintainedBy' list?
|
761 | 746 |
|
@@ -789,15 +774,13 @@ public EntityDetail addExternalEntity(String userId,
|
789 | 774 | final String methodName = "addExternalEntity";
|
790 | 775 |
|
791 | 776 | TypeDef typeDef = super.addExternalEntityParameterValidation(userId, entityTypeGUID, externalSourceGUID, initialProperties, initialClassifications, initialStatus, methodName);
|
792 |
| - |
793 | 777 | EntityDetail newEntity = repositoryHelper.getNewEntity(repositoryName,
|
794 | 778 | externalSourceGUID,
|
795 | 779 | InstanceProvenanceType.EXTERNAL_SOURCE,
|
796 | 780 | userId,
|
797 | 781 | typeDef.getName(),
|
798 | 782 | initialProperties,
|
799 | 783 | initialClassifications);
|
800 |
| - |
801 | 784 | newEntity.setMetadataCollectionName(externalSourceName);
|
802 | 785 | newEntity.setReplicatedBy(metadataCollectionId);
|
803 | 786 |
|
@@ -966,40 +949,36 @@ public EntityDetail deleteEntity(String userId,
|
966 | 949 | repositoryValidator.validateTypeForInstanceDelete(repositoryName, typeDefGUID, typeDefName, entity, methodName);
|
967 | 950 | repositoryValidator.validateInstanceStatusForDelete(repositoryName, entity, methodName);
|
968 | 951 |
|
| 952 | + Transaction.Builder tx = Transaction.builder(); |
| 953 | + |
| 954 | + // 1. Soft-delete every HOMED relationship in which the entity is involved (note that we are only allowed to |
| 955 | + // do this against relationships that are homed in this repository, as a soft-delete can only be done by the |
| 956 | + // home repository: it changes the version, modification times, etc) |
969 | 957 | try {
|
970 |
| - // TODO: optimise this query since we do not really need the entire relationship to delete it? |
971 |
| - List<Relationship> relationships = this.getRelationshipsForEntity(userId, |
972 |
| - obsoleteEntityGUID, |
973 |
| - null, |
974 |
| - 0, |
975 |
| - null, |
976 |
| - null, |
977 |
| - null, |
978 |
| - null, |
979 |
| - Constants.CASCADE_DELETES_PAGE_SIZE); |
| 958 | + List<Relationship> relationships = cruxRepositoryConnector.findHomedRelationshipsForEntity(entity, userId); |
980 | 959 | if (relationships != null) {
|
981 | 960 | for (Relationship relationship : relationships) {
|
982 | 961 | if (relationship != null) {
|
983 |
| - InstanceType type = relationship.getType(); |
984 |
| - if (type != null) { |
985 |
| - this.deleteRelationship(userId, |
986 |
| - type.getTypeDefGUID(), |
987 |
| - type.getTypeDefName(), |
988 |
| - relationship.getGUID()); |
989 |
| - } |
| 962 | + Relationship toDelete = getDeletedRelationshipRepresentation(relationship, userId); |
| 963 | + cruxRepositoryConnector.addUpdateRelationshipStatements(tx, toDelete); |
990 | 964 | }
|
991 | 965 | }
|
992 | 966 | }
|
993 | 967 | } catch (Exception e) {
|
994 | 968 | auditLog.logException(methodName, CruxOMRSAuditCode.FAILED_RELATIONSHIP_DELETE_CASCADE.getMessageDefinition(obsoleteEntityGUID), e);
|
995 | 969 | }
|
996 | 970 |
|
| 971 | + // 2. Update the entity itself |
997 | 972 | EntityDetail updatedEntity = new EntityDetail(entity);
|
998 | 973 | updatedEntity.setStatusOnDelete(entity.getStatus());
|
999 | 974 | updatedEntity.setStatus(InstanceStatus.DELETED);
|
1000 | 975 | updatedEntity = repositoryHelper.incrementVersion(userId, entity, updatedEntity);
|
| 976 | + cruxRepositoryConnector.addUpdateEntityStatements(tx, updatedEntity); |
1001 | 977 |
|
1002 |
| - return cruxRepositoryConnector.updateEntity(updatedEntity); |
| 978 | + // 3. Commit the transaction containing all of these write operations together |
| 979 | + cruxRepositoryConnector.runTx(tx.build()); |
| 980 | + |
| 981 | + return updatedEntity; |
1003 | 982 |
|
1004 | 983 | }
|
1005 | 984 |
|
@@ -1033,37 +1012,42 @@ public void purgeEntity(String userId,
|
1033 | 1012 | repositoryValidator.validateTypeForInstanceDelete(repositoryName, typeDefGUID, typeDefName, entity, methodName);
|
1034 | 1013 | repositoryValidator.validateEntityIsDeleted(repositoryName, entity, methodName);
|
1035 | 1014 |
|
1036 |
| - // TODO: the InMemoryOMRSMetadataCollection makes this call and catches all exceptions (with no logging), but |
1037 |
| - // actually the call itself will ALWAYS give an EntityNotKnownException because we are validating just above |
1038 |
| - // that this entity is deleted, while the getRelationshipsForEntity call is validating that the entity is NOT |
1039 |
| - // deleted (and if it is, will throw an EntityNotKnownException). So it ALWAYS throws an EntityNotKnownException. |
1040 |
| - // We can therefore either remove this code entirely (as it does nothing but add Exception processing overhead), |
1041 |
| - // or we need to change the logic across the various repositories. |
1042 |
| - /*try { |
1043 |
| - List<Relationship> relationships = this.getRelationshipsForEntity(userId, |
1044 |
| - deletedEntityGUID, |
| 1015 | + Transaction.Builder tx = Transaction.builder(); |
| 1016 | + |
| 1017 | + // 1. Purge EVERY SINGLE relationship in which the entity is involved (note that we should be able to do this |
| 1018 | + // against both homed and reference copy relationships since purge operations are allowed against both) |
| 1019 | + try { |
| 1020 | + CruxQuery query = new CruxQuery(); |
| 1021 | + query.addRelationshipEndpointConditions(EntitySummaryMapping.getReference(deletedEntityGUID)); |
| 1022 | + cruxRepositoryConnector.updateQuery(query, |
| 1023 | + TypeDefCategory.RELATIONSHIP_DEF, |
1045 | 1024 | null,
|
1046 |
| - 0, |
1047 | 1025 | null,
|
1048 | 1026 | null,
|
1049 | 1027 | null,
|
1050 | 1028 | null,
|
1051 |
| - Constants.CASCADE_DELETES_PAGE_SIZE); |
1052 |
| - if (relationships != null) { |
1053 |
| - for (Relationship relationship : relationships) { |
1054 |
| - if (relationship != null) { |
1055 |
| - // Note: we will not call this.purgeRelationship() directly to avoid re-retrieving all of these |
1056 |
| - // relationships one-by-one prior to purging |
1057 |
| - cruxRepositoryConnector.purgeRelationship(relationship.getGUID()); |
1058 |
| - } |
1059 |
| - } |
| 1029 | + null, |
| 1030 | + null, |
| 1031 | + null, |
| 1032 | + userId); |
| 1033 | + IPersistentMap q = query.getQuery(); |
| 1034 | + log.debug("Querying with: {}", q); |
| 1035 | + Collection<List<?>> results = cruxRepositoryConnector.getCruxAPI().db().query(q); |
| 1036 | + Collection<List<?>> relationshipRefs = cruxRepositoryConnector.deduplicate(results); |
| 1037 | + for (List<?> relationshipRef : relationshipRefs) { |
| 1038 | + String docRef = (String) relationshipRef.get(0); |
| 1039 | + String guid = RelationshipMapping.trimGuidFromReference(docRef); |
| 1040 | + cruxRepositoryConnector.addPurgeRelationshipStatements(tx, guid); |
1060 | 1041 | }
|
1061 | 1042 | } catch (Exception e) {
|
1062 |
| - log.error("Exception was thrown in purgeEntity.", e); |
1063 | 1043 | auditLog.logException(methodName, CruxOMRSAuditCode.FAILED_RELATIONSHIP_DELETE_CASCADE.getMessageDefinition(deletedEntityGUID), e);
|
1064 |
| - }*/ |
| 1044 | + } |
| 1045 | + |
| 1046 | + // 2. Purge the entity itself |
| 1047 | + cruxRepositoryConnector.addPurgeEntityStatements(tx, entity.getGUID()); |
1065 | 1048 |
|
1066 |
| - cruxRepositoryConnector.purgeEntity(entity.getGUID()); |
| 1049 | + // 3. Commit the transaction containing all of these write operations together |
| 1050 | + cruxRepositoryConnector.runTx(tx.build()); |
1067 | 1051 |
|
1068 | 1052 | }
|
1069 | 1053 |
|
@@ -1501,18 +1485,18 @@ public Relationship deleteRelationship(String userId,
|
1501 | 1485 | final String parameterName = "obsoleteRelationshipGUID";
|
1502 | 1486 |
|
1503 | 1487 | this.manageInstanceParameterValidation(userId, typeDefGUID, typeDefName, obsoleteRelationshipGUID, parameterName, methodName);
|
1504 |
| - |
1505 | 1488 | Relationship relationship = this.getRelationship(userId, obsoleteRelationshipGUID);
|
1506 |
| - |
1507 | 1489 | repositoryValidator.validateTypeForInstanceDelete(repositoryName, typeDefGUID, typeDefName, relationship, methodName);
|
| 1490 | + Relationship updatedRelationship = getDeletedRelationshipRepresentation(relationship, userId); |
| 1491 | + return cruxRepositoryConnector.updateRelationship(updatedRelationship); |
1508 | 1492 |
|
| 1493 | + } |
| 1494 | + |
| 1495 | + private Relationship getDeletedRelationshipRepresentation(Relationship relationship, String userId) { |
1509 | 1496 | Relationship updatedRelationship = new Relationship(relationship);
|
1510 | 1497 | updatedRelationship.setStatusOnDelete(relationship.getStatus());
|
1511 | 1498 | updatedRelationship.setStatus(InstanceStatus.DELETED);
|
1512 |
| - updatedRelationship = repositoryHelper.incrementVersion(userId, relationship, updatedRelationship); |
1513 |
| - |
1514 |
| - return cruxRepositoryConnector.updateRelationship(updatedRelationship); |
1515 |
| - |
| 1499 | + return repositoryHelper.incrementVersion(userId, relationship, updatedRelationship); |
1516 | 1500 | }
|
1517 | 1501 |
|
1518 | 1502 | /**
|
@@ -2125,7 +2109,6 @@ public void saveRelationshipReferenceCopy(String userId,
|
2125 | 2109 | }
|
2126 | 2110 |
|
2127 | 2111 | // Only create entity proxies if the above retrievals indicated that they do not yet exist
|
2128 |
| - // TODO: there may be a more optimal way of achieving this directly through the Crux statements -- future optimisation? |
2129 | 2112 | Transaction.Builder tx = Transaction.builder();
|
2130 | 2113 | if (one == null) {
|
2131 | 2114 | cruxRepositoryConnector.addCreateEntityProxyStatements(tx, relationship.getEntityOneProxy());
|
|
0 commit comments