Merge "Refactored Data Job interfaces"
diff --git a/cps-application/src/main/resources/application.yml b/cps-application/src/main/resources/application.yml
index 83494d6..f61a09b 100644
--- a/cps-application/src/main/resources/application.yml
+++ b/cps-application/src/main/resources/application.yml
@@ -215,7 +215,7 @@
         advised-modules-sync:
             sleep-time-ms: 5000
         locked-modules-sync:
-            sleep-time-ms: 300000
+            sleep-time-ms: 60000
         cm-handle-data-sync:
             sleep-time-ms: 30000
         subscription-forwarding:
diff --git a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/NetworkCmProxyController.java b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/NetworkCmProxyController.java
index a482cf5..af5f226 100755
--- a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/NetworkCmProxyController.java
+++ b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/NetworkCmProxyController.java
@@ -86,7 +86,7 @@
      * Get resource data from datastore.
      *
      * @param datastoreName        name of the datastore
-     * @param cmHandle             cm handle identifier
+     * @param cmHandleReference    cm handle or alternate id identifier
      * @param resourceIdentifier   resource identifier
      * @param optionsParamInQuery  options query parameter
      * @param topicParamInQuery    topic query parameter
@@ -97,15 +97,16 @@
     @Override
     @Timed(value = "cps.ncmp.controller.get", description = "Time taken to get resource data from datastore")
     public ResponseEntity<Object> getResourceDataForCmHandle(final String datastoreName,
-                                                             final String cmHandle,
+                                                             final String cmHandleReference,
                                                              final String resourceIdentifier,
                                                              final String optionsParamInQuery,
                                                              final String topicParamInQuery,
                                                              final Boolean includeDescendants,
                                                              final String authorization) {
-        final CmResourceAddress cmResourceAddress = new CmResourceAddress(datastoreName, cmHandle, resourceIdentifier);
+        final CmResourceAddress cmResourceAddress = new CmResourceAddress(datastoreName, cmHandleReference,
+            resourceIdentifier);
         final Object result = networkCmProxyFacade.getResourceDataForCmHandle(cmResourceAddress, optionsParamInQuery,
-                                                               topicParamInQuery, includeDescendants, authorization);
+            topicParamInQuery, includeDescendants, authorization);
         return ResponseEntity.ok(result);
     }
 
diff --git a/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyControllerSpec.groovy b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyControllerSpec.groovy
index f7d80ad..9f5331d 100644
--- a/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyControllerSpec.groovy
+++ b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyControllerSpec.groovy
@@ -41,6 +41,7 @@
 import org.onap.cps.ncmp.impl.inventory.DataStoreSyncState
 import org.onap.cps.ncmp.impl.inventory.models.CmHandleState
 import org.onap.cps.ncmp.impl.inventory.models.LockReasonCategory
+import org.onap.cps.ncmp.impl.utils.AlternateIdMatcher
 import org.onap.cps.ncmp.rest.model.DataOperationDefinition
 import org.onap.cps.ncmp.rest.model.DataOperationRequest
 import org.onap.cps.ncmp.rest.util.CmHandleStateMapper
@@ -94,6 +95,9 @@
     NetworkCmProxyInventoryFacade mockNetworkCmProxyInventoryFacade = Mock()
 
     @SpringBean
+    AlternateIdMatcher mockalternateIdMatcher = Mock()
+
+    @SpringBean
     ObjectMapper objectMapper = new ObjectMapper()
 
     @SpringBean
@@ -136,12 +140,10 @@
     def 'Get Resource Data from pass-through operational.'() {
         given: 'resource data url'
             def getUrl = "$ncmpBasePathV1/ch/testCmHandle/data/ds/ncmp-datastore:passthrough-operational?resourceIdentifier=parent/child&options=(a=1,b=2)"
-        and: 'the expected cm resource address'
-            def expectedCmResourceAddress = new CmResourceAddress(PASSTHROUGH_OPERATIONAL.datastoreName, 'testCmHandle', 'parent/child')
         when: 'get data resource request is performed'
             def response = mvc.perform(get(getUrl).contentType(MediaType.APPLICATION_JSON)).andReturn().response
         then: 'the NCMP data service is called with correct parameters'
-            1 * mockNetworkCmProxyFacade.getResourceDataForCmHandle(expectedCmResourceAddress, '(a=1,b=2)', NO_TOPIC, false, NO_AUTH_HEADER) >> Mono.just(new ResponseEntity<Object>(HttpStatus.OK))
+            1 * mockNetworkCmProxyFacade.getResourceDataForCmHandle(_, '(a=1,b=2)', NO_TOPIC, false, NO_AUTH_HEADER) >> Mono.just(new ResponseEntity<Object>(HttpStatus.OK))
         and: 'response status is Ok'
             assert response.status == HttpStatus.OK.value()
     }
@@ -150,11 +152,10 @@
         given: 'resource data url'
             def getUrl = "$ncmpBasePathV1/ch/h123/data/ds/ncmp-datastore:operational?resourceIdentifier=parent/child${additionalUrlParam}"
         and: 'the expected cm resource address'
-            def expectedCmResourceAddress = new CmResourceAddress('ncmp-datastore:operational', 'h123', 'parent/child')
         when: 'get data resource request is performed'
             def response = mvc.perform(get(getUrl).contentType(MediaType.APPLICATION_JSON)).andReturn().response
         then: 'the NCMP data service is called with correct parameters'
-            1 * mockNetworkCmProxyFacade.getResourceDataForCmHandle(expectedCmResourceAddress, NO_OPTIONS, NO_TOPIC, expectedIncludeDescendants, NO_AUTH_HEADER)
+            1 * mockNetworkCmProxyFacade.getResourceDataForCmHandle(_, NO_OPTIONS, NO_TOPIC, expectedIncludeDescendants, NO_AUTH_HEADER)
         and: 'response status is OK'
             assert response.status == HttpStatus.OK.value()
         where: 'the following parameters are used'
@@ -206,8 +207,7 @@
         given: 'resource data url'
             def getUrl = "$ncmpBasePathV1/ch/ch-1/data/ds/ncmp-datastore:passthrough-running?resourceIdentifier=$resourceIdentifier&options=(a=1)"
         and: 'ncmp service returns json object'
-            def expectedCmResourceAddress = new CmResourceAddress(PASSTHROUGH_RUNNING.datastoreName, 'ch-1', resourceIdentifier)
-            1 * mockNetworkCmProxyFacade.getResourceDataForCmHandle(expectedCmResourceAddress, '(a=1)', NO_TOPIC, false, NO_AUTH_HEADER)
+            1 * mockNetworkCmProxyFacade.getResourceDataForCmHandle(_, '(a=1)', NO_TOPIC, false, NO_AUTH_HEADER)
                     >> new ResponseEntity<Object>('{valid-json}', HttpStatus.OK)
         when: 'get data resource request is performed'
             def response = mvc.perform(get(getUrl).contentType(MediaType.APPLICATION_JSON)).andReturn().response
diff --git a/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyRestExceptionHandlerSpec.groovy b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyRestExceptionHandlerSpec.groovy
index 97f3e03..e6288ff 100644
--- a/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyRestExceptionHandlerSpec.groovy
+++ b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyRestExceptionHandlerSpec.groovy
@@ -34,6 +34,7 @@
 import org.onap.cps.ncmp.impl.data.NcmpCachedResourceRequestHandler
 import org.onap.cps.ncmp.impl.data.NcmpPassthroughResourceRequestHandler
 import org.onap.cps.ncmp.impl.data.NetworkCmProxyFacade
+import org.onap.cps.ncmp.impl.inventory.InventoryPersistence
 import org.onap.cps.ncmp.rest.util.CmHandleStateMapper
 import org.onap.cps.ncmp.rest.util.DataOperationRequestMapper
 import org.onap.cps.ncmp.rest.util.DeprecationHelper
@@ -77,6 +78,9 @@
     NetworkCmProxyInventoryFacade mockNetworkCmProxyInventoryFacade = Mock()
 
     @SpringBean
+    InventoryPersistence mockInventoryPersistence = Mock()
+
+    @SpringBean
     JsonObjectMapper stubbedJsonObjectMapper = Stub()
 
     @SpringBean
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/data/models/CmResourceAddress.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/data/models/CmResourceAddress.java
index e93aa4c..98a343b 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/data/models/CmResourceAddress.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/data/models/CmResourceAddress.java
@@ -20,6 +20,22 @@
 
 package org.onap.cps.ncmp.api.data.models;
 
-public record CmResourceAddress(String datastoreName, String cmHandleId, String resourceIdentifier) {
+import lombok.AccessLevel;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+import org.onap.cps.ncmp.config.CpsApplicationContext;
+import org.onap.cps.ncmp.impl.utils.AlternateIdMatcher;
 
+@Getter
+@RequiredArgsConstructor
+public class CmResourceAddress {
+
+    private final String datastoreName;
+    @Getter(AccessLevel.NONE)
+    private final String cmHandleReference;
+    private final String resourceIdentifier;
+
+    public String getResolvedCmHandleId() {
+        return CpsApplicationContext.getCpsBean(AlternateIdMatcher.class).getCmHandleId(cmHandleReference);
+    }
 }
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/DmiDataOperations.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/DmiDataOperations.java
index 4cbf9d4..90783a8 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/DmiDataOperations.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/DmiDataOperations.java
@@ -92,12 +92,12 @@
                                                                final String topic,
                                                                final String requestId,
                                                                final String authorization) {
-        final YangModelCmHandle yangModelCmHandle = getYangModelCmHandle(cmResourceAddress.cmHandleId());
+        final YangModelCmHandle yangModelCmHandle = getYangModelCmHandle(cmResourceAddress.getResolvedCmHandleId());
         final CmHandleState cmHandleState = yangModelCmHandle.getCompositeState().getCmHandleState();
         validateIfCmHandleStateReady(yangModelCmHandle, cmHandleState);
         final String jsonRequestBody = getDmiRequestBody(READ, requestId, null, null, yangModelCmHandle);
         final UrlTemplateParameters urlTemplateParameters = getUrlTemplateParameters(cmResourceAddress
-                .datastoreName(), yangModelCmHandle, cmResourceAddress.resourceIdentifier(), options, topic);
+                .getDatastoreName(), yangModelCmHandle, cmResourceAddress.getResourceIdentifier(), options, topic);
         return dmiRestClient.asynchronousPostOperationWithJsonData(DATA, urlTemplateParameters, jsonRequestBody, READ,
                 authorization);
     }
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/NcmpCachedResourceRequestHandler.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/NcmpCachedResourceRequestHandler.java
index bff2f63..01022cc 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/NcmpCachedResourceRequestHandler.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/NcmpCachedResourceRequestHandler.java
@@ -61,9 +61,9 @@
                                                       final String authorization) {
         final FetchDescendantsOption fetchDescendantsOption = getFetchDescendantsOption(includeDescendants);
 
-        final DataNode dataNode = cpsDataService.getDataNodes(cmResourceAddress.datastoreName(),
-            cmResourceAddress.cmHandleId(),
-            cmResourceAddress.resourceIdentifier(),
+        final DataNode dataNode = cpsDataService.getDataNodes(cmResourceAddress.getDatastoreName(),
+            cmResourceAddress.getResolvedCmHandleId(),
+            cmResourceAddress.getResourceIdentifier(),
             fetchDescendantsOption).iterator().next();
         return Mono.justOrEmpty(dataNode);
     }
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/NetworkCmProxyFacade.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/NetworkCmProxyFacade.java
index 5039157..b97088a 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/NetworkCmProxyFacade.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/NetworkCmProxyFacade.java
@@ -33,6 +33,7 @@
 import org.onap.cps.ncmp.api.data.models.DataOperationRequest;
 import org.onap.cps.ncmp.api.data.models.DatastoreType;
 import org.onap.cps.ncmp.api.data.models.OperationType;
+import org.onap.cps.ncmp.impl.utils.AlternateIdMatcher;
 import org.onap.cps.spi.model.DataNode;
 import org.springframework.stereotype.Service;
 
@@ -44,34 +45,35 @@
     private final NcmpCachedResourceRequestHandler ncmpCachedResourceRequestHandler;
     private final NcmpPassthroughResourceRequestHandler ncmpPassthroughResourceRequestHandler;
     private final DmiDataOperations dmiDataOperations;
+    private final AlternateIdMatcher alternateIdMatcher;
 
     /**
      * Fetches resource data for a given data store using DMI (Data Management Interface).
      * This method retrieves data based on the provided CmResourceAddress and additional query parameters.
      * It supports asynchronous processing and handles authorization if required.
      *
-     * @param cmResourceAddress  The target data store, including the CM handle and resource identifier.
-     *                           This parameter must not be null.
-     * @param options            Additional query parameters that may influence the data retrieval process,
-     *                           such as filters or limits. This parameter can be null.
-     * @param topic              The topic name for triggering asynchronous responses. If specified,
-     *                           the response will be sent to this topic. This parameter can be null.
-     * @param includeDescendants include (all) descendants or not
-     * @param authorization      The contents of the Authorization header. This parameter can be null
-     *                           if authorization is not required.
+     * @param cmResourceAddress     The target data store, including the CM handle and resource identifier.
+     *                              This parameter must not be null.
+     * @param optionsParamInQuery   Additional query parameters that may influence the data retrieval process,
+     *                              such as filters or limits. This parameter can be null.
+     * @param topicParamInQuery     The topic name for triggering asynchronous responses. If specified,
+     *                              the response will be sent to this topic. This parameter can be null.
+     * @param includeDescendants    include (all) descendants or not
+     * @param authorization         The contents of the Authorization header. This parameter can be null
+     *                              if authorization is not required.
      * @return the result object, depends on use op topic. With topic a map object with request id is returned
      *         otherwise the result of the request.
      */
     public Object getResourceDataForCmHandle(final CmResourceAddress cmResourceAddress,
-                                             final String options,
-                                             final String topic,
+                                             final String optionsParamInQuery,
+                                             final String topicParamInQuery,
                                              final Boolean includeDescendants,
                                              final String authorization) {
-        final NcmpDatastoreRequestHandler ncmpDatastoreRequestHandler
-            = getNcmpDatastoreRequestHandler(cmResourceAddress.datastoreName());
 
-        return ncmpDatastoreRequestHandler.executeRequest(cmResourceAddress, options, topic, includeDescendants,
-                                                          authorization);
+        final NcmpDatastoreRequestHandler ncmpDatastoreRequestHandler
+            = getNcmpDatastoreRequestHandler(cmResourceAddress.getDatastoreName());
+        return ncmpDatastoreRequestHandler.executeRequest(cmResourceAddress, optionsParamInQuery,
+            topicParamInQuery, includeDescendants, authorization);
     }
 
     /**
@@ -117,7 +119,6 @@
             operationType, requestData, dataType, authorization);
     }
 
-
     private NcmpDatastoreRequestHandler getNcmpDatastoreRequestHandler(final String datastoreName) {
         if (OPERATIONAL.equals(DatastoreType.fromDatastoreName(datastoreName))) {
             return ncmpCachedResourceRequestHandler;
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/InventoryPersistence.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/InventoryPersistence.java
index cb4b04e..beef752 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/InventoryPersistence.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/InventoryPersistence.java
@@ -144,4 +144,12 @@
      * @return Collection of CM handle Ids
      */
     Collection<String> getCmHandleIdsWithGivenModules(Collection<String> moduleNamesForQuery);
+
+    /**
+     * Check database if cm handle id exists if not return false.
+     *
+     * @param cmHandleId cmHandle Id
+     * @return Boolean
+     */
+    boolean isExistingCmHandleId(String cmHandleId);
 }
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/InventoryPersistenceImpl.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/InventoryPersistenceImpl.java
index 0ca2cd3..083b25d 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/InventoryPersistenceImpl.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/InventoryPersistenceImpl.java
@@ -195,6 +195,15 @@
         return cpsAnchorService.queryAnchorNames(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, moduleNamesForQuery);
     }
 
+    @Override
+    public boolean isExistingCmHandleId(final String cmHandleId) {
+        try {
+            return getCmHandleDataNodeByCmHandleId(cmHandleId).size() > 0;
+        } catch (final DataNodeNotFoundException exception) {
+            return false;
+        }
+    }
+
     private static String getXPathForCmHandleById(final String cmHandleId) {
         return NCMP_DMI_REGISTRY_PARENT + "/cm-handles[@id='" + cmHandleId + "']";
     }
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/ModuleSyncService.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/ModuleSyncService.java
index d2bc3ad..ca0f1c6 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/ModuleSyncService.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/ModuleSyncService.java
@@ -29,23 +29,17 @@
 import java.time.OffsetDateTime;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.List;
 import java.util.Map;
 import lombok.AllArgsConstructor;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
-import org.apache.commons.lang3.StringUtils;
 import org.onap.cps.api.CpsAnchorService;
 import org.onap.cps.api.CpsDataService;
 import org.onap.cps.api.CpsModuleService;
-import org.onap.cps.ncmp.impl.inventory.CmHandleQueryService;
 import org.onap.cps.ncmp.impl.inventory.models.CmHandleState;
 import org.onap.cps.ncmp.impl.inventory.models.YangModelCmHandle;
-import org.onap.cps.ncmp.impl.utils.YangDataConverter;
 import org.onap.cps.spi.CascadeDeleteAllowed;
-import org.onap.cps.spi.FetchDescendantsOption;
 import org.onap.cps.spi.exceptions.SchemaSetNotFoundException;
-import org.onap.cps.spi.model.DataNode;
 import org.onap.cps.spi.model.ModuleReference;
 import org.onap.cps.utils.ContentType;
 import org.onap.cps.utils.JsonObjectMapper;
@@ -58,7 +52,6 @@
 
     private final DmiModelOperations dmiModelOperations;
     private final CpsModuleService cpsModuleService;
-    private final CmHandleQueryService cmHandleQueryService;
     private final CpsDataService cpsDataService;
     private final CpsAnchorService cpsAnchorService;
     private final JsonObjectMapper jsonObjectMapper;
@@ -113,34 +106,25 @@
     }
 
     private ModuleDelta getModuleDelta(final YangModelCmHandle yangModelCmHandle, final String targetModuleSetTag) {
-        final Collection<ModuleReference> allModuleReferences;
         final Map<String, String> newYangResources;
-
-        final YangModelCmHandle cmHandleWithSameModuleSetTag = getAnyReadyCmHandleByModuleSetTag(targetModuleSetTag);
-        if (cmHandleWithSameModuleSetTag == null) {
+        Collection<ModuleReference> allModuleReferences = getModuleReferencesByModuleSetTag(targetModuleSetTag);
+        if (allModuleReferences.isEmpty()) {
             allModuleReferences = dmiModelOperations.getModuleReferences(yangModelCmHandle);
             newYangResources = dmiModelOperations.getNewYangResourcesFromDmi(yangModelCmHandle,
                     cpsModuleService.identifyNewModuleReferences(allModuleReferences));
         } else {
             log.info("Found other cm handle having same module set tag: {}", targetModuleSetTag);
-            allModuleReferences = cpsModuleService.getYangResourcesModuleReferences(
-                    NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, cmHandleWithSameModuleSetTag.getId());
             newYangResources = NO_NEW_MODULES;
         }
         return new ModuleDelta(allModuleReferences, newYangResources);
     }
 
-    private YangModelCmHandle getAnyReadyCmHandleByModuleSetTag(final String moduleSetTag) {
-        if (StringUtils.isBlank(moduleSetTag)) {
-            return null;
+    private Collection<ModuleReference> getModuleReferencesByModuleSetTag(final String moduleSetTag) {
+        if (moduleSetTag == null || moduleSetTag.trim().isEmpty()) {
+            return Collections.emptyList();
         }
-        final String escapedModuleSetTag = moduleSetTag.replace("'", "''");
-        final List<DataNode> dataNodes = cmHandleQueryService.queryNcmpRegistryByCpsPath(
-                NCMP_DMI_REGISTRY_PARENT + "/cm-handles[@module-set-tag='" + escapedModuleSetTag + "']",
-                FetchDescendantsOption.DIRECT_CHILDREN_ONLY);
-        return dataNodes.stream().map(YangDataConverter::toYangModelCmHandle)
-                .filter(cmHandle -> cmHandle.getCompositeState().getCmHandleState() == CmHandleState.READY)
-                .findFirst().orElse(null);
+        return cpsModuleService.getModuleReferencesByAttribute(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR,
+                Map.of("module-set-tag", moduleSetTag), Map.of("cm-handle-state", CmHandleState.READY.name()));
     }
 
     private void setCmHandleModuleSetTag(final YangModelCmHandle yangModelCmHandle, final String newModuleSetTag) {
@@ -149,4 +133,5 @@
         cpsDataService.updateNodeLeaves(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, NCMP_DMI_REGISTRY_PARENT,
                 jsonForUpdate, OffsetDateTime.now(), ContentType.JSON);
     }
+
 }
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/utils/AlternateIdMatcher.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/utils/AlternateIdMatcher.java
index 832e576..c408ff9 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/utils/AlternateIdMatcher.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/utils/AlternateIdMatcher.java
@@ -56,6 +56,21 @@
         throw new NoAlternateIdMatchFoundException(alternateId);
     }
 
+    /**
+     * Get cm handle Id from given cmHandleReference.
+     *
+     * @param cmHandleReference alternate ID
+     * @return cm handle id string
+     */
+    public String getCmHandleId(final String cmHandleReference) {
+        if (inventoryPersistence.isExistingCmHandleId(cmHandleReference)) {
+            return cmHandleReference;
+        } else {
+            return inventoryPersistence.getCmHandleDataNodeByAlternateId(cmHandleReference)
+              .getLeaves().get("id").toString();
+        }
+    }
+
     private String getParentPath(final String path, final String separator) {
         final int lastSeparatorIndex = path.lastIndexOf(separator);
         return lastSeparatorIndex < 0 ? "" : path.substring(0, lastSeparatorIndex);
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/data/DmiDataOperationsSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/data/DmiDataOperationsSpec.groovy
index 970444f..8b369bf 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/data/DmiDataOperationsSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/data/DmiDataOperationsSpec.groovy
@@ -32,6 +32,7 @@
 import org.onap.cps.ncmp.impl.dmi.DmiProperties
 import org.onap.cps.ncmp.impl.dmi.UrlTemplateParameters
 import org.onap.cps.ncmp.impl.inventory.models.CmHandleState
+import org.onap.cps.ncmp.impl.utils.AlternateIdMatcher
 import org.onap.cps.ncmp.utils.TestUtils
 import org.onap.cps.utils.JsonObjectMapper
 import org.spockframework.spring.SpringBean
@@ -76,6 +77,9 @@
     @SpringBean
     PolicyExecutor policyExecutor = Mock()
 
+    @SpringBean
+    AlternateIdMatcher alternateIdMatcher = Mock()
+
     def 'call get resource data for #expectedDataStore from DMI without topic #scenario.'() {
         given: 'a cm handle for #cmHandleId'
             mockYangModelCmHandleRetrieval(dmiProperties)
@@ -86,6 +90,7 @@
             mockDmiRestClient.asynchronousPostOperationWithJsonData(DATA, expectedUrlTemplateWithVariables, expectedJson, READ, NO_AUTH_HEADER) >> responseFromDmi
         when: 'get resource data is invoked'
             def cmResourceAddress = new CmResourceAddress(expectedDataStore.datastoreName, cmHandleId, resourceIdentifier)
+            alternateIdMatcher.getCmHandleId(cmHandleId) >> cmHandleId
             def result = objectUnderTest.getResourceDataFromDmi(cmResourceAddress, expectedOptions, NO_TOPIC, NO_REQUEST_ID, NO_AUTH_HEADER).block()
         then: 'the result is the response from the DMI service'
             assert result.body == '{some-key:some-value}'
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/data/NcmpCachedResourceRequestHandlerSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/data/NcmpCachedResourceRequestHandlerSpec.groovy
index 9c696dc..314b761 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/data/NcmpCachedResourceRequestHandlerSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/data/NcmpCachedResourceRequestHandlerSpec.groovy
@@ -21,19 +21,35 @@
 package org.onap.cps.ncmp.impl.data
 
 import org.onap.cps.api.CpsDataService
+import org.onap.cps.events.EventsPublisher
 import org.onap.cps.ncmp.api.data.models.CmResourceAddress
+import org.onap.cps.ncmp.config.CpsApplicationContext
+import org.onap.cps.ncmp.impl.dmi.DmiProperties
+import org.onap.cps.ncmp.impl.utils.AlternateIdMatcher
 import org.onap.cps.spi.model.DataNode
+import org.spockframework.spring.SpringBean
+import org.springframework.boot.test.context.SpringBootTest
+import org.springframework.context.ApplicationContext
+import org.springframework.test.context.ContextConfiguration
 import reactor.core.publisher.Mono
 import spock.lang.Specification
 
 import static org.onap.cps.spi.FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS
 import static org.onap.cps.spi.FetchDescendantsOption.OMIT_DESCENDANTS
 
+@SpringBootTest
+@ContextConfiguration(classes = [CpsApplicationContext])
 class NcmpCachedResourceRequestHandlerSpec extends Specification {
 
     def cpsDataService = Mock(CpsDataService)
     def networkCmProxyQueryService= Mock(NetworkCmProxyQueryService)
 
+    @SpringBean
+    AlternateIdMatcher alternateIdMatcher = Mock()
+
+    @SpringBean
+    ApplicationContext applicationContext = Mock()
+
     def objectUnderTest = new NcmpCachedResourceRequestHandler(cpsDataService, networkCmProxyQueryService)
 
     def 'Execute a request with include descendants = #includeDescendants.'() {
@@ -54,6 +70,7 @@
             def dataNode2 = new DataNode(xpath:'p2')
             cpsDataService.getDataNodes('datastore','ch-1','resource',OMIT_DESCENDANTS) >> [dataNode1, dataNode2]
         when: 'getting the resource data'
+            alternateIdMatcher.getCmHandleId('ch-1') >> 'ch-1'
             def result = objectUnderTest.getResourceDataForCmHandle(cmResourceAddress, 'options', 'topic', 'request id', false, 'authorization')
         then: 'the result is a "Mono" holding just the first data node'
             assert result instanceof Mono
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/data/NetworkCmProxyFacadeSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/data/NetworkCmProxyFacadeSpec.groovy
index f4e4499..5f83ad5 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/data/NetworkCmProxyFacadeSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/data/NetworkCmProxyFacadeSpec.groovy
@@ -26,6 +26,7 @@
 
 import org.onap.cps.ncmp.api.data.models.CmResourceAddress
 import org.onap.cps.ncmp.api.data.models.DataOperationRequest
+import org.onap.cps.ncmp.impl.utils.AlternateIdMatcher
 import org.onap.cps.spi.model.DataNode
 import reactor.core.publisher.Mono
 import spock.lang.Specification
@@ -41,8 +42,9 @@
     def mockDmiDataOperations = Mock(DmiDataOperations)
     def mockNcmpCachedResourceRequestHandler = Mock(NcmpCachedResourceRequestHandler)
     def mockNcmpPassthroughResourceRequestHandler = Mock(NcmpPassthroughResourceRequestHandler)
+    def mockAlternateIdMatcher =  Mock(AlternateIdMatcher)
 
-    def objectUnderTest = new NetworkCmProxyFacade(mockNcmpCachedResourceRequestHandler, mockNcmpPassthroughResourceRequestHandler, mockDmiDataOperations)
+    def objectUnderTest = new NetworkCmProxyFacade(mockNcmpCachedResourceRequestHandler, mockNcmpPassthroughResourceRequestHandler, mockDmiDataOperations, mockAlternateIdMatcher)
 
     def NO_TOPIC = null
 
@@ -87,6 +89,7 @@
         given: 'a cm resource address for datastore operational'
             def cmResourceAddress = new CmResourceAddress('ncmp-datastore:operational', 'some CM Handle', 'some resource Id')
         and: 'get resource data from DMI is called'
+            mockAlternateIdMatcher.getCmHandleId('some CM Handle') >> 'some CM Handle'
             mockNcmpCachedResourceRequestHandler.executeRequest(cmResourceAddress, 'options', NO_TOPIC, false, 'authorization') >>
                     Mono.just('dmi response')
         when: 'get resource data operational for the given cm resource address is called'
@@ -103,6 +106,4 @@
         then: 'DMI called with correct data'
             1 * mockDmiDataOperations.writeResourceDataPassThroughRunningFromDmi('testCmHandle', 'testResourceId', UPDATE, '{some-json}', 'application/json', 'authorization')
     }
-
-
 }
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/sync/ModuleSyncServiceSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/sync/ModuleSyncServiceSpec.groovy
index c3a01a7..6030e5d 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/sync/ModuleSyncServiceSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/sync/ModuleSyncServiceSpec.groovy
@@ -30,8 +30,6 @@
 import org.onap.cps.ncmp.impl.inventory.models.YangModelCmHandle
 import org.onap.cps.spi.CascadeDeleteAllowed
 import org.onap.cps.spi.exceptions.SchemaSetNotFoundException
-import org.onap.cps.spi.model.DataNode
-import org.onap.cps.spi.model.DataNodeBuilder
 import org.onap.cps.spi.model.ModuleReference
 import org.onap.cps.utils.JsonObjectMapper
 import spock.lang.Specification
@@ -49,13 +47,9 @@
     def mockJsonObjectMapper = Mock(JsonObjectMapper)
 
     def objectUnderTest = new ModuleSyncService(mockDmiModelOperations, mockCpsModuleService,
-            mockCmHandleQueries, mockCpsDataService, mockCpsAnchorService, mockJsonObjectMapper)
+            mockCpsDataService, mockCpsAnchorService, mockJsonObjectMapper)
 
     def expectedDataspaceName = NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME
-    def static cmHandleWithModuleSetTag = new DataNodeBuilder()
-            .withXpath("/dmi-registry/cm-handles[@id='otherId']")
-            .withLeaves(['id': 'otherId', 'module-set-tag': 'tag-1'])
-            .withAnchor('otherId').build()
 
     def 'Sync model for a NEW cm handle using module set tags: #scenario.'() {
         given: 'a cm handle state to be synced'
@@ -70,8 +64,8 @@
             mockDmiModelOperations.getNewYangResourcesFromDmi(yangModelCmHandle, identifiedNewModuleReferences) >> newModuleNameContentToMap
         and: 'the module service identifies #identifiedNewModuleReferences.size() new modules'
             mockCpsModuleService.identifyNewModuleReferences(moduleReferences) >> identifiedNewModuleReferences
-        and: 'system contains other cm handle with "same tag" (that is READY)'
-            mockCmHandleQueries.queryNcmpRegistryByCpsPath(*_) >> existingCmHandlesWithSameTag
+        and: 'the service returns a list of module references when queried with the specified attributes'
+            mockCpsModuleService.getModuleReferencesByAttribute(*_) >> existingModuleReferences
         when: 'module sync is triggered'
             objectUnderTest.syncAndCreateSchemaSetAndAnchor(yangModelCmHandle)
         then: 'create schema set from module is invoked with correct parameters'
@@ -79,10 +73,10 @@
         and: 'anchor is created with the correct parameters'
             1 * mockCpsAnchorService.createAnchor(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, 'ch-1', 'ch-1')
         where: 'the following parameters are used'
-            scenario                  | existingModuleResourcesInCps         | identifiedNewModuleReferences         | newModuleNameContentToMap     | moduleSetTag | existingCmHandlesWithSameTag
-            'one new module, new tag' | [['module2': '2'], ['module3': '3']] | [new ModuleReference('module1', '1')] | [module1: 'some yang source'] | ''           | []
-            'no new module, new tag'  | [['module1': '1'], ['module2': '2']] | []                                    | [:]                           | 'new-tag-1'  | []
-            'same tag'                | [['module1': '1'], ['module2': '2']] | []                                    | [:]                           | 'same-tag'   | [cmHandleWithModuleSetTag]
+            scenario                  | identifiedNewModuleReferences         | newModuleNameContentToMap     | moduleSetTag | existingModuleReferences
+            'one new module, new tag' | [new ModuleReference('module1', '1')] | [module1: 'some yang source'] | ''           | []
+            'no new module, new tag'  | []                                    | [:]                           | 'new-tag-1'  | []
+            'same tag'                | []                                    | [:]                           | 'same-tag'   | [new ModuleReference('module1', '1'), new ModuleReference('module2', '2')]
     }
 
     def 'Upgrade model for an existing cm handle with Module Set Tag where the modules are #scenario'() {
@@ -101,8 +95,8 @@
             mockCpsModuleService.identifyNewModuleReferences(_) >> []
         and: 'CPS-Core returns list of existing module resources for TBD'
             mockCpsModuleService.getYangResourcesModuleReferences(*_) >> [ new ModuleReference('module1','1') ]
-        and: 'system contains #existingCmHandlesWithSameTag.size() cm handles with same tag'
-            mockCmHandleQueries.queryNcmpRegistryByCpsPath(*_) >> existingCmHandlesWithSameTag
+        and: 'the service returns a list of module references when queried with the specified attributes'
+            mockCpsModuleService.getModuleReferencesByAttribute(*_) >> existingModuleReferences
         and: 'the other cm handle is a state ready'
             mockCmHandleQueries.cmHandleHasState('otherId', CmHandleState.READY) >> true
         when: 'module sync is triggered'
@@ -114,9 +108,9 @@
         and: 'No anchor is created for the upgraded cm handle'
             0 * mockCpsAnchorService.createAnchor(*_)
         where: 'the following parameters are used'
-            scenario      | existingCmHandlesWithSameTag
+            scenario      | existingModuleReferences
             'new'         | []
-            'in database' | [cmHandleWithModuleSetTag]
+            'in database' | [new ModuleReference('module1', '1')]
     }
 
     def 'upgrade model for a existing cm handle'() {
@@ -130,9 +124,8 @@
         and: 'the module service returns some module references'
             def moduleReferences = [new ModuleReference('module1', '1'), new ModuleReference('module2', '2')]
             mockCpsModuleService.getYangResourcesModuleReferences(*_)>> moduleReferences
-        and: 'a cm handle with the same moduleSetTag can be found in the registry'
-            mockCmHandleQueries.queryNcmpRegistryByCpsPath(*_) >> [new DataNode(xpath: '/dmi-registry/cm-handles[@id=\'cmHandleId-1\']', leaves: ['id': 'cmHandleId-1'],
-                    childDataNodes: [new DataNode(xpath: '/dmi-registry/cm-handles[@id=\'cmHandleId-1\']/state', leaves: ['cm-handle-state': 'READY'])])]
+        and: 'the service returns a list of module references when queried with the specified attributes'
+            mockCpsModuleService.getModuleReferencesByAttribute(*_) >> moduleReferences
         when: 'module upgrade is triggered'
             objectUnderTest.syncAndUpgradeSchemaSet(yangModelCmHandle)
         then: 'the upgrade is delegated to the module service (with the correct parameters)'
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/utils/AlternateIdMatcherSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/utils/AlternateIdMatcherSpec.groovy
index ad84495..a497b45 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/utils/AlternateIdMatcherSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/utils/AlternateIdMatcherSpec.groovy
@@ -63,4 +63,18 @@
             'no match for other child' | '/a/c'
             'no match at all'          | '/x/y'
     }
+
+    def 'Get cmHandle id from passed cmHandleReference (cmHandleId scenario)' () {
+        when: 'a cmHandleCmReference is passed in'
+            def result = objectUnderTest.getCmHandleId(cmHandleReference)
+        then: 'the inventory persistence service returns a cm handle (or not)'
+            mockInventoryPersistence.isExistingCmHandleId(cmHandleReference) >> existingCmHandleIdResponse
+            mockInventoryPersistence.getCmHandleDataNodeByAlternateId(cmHandleReference) >> alternateIdGetResponse
+        and: 'correct result is returned'
+            assert result == cmHandleReference
+        where:
+            cmHandleReference | existingCmHandleIdResponse | alternateIdGetResponse
+            'ch-1'            |  true                      |  ''
+            'alt-1'           |  false                     |  new DataNode(leaves: [id:'alt-1'])
+    }
 }
diff --git a/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsModulePersistenceServiceImpl.java b/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsModulePersistenceServiceImpl.java
index 17f13b8..2c4cc74 100755
--- a/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsModulePersistenceServiceImpl.java
+++ b/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsModulePersistenceServiceImpl.java
@@ -241,6 +241,15 @@
         return moduleReferenceRepository.identifyNewModuleReferences(moduleReferencesToCheck);
     }
 
+    @Override
+    public Collection<ModuleReference> getModuleReferencesByAttribute(final String dataspaceName,
+                                                                      final String anchorName,
+                                                                      final Map<String, String> parentAttributes,
+                                                                      final Map<String, String> childAttributes) {
+        return moduleReferenceRepository.findModuleReferences(dataspaceName, anchorName, parentAttributes,
+                childAttributes);
+    }
+
     private Set<YangResourceEntity> synchronizeYangResources(
         final Map<String, String> moduleReferenceNameToContentMap) {
         final Map<String, YangResourceEntity> checksumToEntityMap = moduleReferenceNameToContentMap.entrySet().stream()
diff --git a/cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentRepositoryCpsPathQueryImpl.java b/cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentRepositoryCpsPathQueryImpl.java
index 78e0f08..9c98f7f 100644
--- a/cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentRepositoryCpsPathQueryImpl.java
+++ b/cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentRepositoryCpsPathQueryImpl.java
@@ -1,6 +1,6 @@
 /*-
  * ============LICENSE_START=======================================================
- *  Copyright (C) 2021-2023 Nordix Foundation.
+ *  Copyright (C) 2021-2024 Nordix Foundation.
  *  Modifications Copyright (C) 2023 TechMahindra Ltd.
  * ================================================================================
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -21,8 +21,6 @@
 
 package org.onap.cps.spi.repository;
 
-import jakarta.persistence.EntityManager;
-import jakarta.persistence.PersistenceContext;
 import jakarta.persistence.Query;
 import jakarta.transaction.Transactional;
 import java.util.List;
@@ -38,9 +36,6 @@
 @Slf4j
 public class FragmentRepositoryCpsPathQueryImpl implements FragmentRepositoryCpsPathQuery {
 
-    @PersistenceContext
-    private EntityManager entityManager;
-
     private final FragmentQueryBuilder fragmentQueryBuilder;
 
     @Override
diff --git a/cps-ri/src/main/java/org/onap/cps/spi/repository/ModuleReferenceQuery.java b/cps-ri/src/main/java/org/onap/cps/spi/repository/ModuleReferenceQuery.java
index 00e53aa..4082307 100644
--- a/cps-ri/src/main/java/org/onap/cps/spi/repository/ModuleReferenceQuery.java
+++ b/cps-ri/src/main/java/org/onap/cps/spi/repository/ModuleReferenceQuery.java
@@ -1,6 +1,6 @@
 /*-
  * ============LICENSE_START=======================================================
- *  Copyright (C) 2022 Nordix Foundation.
+ *  Copyright (C) 2022-2024 Nordix Foundation.
  * ================================================================================
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -21,6 +21,7 @@
 package org.onap.cps.spi.repository;
 
 import java.util.Collection;
+import java.util.Map;
 import org.onap.cps.spi.model.ModuleReference;
 
 /**
@@ -29,4 +30,8 @@
 public interface ModuleReferenceQuery {
 
     Collection<ModuleReference> identifyNewModuleReferences(final Collection<ModuleReference> moduleReferencesToCheck);
+
+    Collection<ModuleReference> findModuleReferences(final String dataspaceName, final String anchorName,
+                                                     final Map<String, String> parentAttributes,
+                                                     final Map<String, String> childAttributes);
 }
diff --git a/cps-ri/src/main/java/org/onap/cps/spi/repository/ModuleReferenceRepositoryImpl.java b/cps-ri/src/main/java/org/onap/cps/spi/repository/ModuleReferenceRepositoryImpl.java
index 454848b..6cc8234 100644
--- a/cps-ri/src/main/java/org/onap/cps/spi/repository/ModuleReferenceRepositoryImpl.java
+++ b/cps-ri/src/main/java/org/onap/cps/spi/repository/ModuleReferenceRepositoryImpl.java
@@ -22,12 +22,15 @@
 
 import jakarta.persistence.EntityManager;
 import jakarta.persistence.PersistenceContext;
+import jakarta.persistence.Query;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
-import lombok.AllArgsConstructor;
+import java.util.Map;
+import java.util.stream.Collectors;
+import lombok.RequiredArgsConstructor;
 import lombok.SneakyThrows;
 import lombok.extern.slf4j.Slf4j;
 import org.onap.cps.spi.model.ModuleReference;
@@ -35,13 +38,13 @@
 
 @Slf4j
 @Transactional
-@AllArgsConstructor
+@RequiredArgsConstructor
 public class ModuleReferenceRepositoryImpl implements ModuleReferenceQuery {
 
     @PersistenceContext
     private EntityManager entityManager;
 
-    private TempTableCreator tempTableCreator;
+    private final TempTableCreator tempTableCreator;
 
     @Override
     @SneakyThrows
@@ -66,6 +69,96 @@
         return identifyNewModuleReferencesForCmHandle(tempTableName);
     }
 
+    /**
+     * Finds module references based on specified dataspace, anchor, and attribute filters.
+     * This method constructs and executes a SQL query to retrieve module references. The query applies filters to
+     * parent and child fragments using the provided attribute maps. The `parentAttributes` are used to filter
+     * parent fragments, while `childAttributes` filter child fragments.
+     *
+     * @param dataspaceName    the name of the dataspace to filter on.
+     * @param anchorName       the name of the anchor to filter on.
+     * @param parentAttributes a map of attributes for filtering parent fragments.
+     * @param childAttributes  a map of attributes for filtering child fragments.
+     * @return a collection of {@link ModuleReference} objects that match the specified filters.
+     */
+    @Transactional
+    @SuppressWarnings("unchecked")
+    @Override
+    public Collection<ModuleReference> findModuleReferences(final String dataspaceName, final String anchorName,
+                                                            final Map<String, String> parentAttributes,
+                                                            final Map<String, String> childAttributes) {
+
+        final String parentFragmentWhereClause = buildWhereClause(childAttributes, "parentFragment");
+        final String childFragmentWhereClause = buildWhereClause(parentAttributes, "childFragment");
+
+        final String moduleReferencesSqlQuery = buildModuleReferencesSqlQuery(parentFragmentWhereClause,
+                childFragmentWhereClause);
+
+        final Query query = entityManager.createNativeQuery(moduleReferencesSqlQuery);
+        setQueryParameters(query, parentAttributes, childAttributes, anchorName, dataspaceName);
+        return processQueryResults(query.getResultList());
+    }
+
+    private String buildWhereClause(final Map<String, String> attributes, final String alias) {
+        return attributes.keySet().stream()
+                .map(attributeName -> String.format("%s.attributes->>'%s' = ?", alias, attributeName))
+                .collect(Collectors.joining(" AND "));
+    }
+
+    private void setQueryParameters(final Query query, final Map<String, String> parentAttributes,
+                                    final Map<String, String> childAttributes, final String anchorName,
+                                    final String dataspaceName) {
+        final String childAttributeValue = childAttributes.entrySet().iterator().next().getValue();
+        query.setParameter(1, childAttributeValue);
+
+        final String parentAttributeValue = parentAttributes.entrySet().iterator().next().getValue();
+        query.setParameter(2, parentAttributeValue);
+
+        query.setParameter(3, anchorName);
+        query.setParameter(4, dataspaceName);
+    }
+
+    private String buildModuleReferencesSqlQuery(final String parentFragmentClause, final String childFragmentClause) {
+        return """
+                WITH Fragment AS (
+                    SELECT childFragment.attributes->>'id' AS schema_set_name
+                    FROM fragment parentFragment
+                    JOIN fragment childFragment ON parentFragment.parent_id = childFragment.id
+                    JOIN anchor anchorInfo ON parentFragment.anchor_id = anchorInfo.id
+                    JOIN dataspace dataspaceInfo ON anchorInfo.dataspace_id = dataspaceInfo.id
+                    WHERE %s
+                    AND %s
+                    AND anchorInfo.name = ?
+                    AND dataspaceInfo.name = ?
+                    LIMIT 1
+                ),
+                SchemaSet AS (
+                    SELECT id
+                    FROM schema_set
+                    WHERE name = (SELECT schema_set_name FROM Fragment)
+                )
+                SELECT yangResource.module_name, yangResource.revision
+                FROM yang_resource yangResource
+                JOIN schema_set_yang_resources schemaSetYangResources
+                ON yangResource.id = schemaSetYangResources.yang_resource_id
+                WHERE schemaSetYangResources.schema_set_id = (SELECT id FROM SchemaSet);
+                """.formatted(parentFragmentClause, childFragmentClause);
+    }
+
+    private Collection<ModuleReference> processQueryResults(final List<Object[]> queryResults) {
+        if (queryResults.isEmpty()) {
+            log.info("No module references found for the provided attributes.");
+            return Collections.emptyList();
+        }
+        return queryResults.stream()
+                .map(queryResult -> {
+                    final String name = (String) queryResult[0];
+                    final String revision = (String) queryResult[1];
+                    return new ModuleReference(name, revision);
+                })
+                .collect(Collectors.toList());
+    }
+
     private Collection<ModuleReference> identifyNewModuleReferencesForCmHandle(final String tempTableName) {
         final String sql = String.format(
                 "SELECT %1$s.module_name, %1$s.revision"
@@ -81,7 +174,6 @@
         for (final Object[] row : resultsAsObjects) {
             resultsAsModuleReferences.add(new ModuleReference((String) row[0], (String) row[1]));
         }
-
         return resultsAsModuleReferences;
     }
 }
diff --git a/cps-service/src/main/java/org/onap/cps/api/CpsModuleService.java b/cps-service/src/main/java/org/onap/cps/api/CpsModuleService.java
index bdd3614..931209c 100644
--- a/cps-service/src/main/java/org/onap/cps/api/CpsModuleService.java
+++ b/cps-service/src/main/java/org/onap/cps/api/CpsModuleService.java
@@ -155,4 +155,37 @@
     Collection<ModuleReference> identifyNewModuleReferences(
         Collection<ModuleReference> moduleReferencesToCheck);
 
+    /**
+     * Retrieves module references based on the provided dataspace name, anchor name and attribute filters
+     * for both parent and child fragments.
+
+     * This method constructs and executes a SQL query to find module references from a database, using
+     * the specified `dataspaceName`, `anchorName` and two sets of attribute filters: one for parent fragments
+     * and one for child fragments. The method applies these filters to identify the appropriate fragments
+     * and schema sets, and then retrieves the corresponding module references.
+
+     * The SQL query is dynamically built based on the provided attribute filters:
+     * - The `parentAttributes` map is used to filter the parent fragments. The entries in this map are
+     * converted into a WHERE clause for the parent fragments.
+     * - The `childAttributes` map is used to filter the child fragments. This is applied to the child fragments
+     * after filtering the parent fragments.
+     *
+     * @param dataspaceName    the name of the dataspace to filter on. It is used to locate the relevant dataspace
+     *                         in the database.
+     * @param anchorName       the name of the anchor to filter on. It is used to locate the relevant anchor within
+     *                         the dataspace.
+     * @param parentAttributes a map of attributes to filter parent fragments. Each entry in this map represents
+     *                         an attribute key-value pair used in the WHERE clause for parent fragments.
+     * @param childAttributes  a map of attributes to filter child fragments. Each entry in this map represents
+     *                         an attribute key-value pair used in the WHERE clause for child fragments.
+     * @return a collection of {@link ModuleReference} objects that match the given criteria. Each
+     * {@code ModuleReference} contains information about a module's name and revision.
+     * @implNote The method assumes that both `parentAttributes` and `childAttributes` maps contain at least
+     *     one entry. The first entry from `parentAttributes` is used to filter parent fragments,
+     *     and the first entry from `childAttributes` is used to filter child fragments.
+     */
+    Collection<ModuleReference> getModuleReferencesByAttribute(final String dataspaceName, final String anchorName,
+                                                               final Map<String, String> parentAttributes,
+                                                               final Map<String, String> childAttributes);
+
 }
diff --git a/cps-service/src/main/java/org/onap/cps/api/impl/CpsModuleServiceImpl.java b/cps-service/src/main/java/org/onap/cps/api/impl/CpsModuleServiceImpl.java
index e6ad9a8..34610f3 100644
--- a/cps-service/src/main/java/org/onap/cps/api/impl/CpsModuleServiceImpl.java
+++ b/cps-service/src/main/java/org/onap/cps/api/impl/CpsModuleServiceImpl.java
@@ -171,6 +171,17 @@
         return cpsModulePersistenceService.identifyNewModuleReferences(moduleReferencesToCheck);
     }
 
+    @Timed(value = "cps.module.service.module.reference.query",
+            description = "Time taken to query list of module references")
+    @Override
+    public Collection<ModuleReference> getModuleReferencesByAttribute(final String dataspaceName,
+                                                                      final String anchorName,
+                                                                      final Map<String, String> parentAttributes,
+                                                                      final Map<String, String> childAttributes) {
+        return cpsModulePersistenceService.getModuleReferencesByAttribute(dataspaceName, anchorName, parentAttributes,
+                childAttributes);
+    }
+
     private boolean isCascadeDeleteProhibited(final CascadeDeleteAllowed cascadeDeleteAllowed) {
         return CascadeDeleteAllowed.CASCADE_DELETE_PROHIBITED == cascadeDeleteAllowed;
     }
diff --git a/cps-service/src/main/java/org/onap/cps/spi/CpsModulePersistenceService.java b/cps-service/src/main/java/org/onap/cps/spi/CpsModulePersistenceService.java
index eeaaa47..793f38e 100755
--- a/cps-service/src/main/java/org/onap/cps/spi/CpsModulePersistenceService.java
+++ b/cps-service/src/main/java/org/onap/cps/spi/CpsModulePersistenceService.java
@@ -153,4 +153,20 @@
     Collection<ModuleReference> identifyNewModuleReferences(
         Collection<ModuleReference> moduleReferencesToCheck);
 
+    /**
+     * Retrieves module references based on the specified dataspace, anchor, and attribute filters.
+
+     * Constructs and executes a SQL query to find module references by applying filters for parent and child fragments.
+     * Uses `parentAttributes` for filtering parent fragments and `childAttributes` for filtering child fragments.
+     *
+     * @param dataspaceName    the name of the dataspace to filter on.
+     * @param anchorName       the name of the anchor to filter on.
+     * @param parentAttributes a map of attributes for filtering parent fragments.
+     * @param childAttributes  a map of attributes for filtering child fragments.
+     * @return a collection of {@link ModuleReference} objects matching the criteria.
+     */
+    Collection<ModuleReference> getModuleReferencesByAttribute(final String dataspaceName, final String anchorName,
+                                                               final Map<String, String> parentAttributes,
+                                                               final Map<String, String> childAttributes);
+
 }
diff --git a/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsModuleServiceImplSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsModuleServiceImplSpec.groovy
index ad8c54b..62eba0c 100644
--- a/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsModuleServiceImplSpec.groovy
+++ b/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsModuleServiceImplSpec.groovy
@@ -238,6 +238,23 @@
             1 * mockCpsModulePersistenceService.identifyNewModuleReferences(moduleReferencesToCheck)
     }
 
+    def 'Get module references when queried by attributes'() {
+        given: 'a valid dataspace name and anchor name'
+            def dataspaceName = 'someDataspace'
+            def anchorName = 'someAnchor'
+        and: 'a set of parent attributes and child attributes used for filtering'
+            def parentAttributes = ['some-property-key1': 'some-property-val1']
+            def childAttributes = ['some-property-key2': 'some-property-val2']
+        and: 'a list of expected module references returned by the persistence service'
+            def expectedModuleReferences = [new ModuleReference(moduleName: 'some-name', revision: 'some-revision')]
+            mockCpsModulePersistenceService.getModuleReferencesByAttribute(dataspaceName, anchorName, parentAttributes, childAttributes) >> expectedModuleReferences
+        when: 'the method is invoked to retrieve module references by attributes'
+            def actualModuleReferences = objectUnderTest.getModuleReferencesByAttribute(dataspaceName, anchorName, parentAttributes, childAttributes)
+        then: 'the retrieved module references should match the expected module references'
+            assert actualModuleReferences == expectedModuleReferences
+    }
+
+
     def 'Getting module definitions with module name'() {
         given: 'module persistence service returns module definitions for module name'
             def moduleDefinitionsFromPersistenceService = [ new ModuleDefinition('name', 'revision', 'content' ) ]
diff --git a/docs/_static/logo_onap_2024.png b/docs/_static/logo_onap_2024.png
new file mode 100644
index 0000000..55d307f
--- /dev/null
+++ b/docs/_static/logo_onap_2024.png
Binary files differ
diff --git a/docs/conf.py b/docs/conf.py
index e8bb663..5d7a799 100755
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -31,7 +31,7 @@
 html_theme_options = {
     "style_nav_header_background": "white",
     "sticky_navigation": "False" }
-html_logo = "_static/logo_onap_2017.png"
+html_logo = "_static/logo_onap_2024.png"
 html_favicon = "_static/favicon.ico"
 html_static_path = ["_static"]
 html_show_sphinx = False
diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/AlternateIdSpec.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/AlternateIdSpec.groovy
new file mode 100644
index 0000000..222b3c0
--- /dev/null
+++ b/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/AlternateIdSpec.groovy
@@ -0,0 +1,54 @@
+/*
+ *  ============LICENSE_START=======================================================
+ *  Copyright (C) 2024 Nordix Foundation
+ *  ================================================================================
+ *  Licensed under the Apache License, Version 2.0 (the 'License');
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an 'AS IS' BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.integration.functional.ncmp
+
+import org.onap.cps.integration.base.CpsIntegrationSpecBase
+import org.springframework.http.HttpStatus
+import org.springframework.http.MediaType
+
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get
+
+class AlternateIdSpec extends CpsIntegrationSpecBase {
+
+    def setup() {
+        dmiDispatcher1.moduleNamesPerCmHandleId['ch-1'] = ['M1', 'M2']
+        registerCmHandle(DMI1_URL, 'ch-1', NO_MODULE_SET_TAG, 'alternateId')
+    }
+
+    def cleanup() {
+        deregisterCmHandle(DMI1_URL, 'ch-1')
+    }
+
+    def 'AlternateId in pass-through data operations should return OK status.'() {
+        given: 'the URL for the pass-through data request'
+            def url = '/ncmp/v1/ch/alternateId/data/ds/ncmp-datastore:passthrough-running'
+        when: 'a pass-through data request is sent to NCMP'
+            def response = mvc.perform(get(url)
+                    .queryParam('resourceIdentifier', 'my-resource-id')
+                    .contentType(MediaType.APPLICATION_JSON))
+                    .andReturn().response
+        then: 'response status is Ok'
+            assert response.status == HttpStatus.OK.value()
+    }
+
+
+
+}
diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/CmHandleCreateSpec.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/CmHandleCreateSpec.groovy
index 3d526c6..d27badc 100644
--- a/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/CmHandleCreateSpec.groovy
+++ b/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/CmHandleCreateSpec.groovy
@@ -24,6 +24,7 @@
 import org.apache.kafka.common.serialization.StringDeserializer
 import org.onap.cps.integration.KafkaTestContainer
 import org.onap.cps.integration.base.CpsIntegrationSpecBase
+import org.onap.cps.ncmp.api.NcmpResponseStatus
 import org.onap.cps.ncmp.api.inventory.NetworkCmProxyInventoryFacade
 import org.onap.cps.ncmp.api.inventory.models.CmHandleRegistrationResponse
 import org.onap.cps.ncmp.api.inventory.models.DmiPluginRegistration
@@ -133,6 +134,38 @@
             deregisterCmHandles(DMI1_URL, ['ch-1', 'ch-2', 'ch-3'])
     }
 
+    def 'Create CM-handles with alternate IDs.'() {
+        given: 'DMI will return modules for all CM-handles when requested'
+            dmiDispatcher1.moduleNamesPerCmHandleId = (1..7).collectEntries{ ['ch-'+it, ['M1']] }
+        and: 'an existing CM-handle with an alternate ID'
+            registerCmHandle(DMI1_URL, 'ch-1', NO_MODULE_SET_TAG, 'existing-alt-id')
+        and: 'an existing CM-handle with no alternate ID'
+            registerCmHandle(DMI1_URL, 'ch-2', NO_MODULE_SET_TAG, NO_ALTERNATE_ID)
+
+        when: 'a batch of CM-handles is registered for creation with various alternate IDs'
+            def cmHandlesToCreate = [
+                    new NcmpServiceCmHandle(cmHandleId: 'ch-3', alternateId: NO_ALTERNATE_ID),
+                    new NcmpServiceCmHandle(cmHandleId: 'ch-4', alternateId: 'unique-alt-id'),
+                    new NcmpServiceCmHandle(cmHandleId: 'ch-5', alternateId: 'existing-alt-id'),
+                    new NcmpServiceCmHandle(cmHandleId: 'ch-6', alternateId: 'duplicate-alt-id'),
+                    new NcmpServiceCmHandle(cmHandleId: 'ch-7', alternateId: 'duplicate-alt-id'),
+            ]
+            def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: DMI1_URL, createdCmHandles: cmHandlesToCreate)
+            def dmiPluginRegistrationResponse = objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
+
+        then: 'registration gives expected responses'
+            assert dmiPluginRegistrationResponse.createdCmHandles.sort { it.cmHandle } == [
+                CmHandleRegistrationResponse.createSuccessResponse('ch-3'),
+                CmHandleRegistrationResponse.createSuccessResponse('ch-4'),
+                CmHandleRegistrationResponse.createFailureResponse('ch-5', NcmpResponseStatus.ALTERNATE_ID_ALREADY_ASSOCIATED),
+                CmHandleRegistrationResponse.createSuccessResponse('ch-6'),
+                CmHandleRegistrationResponse.createFailureResponse('ch-7', NcmpResponseStatus.ALTERNATE_ID_ALREADY_ASSOCIATED),
+            ]
+
+        cleanup: 'deregister CM handles'
+            deregisterCmHandles(DMI1_URL, (1..7).collect{ 'ch-'+it })
+    }
+
     def 'CM Handle retry after failed module sync.'() {
         given: 'DMI is not initially available to handle requests'
             dmiDispatcher1.isAvailable = false
diff --git a/k6-tests/ncmp/common/cmhandle-crud.js b/k6-tests/ncmp/common/cmhandle-crud.js
index 6d5aff7..88ecdb4 100644
--- a/k6-tests/ncmp/common/cmhandle-crud.js
+++ b/k6-tests/ncmp/common/cmhandle-crud.js
@@ -20,7 +20,7 @@
 
 import http from 'k6/http';
 import { check, sleep } from 'k6';
-import { NCMP_BASE_URL, DMI_PLUGIN_URL, TOTAL_CM_HANDLES, REGISTRATION_BATCH_SIZE, CONTENT_TYPE_JSON_PARAM, makeBatchOfCmHandleIds } from './utils.js';
+import { NCMP_BASE_URL, DMI_PLUGIN_URL, TOTAL_CM_HANDLES, MODULE_SET_TAGS, REGISTRATION_BATCH_SIZE, CONTENT_TYPE_JSON_PARAM, makeBatchOfCmHandleIds } from './utils.js';
 import { executeCmHandleIdSearch } from './search-base.js';
 
 export function registerAllCmHandles() {
@@ -44,8 +44,10 @@
     const url = `${NCMP_BASE_URL}/ncmpInventory/v1/ch`;
     const payload = {
         "dmiPlugin": DMI_PLUGIN_URL,
-        "createdCmHandles": cmHandleIds.map(cmHandleId => ({
+        "createdCmHandles": cmHandleIds.map((cmHandleId, index) => ({
             "cmHandle": cmHandleId,
+            "alternateId": `alt-${cmHandleId}`,
+            "moduleSetTag": MODULE_SET_TAGS[index % MODULE_SET_TAGS.length],
             "cmHandleProperties": {"neType": "RadioNode"},
             "publicCmHandleProperties": {
                 "Color": "yellow",
diff --git a/k6-tests/ncmp/common/passthrough-crud.js b/k6-tests/ncmp/common/passthrough-crud.js
index 76bda4e..5617f9d 100644
--- a/k6-tests/ncmp/common/passthrough-crud.js
+++ b/k6-tests/ncmp/common/passthrough-crud.js
@@ -36,6 +36,16 @@
     return response;
 }
 
+export function passthroughReadWithAltId() {
+    const cmHandleId = getRandomCmHandleId();
+    const resourceIdentifier = 'my-resource-identifier';
+    const includeDescendants = true;
+    const datastoreName = 'ncmp-datastore:passthrough-operational';
+    const url = `${NCMP_BASE_URL}/ncmp/v1/ch/alt-${cmHandleId}/data/ds/${datastoreName}?resourceIdentifier=${resourceIdentifier}&include-descendants=${includeDescendants}`
+    const response = http.get(url);
+    return response;
+}
+
 export function passthroughWrite() {
     const cmHandleId = getRandomCmHandleId();
     const resourceIdentifier = 'my-resource-identifier';
diff --git a/k6-tests/ncmp/common/utils.js b/k6-tests/ncmp/common/utils.js
index f24edc5..58c958d 100644
--- a/k6-tests/ncmp/common/utils.js
+++ b/k6-tests/ncmp/common/utils.js
@@ -28,6 +28,7 @@
 export const DATA_OPERATION_READ_BATCH_SIZE = 200;
 export const TOPIC_DATA_OPERATIONS_BATCH_READ = 'topic-data-operations-batch-read';
 export const KAFKA_BOOTSTRAP_SERVERS = ['localhost:9092'];
+export const MODULE_SET_TAGS = ['tagA','tagB','tagC',' tagD']
 
 export function recordTimeInSeconds(functionToExecute) {
     const startTimeInMillis = Date.now();
@@ -66,6 +67,7 @@
         makeSummaryCsvLine('4', 'CM-handle search with Module filter', 'milliseconds', 'http_req_duration{scenario:cm_search_module}', data, options),
         makeSummaryCsvLine('5a', 'Synchronous single CM-handle pass-through read', 'requests/second', 'http_reqs{scenario:passthrough_read}', data, options),
         makeSummaryCsvLine('5b', 'NCMP overhead for Synchronous single CM-handle pass-through read', 'milliseconds', 'ncmp_overhead_passthrough_read', data, options),
+        makeSummaryCsvLine('5c', 'NCMP overhead for Synchronous single CM-handle pass-through read with alternate id', 'milliseconds', 'ncmp_overhead_passthrough_read_alt_id', data, options),
         makeSummaryCsvLine('6a', 'Synchronous single CM-handle pass-through write', 'requests/second', 'http_reqs{scenario:passthrough_write}', data, options),
         makeSummaryCsvLine('6b', 'NCMP overhead for Synchronous single CM-handle pass-through write', 'milliseconds', 'ncmp_overhead_passthrough_write', data, options),
         makeSummaryCsvLine('7', 'Data operations batch read', 'events/second', 'data_operations_batch_read_cmhandles_per_second', data, options),
diff --git a/k6-tests/ncmp/ncmp-kpi.js b/k6-tests/ncmp/ncmp-kpi.js
index 8ff9ec5..d7e4405 100644
--- a/k6-tests/ncmp/ncmp-kpi.js
+++ b/k6-tests/ncmp/ncmp-kpi.js
@@ -27,7 +27,7 @@
 } from './common/utils.js';
 import { registerAllCmHandles, deregisterAllCmHandles } from './common/cmhandle-crud.js';
 import { executeCmHandleSearch, executeCmHandleIdSearch } from './common/search-base.js';
-import { passthroughRead, passthroughWrite, batchRead } from './common/passthrough-crud.js';
+import { passthroughRead, passthroughReadWithAltId, passthroughWrite, batchRead } from './common/passthrough-crud.js';
 import {
     Reader,
 } from 'k6/x/kafka';
@@ -35,6 +35,7 @@
 let cmHandlesCreatedPerSecondGauge = new Gauge('cmhandles_created_per_second');
 let cmHandlesDeletedPerSecondGauge = new Gauge('cmhandles_deleted_per_second');
 let passthroughReadNcmpOverheadTrend = new Trend('ncmp_overhead_passthrough_read');
+let passthroughReadNcmpOverheadTrendWithAlternateId = new Trend('ncmp_overhead_passthrough_read_alt_id');
 let passthroughWriteNcmpOverheadTrend = new Trend('ncmp_overhead_passthrough_write');
 let dataOperationsBatchReadCmHandlePerSecondTrend = new Trend('data_operations_batch_read_cmhandles_per_second');
 
@@ -55,6 +56,12 @@
             vus: 10,
             duration: DURATION,
         },
+        passthrough_read_alt_id: {
+            executor: 'constant-vus',
+            exec: 'passthrough_read_alt_id',
+            vus: 1,
+            duration: DURATION,
+        },
         passthrough_write: {
             executor: 'constant-vus',
             exec: 'passthrough_write',
@@ -96,6 +103,7 @@
         'http_reqs{scenario:passthrough_write}': ['rate >= 13'],
         'http_reqs{scenario:passthrough_read}': ['rate >= 25'],
         'ncmp_overhead_passthrough_read': ['avg <= 100'],
+        'ncmp_overhead_passthrough_read_alt_id': ['avg <= 100'],
         'ncmp_overhead_passthrough_write': ['avg <= 100'],
         'http_req_duration{scenario:id_search_module}': ['avg <= 625'],
         'http_req_duration{scenario:cm_search_module}': ['avg <= 13000'],
@@ -126,6 +134,13 @@
     passthroughReadNcmpOverheadTrend.add(overhead);
 }
 
+export function passthrough_read_alt_id() {
+    const response = passthroughReadWithAltId();
+    check(response, { 'passthrough read with alternate Id status equals 200': (r) => r.status === 200 });
+    const overhead = response.timings.duration - READ_DATA_FOR_CM_HANDLE_DELAY_MS;
+    passthroughReadNcmpOverheadTrendWithAlternateId.add(overhead);
+}
+
 export function passthrough_write() {
     const response = passthroughWrite();
     check(response, { 'passthrough write status equals 201': (r) => r.status === 201 });