From 26b7c3f39c98755fb8fcbcb4e5621c6f4a9cbcb0 Mon Sep 17 00:00:00 2001 From: mateusmolina Date: Thu, 23 Jan 2025 09:23:44 +0100 Subject: [PATCH] feat: implement SmRef methods of the MongoDB AasServiceBackend --- ...ctedAasManagerMultithreadingTestSuite.java | 5 +- .../mongodb/AasServiceBackendImpl.java | 98 +++++++++++++++++-- 2 files changed, 93 insertions(+), 10 deletions(-) diff --git a/basyx.aasenvironment/basyx.aasenvironment-client/src/test/java/org/eclipse/digitaltwin/basyx/aasenvironment/client/ConnectedAasManagerMultithreadingTestSuite.java b/basyx.aasenvironment/basyx.aasenvironment-client/src/test/java/org/eclipse/digitaltwin/basyx/aasenvironment/client/ConnectedAasManagerMultithreadingTestSuite.java index 8ff6b6948..4f8f5f0fb 100644 --- a/basyx.aasenvironment/basyx.aasenvironment-client/src/test/java/org/eclipse/digitaltwin/basyx/aasenvironment/client/ConnectedAasManagerMultithreadingTestSuite.java +++ b/basyx.aasenvironment/basyx.aasenvironment-client/src/test/java/org/eclipse/digitaltwin/basyx/aasenvironment/client/ConnectedAasManagerMultithreadingTestSuite.java @@ -74,8 +74,9 @@ public void testParallelSubmodelCreation() throws ExecutionException, Interrupte } void assertSubmodelWasCreatedAndRegistered(String shellId, String submodelId) { - assertEquals(submodelId, getConnectedAasManager().getSubmodelService(submodelId).getSubmodel().getId()); - assertTrue(getAasRepository().getSubmodelReferences(shellId, PaginationInfo.NO_LIMIT).getResult().stream().map(Reference::getKeys).flatMap(Collection::stream).map(Key::getValue).anyMatch(submodelId::equals)); + assertEquals("No submodel with id " + submodelId + " found by the client", submodelId, getConnectedAasManager().getSubmodelService(submodelId).getSubmodel().getId()); + assertTrue("SubmodelRef " + submodelId + " not found in shell " + shellId, + getAasRepository().getSubmodelReferences(shellId, PaginationInfo.NO_LIMIT).getResult().stream().map(Reference::getKeys).flatMap(Collection::stream).map(Key::getValue).anyMatch(submodelId::equals)); } private AssetAdministrationShell createShell() { diff --git a/basyx.aasrepository/basyx.aasrepository-backend-mongodb/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/backend/mongodb/AasServiceBackendImpl.java b/basyx.aasrepository/basyx.aasrepository-backend-mongodb/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/backend/mongodb/AasServiceBackendImpl.java index e434868ae..762d09ffe 100644 --- a/basyx.aasrepository/basyx.aasrepository-backend-mongodb/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/backend/mongodb/AasServiceBackendImpl.java +++ b/basyx.aasrepository/basyx.aasrepository-backend-mongodb/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/backend/mongodb/AasServiceBackendImpl.java @@ -2,39 +2,108 @@ import java.io.File; import java.io.InputStream; +import java.util.ArrayList; import java.util.List; +import org.eclipse.digitaltwin.aas4j.v3.model.AssetAdministrationShell; import org.eclipse.digitaltwin.aas4j.v3.model.AssetInformation; +import org.eclipse.digitaltwin.aas4j.v3.model.Key; +import org.eclipse.digitaltwin.aas4j.v3.model.KeyTypes; import org.eclipse.digitaltwin.aas4j.v3.model.Reference; +import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultReference; import org.eclipse.digitaltwin.basyx.aasrepository.backend.AasServiceBackend; import org.eclipse.digitaltwin.basyx.core.pagination.CursorResult; import org.eclipse.digitaltwin.basyx.core.pagination.PaginationInfo; import org.springframework.data.mongodb.core.MongoOperations; +import org.springframework.data.mongodb.core.aggregation.Aggregation; +import org.springframework.data.mongodb.core.aggregation.AggregationOperation; +import org.springframework.data.mongodb.core.aggregation.AggregationResults; +import org.springframework.data.mongodb.core.aggregation.LimitOperation; +import org.springframework.data.mongodb.core.aggregation.MatchOperation; +import org.springframework.data.mongodb.core.aggregation.ProjectionOperation; +import org.springframework.data.mongodb.core.aggregation.UnwindOperation; +import org.springframework.data.mongodb.core.query.Criteria; +import org.springframework.data.mongodb.core.query.Query; +import org.springframework.data.mongodb.core.query.Update; +import org.springframework.data.mongodb.repository.support.MappingMongoEntityInformation; +import com.mongodb.client.result.UpdateResult; + +/** + * MongoDB implementation of the {@link AasServiceBackend} + * + * @author mateusmolina + */ public class AasServiceBackendImpl implements AasServiceBackend { + private static final String SMREF_KEY = "submodels"; + private final MongoOperations mongoOperations; + private final String collectionName; - public AasServiceBackendImpl(MongoOperations mongoOperations) { + public AasServiceBackendImpl(MongoOperations mongoOperations, MappingMongoEntityInformation mappingMongoEntityInformation) { this.mongoOperations = mongoOperations; - } + collectionName = mappingMongoEntityInformation.getCollectionName(); + + } @Override public CursorResult> getSubmodelReferences(String aasId, PaginationInfo pInfo) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getSubmodelReferences'"); + Integer limit = pInfo.getLimit(); + String cursor = pInfo.getCursor(); + + MatchOperation matchAasId = Aggregation.match(Criteria.where("_id").is(aasId)); + + UnwindOperation unwindSubmodels = Aggregation.unwind(SMREF_KEY); + + ProjectionOperation projectReference = Aggregation.project().and("submodels.keys").as("keys").and("submodels.type").as("type"); + + Aggregation aggregation; + List aggregationOps = new ArrayList<>(); + aggregationOps.add(matchAasId); + aggregationOps.add(unwindSubmodels); + aggregationOps.add(projectReference); + + if (cursor != null && !cursor.isEmpty()) { + MatchOperation matchCursor = Aggregation.match(Criteria.where("keys.value").gt(cursor)); + aggregationOps.add(matchCursor); + } + + if (limit != null && limit > 0) { + LimitOperation limitOperation = Aggregation.limit(limit); + aggregationOps.add(limitOperation); + } + + aggregation = Aggregation.newAggregation(aggregationOps); + AggregationResults results = mongoOperations.aggregate(aggregation, collectionName, DefaultReference.class); + List submodelReferences = results.getMappedResults(); + + String nextCursor = null; + if (!submodelReferences.isEmpty()) { + Reference lastReference = submodelReferences.get(submodelReferences.size() - 1); + nextCursor = extractSubmodelId(lastReference); + } + + return new CursorResult<>(nextCursor, new ArrayList<>(submodelReferences)); } @Override public void addSubmodelReference(String aasId, Reference submodelReference) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'addSubmodelReference'"); + + Query query = new Query(Criteria.where("_id").is(aasId)); + + Update update = new Update().push(SMREF_KEY, submodelReference); + + UpdateResult result = mongoOperations.updateFirst(query, update, collectionName); } @Override public void removeSubmodelReference(String aasId, String submodelId) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'removeSubmodelReference'"); + Query query = new Query(Criteria.where("_id").is(aasId)); + + Update update = new Update().pull(SMREF_KEY, Query.query(Criteria.where("keys.value").is(submodelId)).getQueryObject()); + + UpdateResult result = mongoOperations.updateFirst(query, update, collectionName); } @Override @@ -49,6 +118,7 @@ public AssetInformation getAssetInformation(String aasId) { throw new UnsupportedOperationException("Unimplemented method 'getAssetInformation'"); } + @Override public File getThumbnail(String aasId) { // TODO Auto-generated method stub @@ -67,4 +137,16 @@ public void deleteThumbnail(String aasId) { throw new UnsupportedOperationException("Unimplemented method 'deleteThumbnail'"); } + private static String extractSubmodelId(Reference reference) { + List keys = reference.getKeys(); + + for (Key key : keys) { + if (key.getType() == KeyTypes.SUBMODEL) { + return key.getValue(); + } + } + + return ""; + } + }